Home | History | Annotate | Download | only in pending
      1 /* netstat.c - Display Linux networking subsystem.
      2  *
      3  * Copyright 2012 Ranjan Kumar <ranjankumar.bth (at) gmail.com>
      4  * Copyright 2013 Kyungwan Han <asura321 (at) gmail.com>
      5  *
      6  * Not in SUSv4.
      7  *
      8 USE_NETSTAT(NEWTOY(netstat, "pWrxwutneal", TOYFLAG_BIN))
      9 config NETSTAT
     10   bool "netstat"
     11   default n
     12   help
     13     usage: netstat [-pWrxwutneal]
     14 
     15     Display networking information.
     16 
     17     -r  Display routing table.
     18     -a  Display all sockets (Default: Connected).
     19     -l  Display listening server sockets.
     20     -t  Display TCP sockets.
     21     -u  Display UDP sockets.
     22     -w  Display Raw sockets.
     23     -x  Display Unix sockets.
     24     -e  Display other/more information.
     25     -n  Don't resolve names.
     26     -W  Wide Display.
     27     -p  Display PID/Program name for sockets.
     28 */
     29 
     30 #define FOR_netstat
     31 #include "toys.h"
     32 
     33 #include <net/route.h>
     34 
     35 GLOBALS(
     36   char current_name[21];
     37   int some_process_unidentified;
     38 );
     39 
     40 typedef union _iaddr {
     41   unsigned u;
     42   unsigned char b[4];
     43 } iaddr;
     44 
     45 typedef union _iaddr6 {
     46   struct {
     47     unsigned a;
     48     unsigned b;
     49     unsigned c;
     50     unsigned d;
     51   } u;
     52   unsigned char b[16];
     53 } iaddr6;
     54 
     55 #define ADDR_LEN (INET6_ADDRSTRLEN + 1 + 5 + 1)//IPv6 addr len + : + port + '\0'
     56 
     57 //For unix states
     58 enum {
     59 	SOCK_ACCEPTCON = (1 << 16),  //performed a listen.
     60 	SOCK_WAIT_DATA = (1 << 17),  //wait data to read.
     61 	SOCK_NO_SPACE = (1 << 18),  //no space to write.
     62 };
     63 
     64 #define SOCK_NOT_CONNECTED 1
     65 
     66 typedef struct _pidlist {
     67   struct _pidlist *next;
     68   long inode;
     69   char name[21];
     70 } PID_LIST;
     71 
     72 PID_LIST *pid_list = NULL;
     73 
     74 /*
     75  * used to convert string into int and
     76  * validate the input str for invalid int value or out-of-range.
     77  */
     78 static unsigned long get_strtou(char *str, char **endp, int base)
     79 {
     80   unsigned long uli;
     81   char *endptr;
     82 
     83   if (!isalnum(str[0])) {
     84     errno = ERANGE;
     85     return UINT_MAX;
     86   }
     87   errno = 0;
     88   uli = strtoul(str, &endptr, base);
     89   if (uli > UINT_MAX) {
     90     errno = ERANGE;
     91     return UINT_MAX;
     92   }
     93 
     94   if (endp) *endp = endptr;
     95   if (endptr[0]) {
     96     if (isalnum(endptr[0]) || errno) { //"123abc" or out-of-range
     97       errno = ERANGE;
     98       return UINT_MAX;
     99     }
    100     errno = EINVAL;
    101   }
    102   return uli;
    103 }
    104 
    105 /*
    106  * used to retrive pid name from pid list.
    107  */
    108 static const char *get_pid_name(unsigned long inode)
    109 {
    110   PID_LIST *tmp;
    111 
    112   for (tmp = pid_list; tmp; tmp = tmp->next)
    113     if (tmp->inode == inode) return tmp->name;
    114 
    115   return "-";
    116 }
    117 
    118 /*
    119  * For TCP/UDP/RAW display data.
    120  */
    121 static void display_data(unsigned rport, char *label,
    122                          unsigned rxq, unsigned txq, char *lip, char *rip,
    123                          unsigned state, unsigned uid, unsigned long inode)
    124 {
    125   char *ss_state = "UNKNOWN", buf[12];
    126   char *state_label[] = {"", "ESTABLISHED", "SYN_SENT", "SYN_RECV", "FIN_WAIT1",
    127                          "FIN_WAIT2", "TIME_WAIT", "CLOSE", "CLOSE_WAIT",
    128                          "LAST_ACK", "LISTEN", "CLOSING", "UNKNOWN"};
    129   char user[11];
    130   struct passwd *pw;
    131 
    132   if (!strcmp(label, "tcp")) {
    133     int sz = ARRAY_LEN(state_label);
    134     if (!state || state >= sz) state = sz-1;
    135     ss_state = state_label[state];
    136   }
    137   else if (!strcmp(label, "udp")) {
    138     if (state == 1) ss_state = state_label[state];
    139     else if (state == 7) ss_state = "";
    140   }
    141   else if (!strcmp(label, "raw")) sprintf(ss_state = buf, "%u", state);
    142 
    143   if (!(toys.optflags & FLAG_n) && (pw = getpwuid(uid))) {
    144     snprintf(user, sizeof(user), "%s", pw->pw_name);
    145   } else snprintf(user, sizeof(user), "%d", uid);
    146 
    147   xprintf("%3s   %6d %6d ", label, rxq, txq);
    148   xprintf((toys.optflags & FLAG_W) ? "%-51.51s %-51.51s " : "%-23.23s %-23.23s "
    149            , lip, rip);
    150   xprintf("%-11s ", ss_state);
    151   if ((toys.optflags & FLAG_e)) xprintf("%-10s %-11ld ", user, inode);
    152   if ((toys.optflags & FLAG_p)) xprintf("%s", get_pid_name(inode));
    153   xputc('\n');
    154 }
    155 
    156 /*
    157  * For TCP/UDP/RAW show data.
    158  */
    159 static void show_data(unsigned rport, char *label, unsigned rxq, unsigned txq,
    160                       char *lip, char *rip, unsigned state, unsigned uid,
    161                       unsigned long inode)
    162 {
    163   if (toys.optflags & FLAG_l) {
    164     if (!rport && (state & 0xA))
    165       display_data(rport, label, rxq, txq, lip, rip, state, uid, inode);
    166   } else if (toys.optflags & FLAG_a)
    167     display_data(rport, label, rxq, txq, lip, rip, state, uid, inode);
    168   //rport && (TCP | UDP | RAW)
    169   else if (rport & (0x10 | 0x20 | 0x40))
    170     display_data(rport, label, rxq, txq, lip, rip, state, uid, inode);
    171 }
    172 
    173 /*
    174  * used to get service name.
    175  */
    176 static char *get_servname(int port, char *label)
    177 {
    178   int lport = htons(port);
    179   if (!lport) return xmprintf("%s", "*");
    180   struct servent *ser = getservbyport(lport, label);
    181   if (ser) return xmprintf("%s", ser->s_name);
    182   return xmprintf("%u", (unsigned)ntohs(lport));
    183 }
    184 
    185 /*
    186  * used to convert address into text format.
    187  */
    188 static void addr2str(int af, void *addr, unsigned port, char *buf, char *label)
    189 {
    190   char ip[ADDR_LEN] = {0,};
    191   if (!inet_ntop(af, addr, ip, ADDR_LEN)) {
    192     *buf = '\0';
    193     return;
    194   }
    195   size_t iplen = strlen(ip);
    196   if (!port) {
    197     strncat(ip+iplen, ":*", ADDR_LEN-iplen-1);
    198     memcpy(buf, ip, ADDR_LEN);
    199     return;
    200   }
    201 
    202   if (!(toys.optflags & FLAG_n)) {
    203     struct addrinfo hints, *result, *rp;
    204 
    205     memset(&hints, 0, sizeof(struct addrinfo));
    206     hints.ai_family = af;
    207 
    208     if (!getaddrinfo(ip, NULL, &hints, &result)) {
    209       char hbuf[NI_MAXHOST] = {0,}, sbuf[NI_MAXSERV] = {0,};
    210       socklen_t sock_len;
    211       char *sname = NULL;
    212       int plen = 0;
    213 
    214       if (af == AF_INET) sock_len = sizeof(struct sockaddr_in);
    215       else sock_len = sizeof(struct sockaddr_in6);
    216 
    217       for (rp = result; rp; rp = rp->ai_next)
    218         if (!getnameinfo(rp->ai_addr, sock_len, hbuf, sizeof(hbuf), sbuf,
    219             sizeof(sbuf), NI_NUMERICSERV))
    220           break;
    221 
    222       freeaddrinfo(result);
    223       sname = get_servname(port, label);
    224       plen = strlen(sname);
    225       if (*hbuf) {
    226         memset(ip, 0, ADDR_LEN);
    227         memcpy(ip, hbuf, (ADDR_LEN - plen - 2));
    228         iplen = strlen(ip);
    229       }
    230       snprintf(ip + iplen, ADDR_LEN-iplen, ":%s", sname);
    231       free(sname);
    232     }
    233   }
    234   else snprintf(ip+iplen, ADDR_LEN-iplen, ":%d", port);
    235   memcpy(buf, ip, ADDR_LEN);
    236 }
    237 
    238 /*
    239  * display ipv4 info for TCP/UDP/RAW.
    240  */
    241 static void show_ipv4(char *fname, char *label)
    242 {
    243   FILE *fp = fopen(fname, "r");
    244   if (!fp) {
    245      perror_msg("'%s'", fname);
    246      return;
    247   }
    248 
    249   if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header.
    250 
    251   while (fgets(toybuf, sizeof(toybuf), fp)) {
    252     char lip[ADDR_LEN] = {0,}, rip[ADDR_LEN] = {0,};
    253     iaddr laddr, raddr;
    254     unsigned lport, rport, state, txq, rxq, num, uid;
    255     unsigned long inode;
    256 
    257     int nitems = sscanf(toybuf, " %d: %x:%x %x:%x %x %x:%x %*X:%*X %*X %d %*d %ld",
    258                         &num, &laddr.u, &lport, &raddr.u, &rport, &state, &txq,
    259                         &rxq, &uid, &inode);
    260     if (nitems == 10) {
    261       addr2str(AF_INET, &laddr, lport, lip, label);
    262       addr2str(AF_INET, &raddr, rport, rip, label);
    263       show_data(rport, label, rxq, txq, lip, rip, state, uid, inode);
    264     }
    265   }//End of While
    266   fclose(fp);
    267 }
    268 
    269 /*
    270  * display ipv6 info for TCP/UDP/RAW.
    271  */
    272 static void show_ipv6(char *fname, char *label)
    273 {
    274   FILE *fp = fopen(fname, "r");
    275   if (!fp) {
    276     perror_msg("'%s'", fname);
    277     return;
    278   }
    279 
    280   if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header.
    281 
    282   while (fgets(toybuf, sizeof(toybuf), fp)) {
    283     char lip[ADDR_LEN] = {0,}, rip[ADDR_LEN] = {0,};
    284     iaddr6 laddr6, raddr6;
    285     unsigned lport, rport, state, txq, rxq, num, uid;
    286     unsigned long inode;
    287     int nitems = sscanf(toybuf, " %d: %8x%8x%8x%8x:%x %8x%8x%8x%8x:%x %x %x:%x "
    288                                 "%*X:%*X %*X %d %*d %ld",
    289                         &num, &laddr6.u.a, &laddr6.u.b, &laddr6.u.c,
    290                         &laddr6.u.d, &lport, &raddr6.u.a, &raddr6.u.b,
    291                         &raddr6.u.c, &raddr6.u.d, &rport, &state, &txq, &rxq,
    292                         &uid, &inode);
    293     if (nitems == 16) {
    294       addr2str(AF_INET6, &laddr6, lport, lip, label);
    295       addr2str(AF_INET6, &raddr6, rport, rip, label);
    296       show_data(rport, label, rxq, txq, lip, rip, state, uid, inode);
    297     }
    298   }//End of While
    299   fclose(fp);
    300 }
    301 
    302 /*
    303  * display unix socket info.
    304  */
    305 static void show_unix_sockets(char *fname, char *label)
    306 {
    307   FILE *fp = fopen((char *)fname, "r");
    308   if (!fp) {
    309     perror_msg("'%s'", fname);
    310     return;
    311   }
    312 
    313   if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header.
    314 
    315   while (fgets(toybuf, sizeof(toybuf), fp)) {
    316     unsigned long refcount, label, flags, inode;
    317     int nitems = 0, path_offset = 0, type, state;
    318     char sock_flags[32] = {0,}, *sock_type, *sock_state, *bptr = toybuf, *term;
    319 
    320     if (!toybuf[0]) continue;
    321 
    322     nitems = sscanf(toybuf, "%*p: %lX %lX %lX %X %X %lu %n",
    323         &refcount, &label, &flags, &type, &state, &inode, &path_offset);
    324 
    325     //for state one less
    326     if (nitems < 6) break;
    327 
    328     if (toys.optflags & FLAG_l) {
    329       if ( !((state == SOCK_NOT_CONNECTED) && (flags & SOCK_ACCEPTCON)) )
    330         continue;
    331     } else if (!(toys.optflags & FLAG_a)) {
    332       if ((state == SOCK_NOT_CONNECTED) && (flags & SOCK_ACCEPTCON)) continue;
    333     }
    334 
    335     //prepare socket type, state and flags.
    336     {
    337       char *ss_type[] = { "", "STREAM", "DGRAM", "RAW", "RDM", "SEQPACKET",
    338                           "UNKNOWN"};
    339       char *ss_state[] = { "FREE", "LISTENING", "CONNECTING", "CONNECTED",
    340                            "DISCONNECTING", "UNKNOWN"};
    341 
    342       int sz = ARRAY_LEN(ss_type);//sizeof(ss_type)/sizeof(ss_type[0]);
    343       if ( (type < SOCK_STREAM) || (type > SOCK_SEQPACKET) )
    344         sock_type = ss_type[sz-1];
    345       else sock_type = ss_type[type];
    346 
    347       sz = ARRAY_LEN(ss_state);//sizeof(ss_state)/sizeof(ss_state[0]);
    348       if ((state < 0) || (state > sz-2)) sock_state = ss_state[sz-1];
    349       else if (state == SOCK_NOT_CONNECTED) {
    350         if (flags & SOCK_ACCEPTCON) sock_state = ss_state[state];
    351         else sock_state = " ";
    352       } else sock_state = ss_state[state];
    353 
    354       strcpy(sock_flags, "[ ");
    355       if (flags & SOCK_ACCEPTCON) strcat(sock_flags, "ACC ");
    356       if (flags & SOCK_WAIT_DATA) strcat(sock_flags, "W ");
    357       if (flags & SOCK_NO_SPACE) strcat(sock_flags, "N ");
    358       strcat(sock_flags, "]");
    359     }
    360     xprintf("%-5s %-6ld %-11s %-10s %-13s %8lu ", (!label ? "unix" : "??"),
    361       refcount, sock_flags, sock_type, sock_state, inode);
    362     if (toys.optflags & FLAG_p) xprintf("%-20s", get_pid_name(inode));
    363 
    364     bptr += path_offset;
    365     if ((term = strchr(bptr, '\n'))) *term = '\0';
    366     xprintf("%s\n", bptr);
    367   }//End of while
    368   fclose(fp);
    369 }
    370 
    371 /*
    372  * extract inode value from the link.
    373  */
    374 static long ss_inode(char *link)
    375 {
    376   long inode = -1;
    377   //"link = socket:[12345]", get "12345" as inode.
    378   if (!strncmp(link, "socket:[", sizeof("socket:[")-1)) {
    379     inode = get_strtou(link + sizeof("socket:[")-1, (char**)&link, 0);
    380     if (*link != ']') inode = -1;
    381   }
    382   //"link = [0000]:12345", get "12345" as inode.
    383   else if (!strncmp(link, "[0000]:", sizeof("[0000]:")-1)) {
    384     inode = get_strtou(link + sizeof("[0000]:")-1, NULL, 0);
    385     //if not NULL terminated.
    386     if (errno) inode = -1;
    387   }
    388   return inode;
    389 }
    390 
    391 /*
    392  * add inode and progname in the pid list.
    393  */
    394 static void add2list(long inode)
    395 {
    396   PID_LIST *node = pid_list;
    397 
    398   for(; node; node = node->next) {
    399     if(node->inode == inode)
    400       return;
    401   }
    402 
    403   PID_LIST *new = (PID_LIST *)xzalloc(sizeof(PID_LIST));
    404   new->inode = inode;
    405   xstrncpy(new->name, TT.current_name, sizeof(new->name));
    406   new->next = pid_list;
    407   pid_list = new;
    408 }
    409 
    410 static void scan_pid_inodes(char *path)
    411 {
    412   DIR *dp;
    413   struct dirent *entry;
    414 
    415   if (!(dp = opendir(path))) {
    416     if (errno == EACCES) {
    417       TT.some_process_unidentified = 1;
    418       return;
    419     } else perror_exit("%s", path);
    420   }
    421   while ((entry = readdir(dp))) {
    422     char link_name[64], *link;
    423     long inode;
    424 
    425     if (!isdigit(entry->d_name[0])) continue;
    426     snprintf(link_name, sizeof(link_name), "%s/%s", path, entry->d_name);
    427     link = xreadlink(link_name);
    428     if ((inode = ss_inode(link)) != -1) add2list(inode);
    429     free(link);
    430   }
    431   closedir(dp);
    432 }
    433 
    434 static void scan_pid(int pid)
    435 {
    436   char *line, *p, *fd_dir;
    437 
    438   snprintf(toybuf, sizeof(toybuf), "/proc/%d/cmdline", pid);
    439   line = xreadfile(toybuf, 0, 0);
    440 
    441   if ((p = strchr(line, ' '))) *p = 0; // "/bin/netstat -ntp" -> "/bin/netstat"
    442   snprintf(TT.current_name, sizeof(TT.current_name), "%d/%s",
    443            pid, basename_r(line)); // "584/netstat"
    444   free(line);
    445 
    446   fd_dir = xmprintf("/proc/%d/fd", pid);
    447   scan_pid_inodes(fd_dir);
    448   free(fd_dir);
    449 }
    450 
    451 static int scan_pids(struct dirtree *node)
    452 {
    453   int pid;
    454 
    455   if (!node->parent) return DIRTREE_RECURSE;
    456   if ((pid = atol(node->name))) scan_pid(pid);
    457 
    458   return 0;
    459 }
    460 
    461 /*
    462  * Dealloc pid list.
    463  */
    464 static void clean_pid_list(void)
    465 {
    466   PID_LIST *tmp;
    467   while (pid_list) {
    468     tmp = pid_list->next;
    469     free(pid_list);
    470     pid_list = tmp;
    471   }
    472 }
    473 
    474 /*
    475  * For TCP/UDP/RAW show the header.
    476  */
    477 static void show_header(void)
    478 {
    479   xprintf("Proto Recv-Q Send-Q ");
    480   xprintf((toys.optflags & FLAG_W) ? "%-51s %-51s" : "%-23s %-23s",
    481           "Local Address", "Foreign Address");
    482   xprintf(" State      ");
    483   if (toys.optflags & FLAG_e) xprintf(" User       Inode      ");
    484   if (toys.optflags & FLAG_p) xprintf(" PID/Program Name");
    485   xputc('\n');
    486 }
    487 
    488 /*
    489  * used to get the flag values for route command.
    490  */
    491 static void get_flag_value(char *flagstr, int flags)
    492 {
    493   int i = 0;
    494   char *str = flagstr;
    495   static const char flagchars[] = "GHRDMDAC";
    496   static const unsigned flagarray[] = {
    497     RTF_GATEWAY,
    498     RTF_HOST,
    499     RTF_REINSTATE,
    500     RTF_DYNAMIC,
    501     RTF_MODIFIED,
    502     RTF_DEFAULT,
    503     RTF_ADDRCONF,
    504     RTF_CACHE
    505   };
    506   *str++ = 'U';
    507 
    508   while ( (*str = flagchars[i]) ) {
    509     if (flags & flagarray[i++]) ++str;
    510   }
    511 }
    512 
    513 /*
    514  * extract inet4 route info from /proc/net/route file and display it.
    515  */
    516 static void display_routes(int is_more_info, int notresolve)
    517 {
    518 #define IPV4_MASK (RTF_GATEWAY|RTF_HOST|RTF_REINSTATE|RTF_DYNAMIC|RTF_MODIFIED)
    519   unsigned long dest, gate, mask;
    520   int flags, ref, use, metric, mss, win, irtt;
    521   char iface[64]={0,};
    522   char flag_val[10]={0,}; //there are 9 flags "UGHRDMDAC" for route.
    523 
    524   FILE *fp = xfopen("/proc/net/route", "r");
    525 
    526   if(!fgets(toybuf, sizeof(toybuf), fp)) return; //skip header.
    527 
    528   xprintf("Kernel IP routing table\n"
    529           "Destination     Gateway         Genmask         Flags %s Iface\n",
    530           is_more_info ? "  MSS Window  irtt" : "Metric Ref    Use");
    531 
    532   while (fgets(toybuf, sizeof(toybuf), fp)) {
    533      int nitems = 0;
    534      char *destip = NULL, *gateip = NULL, *maskip = NULL;
    535 
    536      nitems = sscanf(toybuf, "%63s%lx%lx%X%d%d%d%lx%d%d%d\n", iface, &dest,
    537        &gate, &flags, &ref, &use, &metric, &mask, &mss, &win, &irtt);
    538      if (nitems != 11) {//EOF with no (nonspace) chars read.
    539        if ((nitems < 0) && feof(fp)) break;
    540       perror_exit("sscanf");
    541     }
    542 
    543     //skip down interfaces.
    544     if (!(flags & RTF_UP)) continue;
    545 
    546     if (dest) {//For Destination
    547       if (inet_ntop(AF_INET, &dest, toybuf, sizeof(toybuf)) )
    548         destip = xstrdup(toybuf);
    549     } else {
    550       if (!notresolve) destip = xstrdup("default");
    551       else destip = xstrdup("0.0.0.0");
    552     }
    553 
    554     if (gate) {//For Gateway
    555       if (inet_ntop(AF_INET, &gate, toybuf, sizeof(toybuf)) )
    556         gateip = xstrdup(toybuf);
    557     } else {
    558       if (!notresolve) gateip = xstrdup("*");
    559       else gateip = xstrdup("0.0.0.0");
    560     }
    561 
    562     //For Mask
    563     if (inet_ntop(AF_INET, &mask, toybuf, sizeof(toybuf)) )
    564       maskip = xstrdup(toybuf);
    565 
    566     //Get flag Values
    567     get_flag_value(flag_val, flags & IPV4_MASK);
    568     if (flags & RTF_REJECT) flag_val[0] = '!';
    569 
    570     xprintf("%-15.15s %-15.15s %-16s%-6s", destip, gateip, maskip, flag_val);
    571     if (is_more_info) xprintf("%5d %-5d %6d %s\n", mss, win, irtt, iface);
    572     else xprintf("%-6d %-2d %7d %s\n", metric, ref, use, iface);
    573 
    574     if (destip) free(destip);
    575     if (gateip) free(gateip);
    576     if (maskip) free(maskip);
    577   }//end of while.
    578   fclose(fp);
    579 #undef IPV4_MASK
    580 }
    581 
    582 /*
    583  * netstat utility main function.
    584  */
    585 void netstat_main(void)
    586 {
    587 #define IS_NETSTAT_PROTO_FLAGS_UP (toys.optflags & (FLAG_t | FLAG_u | FLAG_w \
    588                                                     | FLAG_x))
    589 
    590   // For no parameter, add 't', 'u', 'w', 'x' options as default
    591   if (!toys.optflags) toys.optflags = FLAG_t | FLAG_u | FLAG_w | FLAG_x;
    592 
    593   // For both 'a' and 'l' are set, remove 'l' option
    594   if (toys.optflags & FLAG_a && toys.optflags & FLAG_l)
    595       toys.optflags &= ~FLAG_l;
    596 
    597   // For each 'a', 'l', 'e', 'n', 'W', 'p' options
    598   // without any 't', 'u', 'w', 'x' option, add 't', 'u', 'w', 'x' options
    599   if (((toys.optflags & FLAG_a) || (toys.optflags & FLAG_l) ||
    600        (toys.optflags & FLAG_e) || (toys.optflags & FLAG_n) ||
    601        (toys.optflags & FLAG_W) || (toys.optflags & FLAG_p)) &&
    602          (!IS_NETSTAT_PROTO_FLAGS_UP) )
    603     toys.optflags |= FLAG_t | FLAG_u | FLAG_w | FLAG_x;
    604 
    605   //Display routing table.
    606   if (toys.optflags & FLAG_r) {
    607     display_routes(!(toys.optflags & FLAG_e), (toys.optflags & FLAG_n));
    608     return;
    609   }
    610 
    611   if (toys.optflags & FLAG_p) {
    612     dirtree_read("/proc", scan_pids);
    613     // TODO: we probably shouldn't warn if all the processes we're going to
    614     // list were identified.
    615     if (TT.some_process_unidentified)
    616       fprintf(stderr,
    617         "(Not all processes could be identified, non-owned process info\n"
    618         " will not be shown, you would have to be root to see it all.)\n");
    619   }
    620 
    621   //For TCP/UDP/RAW.
    622   if ( (toys.optflags & FLAG_t) || (toys.optflags & FLAG_u) ||
    623        (toys.optflags & FLAG_w) ) {
    624     xprintf("Active Internet connections ");
    625 
    626     if (toys.optflags & FLAG_a) xprintf("(servers and established)\n");
    627     else if (toys.optflags & FLAG_l) xprintf("(only servers)\n");
    628     else xprintf("(w/o servers)\n");
    629 
    630     show_header();
    631     if (toys.optflags & FLAG_t) {//For TCP
    632       show_ipv4("/proc/net/tcp",  "tcp");
    633       show_ipv6("/proc/net/tcp6", "tcp");
    634     }
    635     if (toys.optflags & FLAG_u) {//For UDP
    636       show_ipv4("/proc/net/udp",  "udp");
    637       show_ipv6("/proc/net/udp6", "udp");
    638     }
    639     if (toys.optflags & FLAG_w) {//For raw
    640       show_ipv4("/proc/net/raw",  "raw");
    641       show_ipv6("/proc/net/raw6", "raw");
    642     }
    643   }
    644   if (toys.optflags & FLAG_x) {//For UNIX
    645     xprintf("Active UNIX domain sockets ");
    646     if (toys.optflags & FLAG_a) xprintf("(servers and established)\n");
    647     else if (toys.optflags & FLAG_l) xprintf("(only servers)\n");
    648     else xprintf("(w/o servers)\n");
    649 
    650     if (toys.optflags & FLAG_p)
    651       xprintf("Proto RefCnt Flags       Type       State           "
    652               "I-Node PID/Program Name    Path\n");
    653     else
    654       xprintf("Proto RefCnt Flags       Type       State           "
    655               "I-Node Path\n");
    656     show_unix_sockets("/proc/net/unix", "unix");
    657   }
    658   if (toys.optflags & FLAG_p) clean_pid_list();
    659   if (toys.exitval) toys.exitval = 0;
    660 #undef IS_NETSTAT_PROTO_FLAGS_UP
    661 }
    662