]> CyberLeo.Net >> Repos - FreeBSD/stable/8.git/blob - usr.bin/sockstat/sockstat.c
MFC r235870
[FreeBSD/stable/8.git] / usr.bin / sockstat / sockstat.c
1 /*-
2  * Copyright (c) 2002 Dag-Erling Coïdan Smørgrav
3  * All rights reserved.
4  *
5  * Redistribution and use in source and binary forms, with or without
6  * modification, are permitted provided that the following conditions
7  * are met:
8  * 1. Redistributions of source code must retain the above copyright
9  *    notice, this list of conditions and the following disclaimer
10  *    in this position and unchanged.
11  * 2. Redistributions in binary form must reproduce the above copyright
12  *    notice, this list of conditions and the following disclaimer in the
13  *    documentation and/or other materials provided with the distribution.
14  * 3. The name of the author may not be used to endorse or promote products
15  *    derived from this software without specific prior written permission.
16  *
17  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
18  * IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
19  * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
20  * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT,
21  * INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
22  * NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
23  * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
24  * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
25  * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
26  * THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
27  */
28
29 #include <sys/cdefs.h>
30 __FBSDID("$FreeBSD$");
31
32 #include <sys/param.h>
33 #include <sys/socket.h>
34 #include <sys/socketvar.h>
35 #include <sys/sysctl.h>
36 #include <sys/file.h>
37 #include <sys/user.h>
38
39 #include <sys/un.h>
40 #include <sys/unpcb.h>
41
42 #include <net/route.h>
43
44 #include <netinet/in.h>
45 #include <netinet/in_pcb.h>
46 #include <netinet/tcp.h>
47 #include <netinet/tcp_seq.h>
48 #include <netinet/tcp_var.h>
49 #include <arpa/inet.h>
50
51 #include <ctype.h>
52 #include <err.h>
53 #include <errno.h>
54 #include <netdb.h>
55 #include <pwd.h>
56 #include <stdarg.h>
57 #include <stdio.h>
58 #include <stdlib.h>
59 #include <string.h>
60 #include <unistd.h>
61
62 static int       opt_4;         /* Show IPv4 sockets */
63 static int       opt_6;         /* Show IPv6 sockets */
64 static int       opt_c;         /* Show connected sockets */
65 static int       opt_j;         /* Show specified jail */
66 static int       opt_L;         /* Don't show IPv4 or IPv6 loopback sockets */
67 static int       opt_l;         /* Show listening sockets */
68 static int       opt_u;         /* Show Unix domain sockets */
69 static int       opt_v;         /* Verbose mode */
70
71 /*
72  * Default protocols to use if no -P was defined.
73  */
74 static const char *default_protos[] = {"tcp", "udp", "divert" };
75 static size_t      default_numprotos =
76     sizeof(default_protos) / sizeof(default_protos[0]);
77
78 static int      *protos;        /* protocols to use */
79 static size_t    numprotos;     /* allocated size of protos[] */
80
81 static int      *ports;
82
83 #define INT_BIT (sizeof(int)*CHAR_BIT)
84 #define SET_PORT(p) do { ports[p / INT_BIT] |= 1 << (p % INT_BIT); } while (0)
85 #define CHK_PORT(p) (ports[p / INT_BIT] & (1 << (p % INT_BIT)))
86
87 struct sock {
88         void *socket;
89         void *pcb;
90         int shown;
91         int vflag;
92         int family;
93         int proto;
94         const char *protoname;
95         struct sockaddr_storage laddr;
96         struct sockaddr_storage faddr;
97         struct sock *next;
98 };
99
100 #define HASHSIZE 1009
101 static struct sock *sockhash[HASHSIZE];
102
103 static struct xfile *xfiles;
104 static int nxfiles;
105
106 static int
107 xprintf(const char *fmt, ...)
108 {
109         va_list ap;
110         int len;
111
112         va_start(ap, fmt);
113         len = vprintf(fmt, ap);
114         va_end(ap);
115         if (len < 0)
116                 err(1, "printf()");
117         return (len);
118 }
119
120
121 static int
122 get_proto_type(const char *proto)
123 {
124         struct protoent *pent;
125
126         if (strlen(proto) == 0)
127                 return (0);
128         pent = getprotobyname(proto);
129         if (pent == NULL) {
130                 warn("getprotobyname");
131                 return (-1);
132         }
133         return (pent->p_proto);
134 }
135
136
137 static void init_protos(int num)
138 {
139         int proto_count = 0;
140
141         if (num > 0) {
142                 proto_count = num;
143         } else {
144                 /* Find the maximum number of possible protocols. */
145                 while (getprotoent() != NULL)
146                         proto_count++;
147                 endprotoent();
148         }
149
150         if ((protos = malloc(sizeof(int) * proto_count)) == NULL)
151                 err(1, "malloc");
152         numprotos = proto_count;
153 }
154
155
156 static int
157 parse_protos(char *protospec)
158 {
159         char *prot;
160         char *tmp = protospec;
161         int proto_type, proto_index;
162
163         if (protospec == NULL)
164                 return (-1);
165
166         init_protos(0);
167         proto_index = 0;
168         while ((prot = strsep(&tmp, ",")) != NULL) {
169                 if (strlen(prot) == 0)
170                         continue;
171                 proto_type = get_proto_type(prot);
172                 if (proto_type != -1)
173                         protos[proto_index++] = proto_type;
174         }
175         numprotos = proto_index;
176         return (proto_index);
177 }
178
179
180 static void
181 parse_ports(const char *portspec)
182 {
183         const char *p, *q;
184         int port, end;
185
186         if (ports == NULL)
187                 if ((ports = calloc(65536 / INT_BIT, sizeof(int))) == NULL)
188                         err(1, "calloc()");
189         p = portspec;
190         while (*p != '\0') {
191                 if (!isdigit(*p))
192                         errx(1, "syntax error in port range");
193                 for (q = p; *q != '\0' && isdigit(*q); ++q)
194                         /* nothing */ ;
195                 for (port = 0; p < q; ++p)
196                         port = port * 10 + digittoint(*p);
197                 if (port < 0 || port > 65535)
198                         errx(1, "invalid port number");
199                 SET_PORT(port);
200                 switch (*p) {
201                 case '-':
202                         ++p;
203                         break;
204                 case ',':
205                         ++p;
206                         /* fall through */
207                 case '\0':
208                 default:
209                         continue;
210                 }
211                 for (q = p; *q != '\0' && isdigit(*q); ++q)
212                         /* nothing */ ;
213                 for (end = 0; p < q; ++p)
214                         end = end * 10 + digittoint(*p);
215                 if (end < port || end > 65535)
216                         errx(1, "invalid port number");
217                 while (port++ < end)
218                         SET_PORT(port);
219                 if (*p == ',')
220                         ++p;
221         }
222 }
223
224 static void
225 sockaddr(struct sockaddr_storage *sa, int af, void *addr, int port)
226 {
227         struct sockaddr_in *sin4;
228         struct sockaddr_in6 *sin6;
229
230         bzero(sa, sizeof *sa);
231         switch (af) {
232         case AF_INET:
233                 sin4 = (struct sockaddr_in *)sa;
234                 sin4->sin_len = sizeof *sin4;
235                 sin4->sin_family = af;
236                 sin4->sin_port = port;
237                 sin4->sin_addr = *(struct in_addr *)addr;
238                 break;
239         case AF_INET6:
240                 sin6 = (struct sockaddr_in6 *)sa;
241                 sin6->sin6_len = sizeof *sin6;
242                 sin6->sin6_family = af;
243                 sin6->sin6_port = port;
244                 sin6->sin6_addr = *(struct in6_addr *)addr;
245                 break;
246         default:
247                 abort();
248         }
249 }
250
251 static void
252 gather_inet(int proto)
253 {
254         struct xinpgen *xig, *exig;
255         struct xinpcb *xip;
256         struct xtcpcb *xtp;
257         struct inpcb *inp;
258         struct xsocket *so;
259         struct sock *sock;
260         const char *varname, *protoname;
261         size_t len, bufsize;
262         void *buf;
263         int hash, retry, vflag;
264
265         vflag = 0;
266         if (opt_4)
267                 vflag |= INP_IPV4;
268         if (opt_6)
269                 vflag |= INP_IPV6;
270
271         switch (proto) {
272         case IPPROTO_TCP:
273                 varname = "net.inet.tcp.pcblist";
274                 protoname = "tcp";
275                 break;
276         case IPPROTO_UDP:
277                 varname = "net.inet.udp.pcblist";
278                 protoname = "udp";
279                 break;
280         case IPPROTO_DIVERT:
281                 varname = "net.inet.divert.pcblist";
282                 protoname = "div";
283                 break;
284         default:
285                 errx(1, "protocol %d not supported", proto);
286         }
287
288         buf = NULL;
289         bufsize = 8192;
290         retry = 5;
291         do {
292                 for (;;) {
293                         if ((buf = realloc(buf, bufsize)) == NULL)
294                                 err(1, "realloc()");
295                         len = bufsize;
296                         if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
297                                 break;
298                         if (errno == ENOENT)
299                                 goto out;
300                         if (errno != ENOMEM || len != bufsize)
301                                 err(1, "sysctlbyname()");
302                         bufsize *= 2;
303                 }
304                 xig = (struct xinpgen *)buf;
305                 exig = (struct xinpgen *)(void *)
306                     ((char *)buf + len - sizeof *exig);
307                 if (xig->xig_len != sizeof *xig ||
308                     exig->xig_len != sizeof *exig)
309                         errx(1, "struct xinpgen size mismatch");
310         } while (xig->xig_gen != exig->xig_gen && retry--);
311
312         if (xig->xig_gen != exig->xig_gen && opt_v)
313                 warnx("warning: data may be inconsistent");
314
315         for (;;) {
316                 xig = (struct xinpgen *)(void *)((char *)xig + xig->xig_len);
317                 if (xig >= exig)
318                         break;
319                 switch (proto) {
320                 case IPPROTO_TCP:
321                         xtp = (struct xtcpcb *)xig;
322                         if (xtp->xt_len != sizeof *xtp) {
323                                 warnx("struct xtcpcb size mismatch");
324                                 goto out;
325                         }
326                         inp = &xtp->xt_inp;
327                         so = &xtp->xt_socket;
328                         break;
329                 case IPPROTO_UDP:
330                 case IPPROTO_DIVERT:
331                         xip = (struct xinpcb *)xig;
332                         if (xip->xi_len != sizeof *xip) {
333                                 warnx("struct xinpcb size mismatch");
334                                 goto out;
335                         }
336                         inp = &xip->xi_inp;
337                         so = &xip->xi_socket;
338                         break;
339                 default:
340                         errx(1, "protocol %d not supported", proto);
341                 }
342                 if ((inp->inp_vflag & vflag) == 0)
343                         continue;
344                 if (inp->inp_vflag & INP_IPV4) {
345                         if ((inp->inp_fport == 0 && !opt_l) ||
346                             (inp->inp_fport != 0 && !opt_c))
347                                 continue;
348 #define __IN_IS_ADDR_LOOPBACK(pina) \
349         ((ntohl((pina)->s_addr) >> IN_CLASSA_NSHIFT) == IN_LOOPBACKNET)
350                         if (opt_L &&
351                             (__IN_IS_ADDR_LOOPBACK(&inp->inp_faddr) ||
352                              __IN_IS_ADDR_LOOPBACK(&inp->inp_laddr)))
353                                 continue;
354 #undef __IN_IS_ADDR_LOOPBACK
355                 } else if (inp->inp_vflag & INP_IPV6) {
356                         if ((inp->inp_fport == 0 && !opt_l) ||
357                             (inp->inp_fport != 0 && !opt_c))
358                                 continue;
359                         if (opt_L &&
360                             (IN6_IS_ADDR_LOOPBACK(&inp->in6p_faddr) ||
361                              IN6_IS_ADDR_LOOPBACK(&inp->in6p_laddr)))
362                                 continue;
363                 } else {
364                         if (opt_v)
365                                 warnx("invalid vflag 0x%x", inp->inp_vflag);
366                         continue;
367                 }
368                 if ((sock = calloc(1, sizeof *sock)) == NULL)
369                         err(1, "malloc()");
370                 sock->socket = so->xso_so;
371                 sock->proto = proto;
372                 if (inp->inp_vflag & INP_IPV4) {
373                         sock->family = AF_INET;
374                         sockaddr(&sock->laddr, sock->family,
375                             &inp->inp_laddr, inp->inp_lport);
376                         sockaddr(&sock->faddr, sock->family,
377                             &inp->inp_faddr, inp->inp_fport);
378                 } else if (inp->inp_vflag & INP_IPV6) {
379                         sock->family = AF_INET6;
380                         sockaddr(&sock->laddr, sock->family,
381                             &inp->in6p_laddr, inp->inp_lport);
382                         sockaddr(&sock->faddr, sock->family,
383                             &inp->in6p_faddr, inp->inp_fport);
384                 }
385                 sock->vflag = inp->inp_vflag;
386                 sock->protoname = protoname;
387                 hash = (int)((uintptr_t)sock->socket % HASHSIZE);
388                 sock->next = sockhash[hash];
389                 sockhash[hash] = sock;
390         }
391 out:
392         free(buf);
393 }
394
395 static void
396 gather_unix(int proto)
397 {
398         struct xunpgen *xug, *exug;
399         struct xunpcb *xup;
400         struct sock *sock;
401         const char *varname, *protoname;
402         size_t len, bufsize;
403         void *buf;
404         int hash, retry;
405
406         switch (proto) {
407         case SOCK_STREAM:
408                 varname = "net.local.stream.pcblist";
409                 protoname = "stream";
410                 break;
411         case SOCK_DGRAM:
412                 varname = "net.local.dgram.pcblist";
413                 protoname = "dgram";
414                 break;
415         default:
416                 abort();
417         }
418         buf = NULL;
419         bufsize = 8192;
420         retry = 5;
421         do {
422                 for (;;) {
423                         if ((buf = realloc(buf, bufsize)) == NULL)
424                                 err(1, "realloc()");
425                         len = bufsize;
426                         if (sysctlbyname(varname, buf, &len, NULL, 0) == 0)
427                                 break;
428                         if (errno != ENOMEM || len != bufsize)
429                                 err(1, "sysctlbyname()");
430                         bufsize *= 2;
431                 }
432                 xug = (struct xunpgen *)buf;
433                 exug = (struct xunpgen *)(void *)
434                     ((char *)buf + len - sizeof *exug);
435                 if (xug->xug_len != sizeof *xug ||
436                     exug->xug_len != sizeof *exug) {
437                         warnx("struct xinpgen size mismatch");
438                         goto out;
439                 }
440         } while (xug->xug_gen != exug->xug_gen && retry--);
441
442         if (xug->xug_gen != exug->xug_gen && opt_v)
443                 warnx("warning: data may be inconsistent");
444
445         for (;;) {
446                 xug = (struct xunpgen *)(void *)((char *)xug + xug->xug_len);
447                 if (xug >= exug)
448                         break;
449                 xup = (struct xunpcb *)xug;
450                 if (xup->xu_len != sizeof *xup) {
451                         warnx("struct xunpcb size mismatch");
452                         goto out;
453                 }
454                 if ((xup->xu_unp.unp_conn == NULL && !opt_l) ||
455                     (xup->xu_unp.unp_conn != NULL && !opt_c))
456                         continue;
457                 if ((sock = calloc(1, sizeof *sock)) == NULL)
458                         err(1, "malloc()");
459                 sock->socket = xup->xu_socket.xso_so;
460                 sock->pcb = xup->xu_unpp;
461                 sock->proto = proto;
462                 sock->family = AF_UNIX;
463                 sock->protoname = protoname;
464                 if (xup->xu_unp.unp_addr != NULL)
465                         sock->laddr =
466                             *(struct sockaddr_storage *)(void *)&xup->xu_addr;
467                 else if (xup->xu_unp.unp_conn != NULL)
468                         *(void **)&sock->faddr = xup->xu_unp.unp_conn;
469                 hash = (int)((uintptr_t)sock->socket % HASHSIZE);
470                 sock->next = sockhash[hash];
471                 sockhash[hash] = sock;
472         }
473 out:
474         free(buf);
475 }
476
477 static void
478 getfiles(void)
479 {
480         size_t len, olen;
481
482         olen = len = sizeof *xfiles;
483         if ((xfiles = malloc(len)) == NULL)
484                 err(1, "malloc()");
485         while (sysctlbyname("kern.file", xfiles, &len, 0, 0) == -1) {
486                 if (errno != ENOMEM || len != olen)
487                         err(1, "sysctlbyname()");
488                 olen = len *= 2;
489                 if ((xfiles = realloc(xfiles, len)) == NULL)
490                         err(1, "realloc()");
491         }
492         if (len > 0 && xfiles->xf_size != sizeof *xfiles)
493                 errx(1, "struct xfile size mismatch");
494         nxfiles = len / sizeof *xfiles;
495 }
496
497 static int
498 printaddr(int af, struct sockaddr_storage *ss)
499 {
500         char addrstr[INET6_ADDRSTRLEN] = { '\0', '\0' };
501         struct sockaddr_un *sun;
502         void *addr;
503         int off, port;
504
505         switch (af) {
506         case AF_INET:
507                 addr = &((struct sockaddr_in *)ss)->sin_addr;
508                 if (inet_lnaof(*(struct in_addr *)addr) == INADDR_ANY)
509                         addrstr[0] = '*';
510                 port = ntohs(((struct sockaddr_in *)ss)->sin_port);
511                 break;
512         case AF_INET6:
513                 addr = &((struct sockaddr_in6 *)ss)->sin6_addr;
514                 if (IN6_IS_ADDR_UNSPECIFIED((struct in6_addr *)addr))
515                         addrstr[0] = '*';
516                 port = ntohs(((struct sockaddr_in6 *)ss)->sin6_port);
517                 break;
518         case AF_UNIX:
519                 sun = (struct sockaddr_un *)ss;
520                 off = (int)((char *)&sun->sun_path - (char *)sun);
521                 return (xprintf("%.*s", sun->sun_len - off, sun->sun_path));
522         }
523         if (addrstr[0] == '\0')
524                 inet_ntop(af, addr, addrstr, sizeof addrstr);
525         if (port == 0)
526                 return xprintf("%s:*", addrstr);
527         else
528                 return xprintf("%s:%d", addrstr, port);
529 }
530
531 static const char *
532 getprocname(pid_t pid)
533 {
534         static struct kinfo_proc proc;
535         size_t len;
536         int mib[4];
537
538         mib[0] = CTL_KERN;
539         mib[1] = KERN_PROC;
540         mib[2] = KERN_PROC_PID;
541         mib[3] = (int)pid;
542         len = sizeof proc;
543         if (sysctl(mib, 4, &proc, &len, NULL, 0) == -1) {
544                 /* Do not warn if the process exits before we get its name. */
545                 if (errno != ESRCH)
546                         warn("sysctl()");
547                 return ("??");
548         }
549         return (proc.ki_comm);
550 }
551
552 static int
553 getprocjid(pid_t pid)
554 {
555         static struct kinfo_proc proc;
556         size_t len;
557         int mib[4];
558
559         mib[0] = CTL_KERN;
560         mib[1] = KERN_PROC;
561         mib[2] = KERN_PROC_PID;
562         mib[3] = (int)pid;
563         len = sizeof proc;
564         if (sysctl(mib, 4, &proc, &len, NULL, 0) == -1) {
565                 /* Do not warn if the process exits before we get its jid. */
566                 if (errno != ESRCH)
567                         warn("sysctl()");
568                 return (-1);
569         }
570         return (proc.ki_jid);
571 }
572
573 static int
574 check_ports(struct sock *s)
575 {
576         int port;
577
578         if (ports == NULL)
579                 return (1);
580         if ((s->family != AF_INET) && (s->family != AF_INET6))
581                 return (1);
582         if (s->family == AF_INET)
583                 port = ntohs(((struct sockaddr_in *)(&s->laddr))->sin_port);
584         else
585                 port = ntohs(((struct sockaddr_in6 *)(&s->laddr))->sin6_port);
586         if (CHK_PORT(port))
587                 return (1);
588         if (s->family == AF_INET)
589                 port = ntohs(((struct sockaddr_in *)(&s->faddr))->sin_port);
590         else
591                 port = ntohs(((struct sockaddr_in6 *)(&s->faddr))->sin6_port);
592         if (CHK_PORT(port))
593                 return (1);
594         return (0);
595 }
596
597 static void
598 displaysock(struct sock *s, int pos)
599 {
600         void *p;
601         int hash;
602
603         while (pos < 29)
604                 pos += xprintf(" ");
605         pos += xprintf("%s", s->protoname);
606         if (s->vflag & INP_IPV4)
607                 pos += xprintf("4 ");
608         if (s->vflag & INP_IPV6)
609                 pos += xprintf("6 ");
610         while (pos < 36)
611                 pos += xprintf(" ");
612         switch (s->family) {
613         case AF_INET:
614         case AF_INET6:
615                 pos += printaddr(s->family, &s->laddr);
616                 if (s->family == AF_INET6 && pos >= 58)
617                         pos += xprintf(" ");
618                 while (pos < 58)
619                         pos += xprintf(" ");
620                 pos += printaddr(s->family, &s->faddr);
621                 break;
622         case AF_UNIX:
623                 /* server */
624                 if (s->laddr.ss_len > 0) {
625                         pos += printaddr(s->family, &s->laddr);
626                         break;
627                 }
628                 /* client */
629                 p = *(void **)&s->faddr;
630                 if (p == NULL) {
631                         pos += xprintf("(not connected)");
632                         break;
633                 }
634                 pos += xprintf("-> ");
635                 for (hash = 0; hash < HASHSIZE; ++hash) {
636                         for (s = sockhash[hash]; s != NULL; s = s->next)
637                                 if (s->pcb == p)
638                                         break;
639                         if (s != NULL)
640                                 break;
641                 }
642                 if (s == NULL || s->laddr.ss_len == 0)
643                         pos += xprintf("??");
644                 else
645                         pos += printaddr(s->family, &s->laddr);
646                 break;
647         default:
648                 abort();
649         }
650         xprintf("\n");
651 }
652
653 static void
654 display(void)
655 {
656         struct passwd *pwd;
657         struct xfile *xf;
658         struct sock *s;
659         int hash, n, pos;
660
661         printf("%-8s %-10s %-5s %-2s %-6s %-21s %-21s\n",
662             "USER", "COMMAND", "PID", "FD", "PROTO",
663             "LOCAL ADDRESS", "FOREIGN ADDRESS");
664         setpassent(1);
665         for (xf = xfiles, n = 0; n < nxfiles; ++n, ++xf) {
666                 if (xf->xf_data == NULL)
667                         continue;
668                 if (opt_j >= 0 && opt_j != getprocjid(xf->xf_pid))
669                         continue;
670                 hash = (int)((uintptr_t)xf->xf_data % HASHSIZE);
671                 for (s = sockhash[hash]; s != NULL; s = s->next)
672                         if ((void *)s->socket == xf->xf_data)
673                                 break;
674                 if (s == NULL)
675                         continue;
676                 if (!check_ports(s))
677                         continue;
678                 s->shown = 1;
679                 pos = 0;
680                 if ((pwd = getpwuid(xf->xf_uid)) == NULL)
681                         pos += xprintf("%lu ", (u_long)xf->xf_uid);
682                 else
683                         pos += xprintf("%s ", pwd->pw_name);
684                 while (pos < 9)
685                         pos += xprintf(" ");
686                 pos += xprintf("%.10s", getprocname(xf->xf_pid));
687                 while (pos < 20)
688                         pos += xprintf(" ");
689                 pos += xprintf("%lu ", (u_long)xf->xf_pid);
690                 while (pos < 26)
691                         pos += xprintf(" ");
692                 pos += xprintf("%d ", xf->xf_fd);
693                 displaysock(s, pos);
694         }
695         if (opt_j >= 0)
696                 return;
697         for (hash = 0; hash < HASHSIZE; hash++) {
698                 for (s = sockhash[hash]; s != NULL; s = s->next) {
699                         if (s->shown)
700                                 continue;
701                         if (!check_ports(s))
702                                 continue;
703                         pos = 0;
704                         pos += xprintf("%-8s %-10s %-5s %-2s ",
705                             "?", "?", "?", "?");
706                         displaysock(s, pos);
707                 }
708         }
709 }
710
711 static int set_default_protos(void)
712 {
713         struct protoent *prot;
714         const char *pname;
715         size_t pindex;
716
717         init_protos(default_numprotos);
718
719         for (pindex = 0; pindex < default_numprotos; pindex++) {
720                 pname = default_protos[pindex];
721                 prot = getprotobyname(pname);
722                 if (prot == NULL)
723                         err(1, "getprotobyname: %s", pname);
724                 protos[pindex] = prot->p_proto;
725         }
726         numprotos = pindex;
727         return (pindex);
728 }
729
730
731 static void
732 usage(void)
733 {
734         fprintf(stderr,
735             "Usage: sockstat [-46cLlu] [-p ports] [-P protocols]\n");
736         exit(1);
737 }
738
739 int
740 main(int argc, char *argv[])
741 {
742         int protos_defined = -1;
743         int o, i;
744
745         opt_j = -1;
746         while ((o = getopt(argc, argv, "46cj:Llp:P:uv")) != -1)
747                 switch (o) {
748                 case '4':
749                         opt_4 = 1;
750                         break;
751                 case '6':
752                         opt_6 = 1;
753                         break;
754                 case 'c':
755                         opt_c = 1;
756                         break;
757                 case 'j':
758                         opt_j = atoi(optarg);
759                         break;
760                 case 'L':
761                         opt_L = 1;
762                         break;
763                 case 'l':
764                         opt_l = 1;
765                         break;
766                 case 'p':
767                         parse_ports(optarg);
768                         break;
769                 case 'P':
770                         protos_defined = parse_protos(optarg);
771                         break;
772                 case 'u':
773                         opt_u = 1;
774                         break;
775                 case 'v':
776                         ++opt_v;
777                         break;
778                 default:
779                         usage();
780                 }
781
782         argc -= optind;
783         argv += optind;
784
785         if (argc > 0)
786                 usage();
787
788         if ((!opt_4 && !opt_6) && protos_defined != -1)
789                 opt_4 = opt_6 = 1;
790         if (!opt_4 && !opt_6 && !opt_u)
791                 opt_4 = opt_6 = opt_u = 1;
792         if ((opt_4 || opt_6) && protos_defined == -1)
793                 protos_defined = set_default_protos();
794         if (!opt_c && !opt_l)
795                 opt_c = opt_l = 1;
796
797         if (opt_4 || opt_6) {
798                 for (i = 0; i < protos_defined; i++)
799                         gather_inet(protos[i]);
800         }
801
802         if (opt_u || (protos_defined == -1 && !opt_4 && !opt_6)) {
803                 gather_unix(SOCK_STREAM);
804                 gather_unix(SOCK_DGRAM);
805         }
806         getfiles();
807         display();
808         exit(0);
809 }