Magellan Linux

Annotation of /trunk/mkinitrd-magellan/busybox/networking/netstat.c

Parent Directory Parent Directory | Revision Log Revision Log


Revision 1123 - (hide annotations) (download)
Wed Aug 18 21:56:57 2010 UTC (13 years, 9 months ago) by niro
File MIME type: text/plain
File size: 17741 byte(s)
-updated to busybox-1.17.1
1 niro 532 /* vi: set sw=4 ts=4: */
2     /*
3     * Mini netstat implementation(s) for busybox
4     * based in part on the netstat implementation from net-tools.
5     *
6     * Copyright (C) 2002 by Bart Visscher <magick@linux-fan.com>
7     *
8     * 2002-04-20
9     * IPV6 support added by Bart Visscher <magick@linux-fan.com>
10     *
11 niro 816 * 2008-07-10
12     * optional '-p' flag support ported from net-tools by G. Somlo <somlo@cmu.edu>
13     *
14 niro 532 * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
15     */
16    
17 niro 816 #include "libbb.h"
18 niro 532 #include "inet_common.h"
19    
20 niro 816 #define NETSTAT_OPTS "laentuwx" \
21 niro 984 IF_ROUTE( "r") \
22     IF_FEATURE_NETSTAT_WIDE("W") \
23     IF_FEATURE_NETSTAT_PRG( "p")
24 niro 532
25 niro 816 enum {
26     OPTBIT_KEEP_OLD = 7,
27 niro 984 IF_ROUTE( OPTBIT_ROUTE,)
28     IF_FEATURE_NETSTAT_WIDE(OPTBIT_WIDE ,)
29     IF_FEATURE_NETSTAT_PRG( OPTBIT_PRG ,)
30 niro 816 OPT_sock_listen = 1 << 0, // l
31     OPT_sock_all = 1 << 1, // a
32     OPT_extended = 1 << 2, // e
33     OPT_noresolve = 1 << 3, // n
34     OPT_sock_tcp = 1 << 4, // t
35     OPT_sock_udp = 1 << 5, // u
36     OPT_sock_raw = 1 << 6, // w
37     OPT_sock_unix = 1 << 7, // x
38 niro 984 OPT_route = IF_ROUTE( (1 << OPTBIT_ROUTE)) + 0, // r
39     OPT_wide = IF_FEATURE_NETSTAT_WIDE((1 << OPTBIT_WIDE )) + 0, // W
40     OPT_prg = IF_FEATURE_NETSTAT_PRG( (1 << OPTBIT_PRG )) + 0, // p
41 niro 816 };
42    
43     #define NETSTAT_CONNECTED 0x01
44     #define NETSTAT_LISTENING 0x02
45     #define NETSTAT_NUMERIC 0x04
46 niro 532 /* Must match getopt32 option string */
47 niro 816 #define NETSTAT_TCP 0x10
48     #define NETSTAT_UDP 0x20
49     #define NETSTAT_RAW 0x40
50     #define NETSTAT_UNIX 0x80
51     #define NETSTAT_ALLPROTO (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW|NETSTAT_UNIX)
52 niro 532
53    
54     enum {
55 niro 816 TCP_ESTABLISHED = 1,
56     TCP_SYN_SENT,
57     TCP_SYN_RECV,
58     TCP_FIN_WAIT1,
59     TCP_FIN_WAIT2,
60     TCP_TIME_WAIT,
61     TCP_CLOSE,
62     TCP_CLOSE_WAIT,
63     TCP_LAST_ACK,
64     TCP_LISTEN,
65     TCP_CLOSING, /* now a valid state */
66 niro 532 };
67    
68 niro 816 static const char *const tcp_state[] = {
69     "",
70     "ESTABLISHED",
71     "SYN_SENT",
72     "SYN_RECV",
73     "FIN_WAIT1",
74     "FIN_WAIT2",
75     "TIME_WAIT",
76     "CLOSE",
77     "CLOSE_WAIT",
78     "LAST_ACK",
79     "LISTEN",
80     "CLOSING"
81 niro 532 };
82    
83     typedef enum {
84 niro 816 SS_FREE = 0, /* not allocated */
85     SS_UNCONNECTED, /* unconnected to any socket */
86     SS_CONNECTING, /* in process of connecting */
87     SS_CONNECTED, /* connected to socket */
88     SS_DISCONNECTING /* in process of disconnecting */
89 niro 532 } socket_state;
90    
91 niro 816 #define SO_ACCEPTCON (1<<16) /* performed a listen */
92     #define SO_WAITDATA (1<<17) /* wait data to read */
93     #define SO_NOSPACE (1<<18) /* no space to write */
94 niro 532
95 niro 816 /* Standard printout size */
96     #define PRINT_IP_MAX_SIZE 23
97     #define PRINT_NET_CONN "%s %6ld %6ld %-23s %-23s %-12s"
98     #define PRINT_NET_CONN_HEADER "\nProto Recv-Q Send-Q %-23s %-23s State "
99    
100     /* When there are IPv6 connections the IPv6 addresses will be
101     * truncated to none-recognition. The '-W' option makes the
102     * address columns wide enough to accomodate for longest possible
103     * IPv6 addresses, i.e. addresses of the form
104     * xxxx:xxxx:xxxx:xxxx:xxxx:xxxx:ddd.ddd.ddd.ddd
105     */
106     #define PRINT_IP_MAX_SIZE_WIDE 51 /* INET6_ADDRSTRLEN + 5 for the port number */
107     #define PRINT_NET_CONN_WIDE "%s %6ld %6ld %-51s %-51s %-12s"
108     #define PRINT_NET_CONN_HEADER_WIDE "\nProto Recv-Q Send-Q %-51s %-51s State "
109    
110     #define PROGNAME_WIDTH 20
111     #define PROGNAME_WIDTH_STR "20"
112     /* PROGNAME_WIDTH chars: 12345678901234567890 */
113     #define PROGNAME_BANNER "PID/Program name "
114    
115     struct prg_node {
116     struct prg_node *next;
117     long inode;
118     char name[PROGNAME_WIDTH];
119     };
120    
121     #define PRG_HASH_SIZE 211
122    
123     struct globals {
124     const char *net_conn_line;
125     smallint flags;
126     #if ENABLE_FEATURE_NETSTAT_PRG
127     smallint prg_cache_loaded;
128     struct prg_node *prg_hash[PRG_HASH_SIZE];
129     #endif
130     };
131     #define G (*ptr_to_globals)
132     #define flags (G.flags )
133     #define net_conn_line (G.net_conn_line )
134     #define prg_hash (G.prg_hash )
135     #define prg_cache_loaded (G.prg_cache_loaded)
136     #define INIT_G() do { \
137     SET_PTR_TO_GLOBALS(xzalloc(sizeof(G))); \
138     flags = NETSTAT_CONNECTED | NETSTAT_ALLPROTO; \
139     net_conn_line = PRINT_NET_CONN; \
140     } while (0)
141    
142    
143     #if ENABLE_FEATURE_NETSTAT_PRG
144    
145     /* Deliberately truncating long to unsigned *int* */
146     #define PRG_HASHIT(x) ((unsigned)(x) % PRG_HASH_SIZE)
147    
148     #define print_progname_banner() do { \
149     if (option_mask32 & OPT_prg) printf(PROGNAME_BANNER); \
150     } while (0)
151    
152     static void prg_cache_add(long inode, char *name)
153 niro 532 {
154 niro 816 unsigned hi = PRG_HASHIT(inode);
155     struct prg_node **pnp, *pn;
156    
157     prg_cache_loaded = 2;
158     for (pnp = prg_hash + hi; (pn = *pnp) != NULL; pnp = &pn->next) {
159     if (pn->inode == inode) {
160     /* Some warning should be appropriate here
161     as we got multiple processes for one i-node */
162     return;
163     }
164 niro 532 }
165 niro 816 *pnp = xzalloc(sizeof(struct prg_node));
166     pn = *pnp;
167     pn->inode = inode;
168     safe_strncpy(pn->name, name, PROGNAME_WIDTH);
169 niro 532 }
170    
171 niro 816 static const char *prg_cache_get(long inode)
172 niro 532 {
173 niro 816 unsigned hi = PRG_HASHIT(inode);
174     struct prg_node *pn;
175 niro 532
176 niro 816 for (pn = prg_hash[hi]; pn; pn = pn->next)
177     if (pn->inode == inode)
178     return pn->name;
179     return "-";
180 niro 532 }
181    
182 niro 816 #if ENABLE_FEATURE_CLEAN_UP
183     static void prg_cache_clear(void)
184 niro 532 {
185 niro 816 struct prg_node **pnp, *pn;
186    
187     for (pnp = prg_hash; pnp < prg_hash + PRG_HASH_SIZE; pnp++) {
188     while ((pn = *pnp) != NULL) {
189     *pnp = pn->next;
190     free(pn);
191     }
192     }
193     }
194 niro 532 #else
195 niro 816 #define prg_cache_clear() ((void)0)
196 niro 532 #endif
197    
198 niro 816 static long extract_socket_inode(const char *lname)
199     {
200     long inode = -1;
201 niro 532
202 niro 816 if (strncmp(lname, "socket:[", sizeof("socket:[")-1) == 0) {
203     /* "socket:[12345]", extract the "12345" as inode */
204     inode = bb_strtol(lname + sizeof("socket:[")-1, (char**)&lname, 0);
205     if (*lname != ']')
206     inode = -1;
207     } else if (strncmp(lname, "[0000]:", sizeof("[0000]:")-1) == 0) {
208     /* "[0000]:12345", extract the "12345" as inode */
209     inode = bb_strtol(lname + sizeof("[0000]:")-1, NULL, 0);
210     if (errno) /* not NUL terminated? */
211     inode = -1;
212     }
213 niro 532
214 niro 816 #if 0 /* bb_strtol returns all-ones bit pattern on ERANGE anyway */
215     if (errno == ERANGE)
216     inode = -1;
217 niro 532 #endif
218 niro 816 return inode;
219     }
220 niro 532
221 niro 816 static int FAST_FUNC file_act(const char *fileName,
222     struct stat *statbuf UNUSED_PARAM,
223     void *userData,
224     int depth UNUSED_PARAM)
225     {
226     char *linkname;
227     long inode;
228    
229     linkname = xmalloc_readlink(fileName);
230     if (linkname != NULL) {
231     inode = extract_socket_inode(linkname);
232     free(linkname);
233     if (inode >= 0)
234     prg_cache_add(inode, (char *)userData);
235 niro 532 }
236 niro 816 return TRUE;
237     }
238 niro 532
239 niro 816 static int FAST_FUNC dir_act(const char *fileName,
240     struct stat *statbuf UNUSED_PARAM,
241     void *userData UNUSED_PARAM,
242     int depth)
243     {
244     const char *shortName;
245     char *p, *q;
246     char cmdline_buf[512];
247     int i;
248 niro 532
249 niro 816 if (depth == 0) /* "/proc" itself */
250     return TRUE; /* continue looking one level below /proc */
251 niro 532
252 niro 816 shortName = fileName + sizeof("/proc/")-1; /* point after "/proc/" */
253     if (!isdigit(shortName[0])) /* skip /proc entries whic aren't processes */
254     return SKIP;
255    
256     p = concat_path_file(fileName, "cmdline"); /* "/proc/PID/cmdline" */
257     i = open_read_close(p, cmdline_buf, sizeof(cmdline_buf) - 1);
258     free(p);
259     if (i < 0)
260     return FALSE;
261     cmdline_buf[i] = '\0';
262     q = concat_path_file(shortName, bb_basename(cmdline_buf)); /* "PID/argv0" */
263    
264     /* go through all files in /proc/PID/fd */
265     p = concat_path_file(fileName, "fd");
266     i = recursive_action(p, ACTION_RECURSE | ACTION_QUIET,
267     file_act, NULL, (void *)q, 0);
268    
269     free(p);
270     free(q);
271    
272     if (!i)
273     return FALSE; /* signal permissions error to caller */
274    
275     return SKIP; /* caller should not recurse further into this dir. */
276 niro 532 }
277    
278 niro 816 static void prg_cache_load(void)
279 niro 532 {
280 niro 816 int load_ok;
281 niro 532
282 niro 816 prg_cache_loaded = 1;
283     load_ok = recursive_action("/proc", ACTION_RECURSE | ACTION_QUIET,
284     NULL, dir_act, NULL, 0);
285     if (load_ok)
286 niro 532 return;
287    
288 niro 816 if (prg_cache_loaded == 1)
289     bb_error_msg("can't scan /proc - are you root?");
290     else
291     bb_error_msg("showing only processes with your user ID");
292     }
293 niro 532
294 niro 816 #else
295 niro 532
296 niro 816 #define prg_cache_clear() ((void)0)
297     #define print_progname_banner() ((void)0)
298 niro 532
299 niro 816 #endif //ENABLE_FEATURE_NETSTAT_PRG
300 niro 532
301    
302 niro 816 #if ENABLE_FEATURE_IPV6
303     static void build_ipv6_addr(char* local_addr, struct sockaddr_in6* localaddr)
304     {
305     char addr6[INET6_ADDRSTRLEN];
306     struct in6_addr in6;
307    
308     sscanf(local_addr, "%08X%08X%08X%08X",
309     &in6.s6_addr32[0], &in6.s6_addr32[1],
310     &in6.s6_addr32[2], &in6.s6_addr32[3]);
311     inet_ntop(AF_INET6, &in6, addr6, sizeof(addr6));
312 niro 1123 inet_pton(AF_INET6, addr6, &localaddr->sin6_addr);
313 niro 816
314     localaddr->sin6_family = AF_INET6;
315     }
316     #endif
317    
318     static void build_ipv4_addr(char* local_addr, struct sockaddr_in* localaddr)
319     {
320 niro 1123 sscanf(local_addr, "%X", &localaddr->sin_addr.s_addr);
321     localaddr->sin_family = AF_INET;
322 niro 816 }
323 niro 532
324 niro 816 static const char *get_sname(int port, const char *proto, int numeric)
325     {
326     if (!port)
327     return "*";
328     if (!numeric) {
329     struct servent *se = getservbyport(port, proto);
330     if (se)
331     return se->s_name;
332     }
333     /* hummm, we may return static buffer here!! */
334     return itoa(ntohs(port));
335     }
336 niro 532
337 niro 816 static char *ip_port_str(struct sockaddr *addr, int port, const char *proto, int numeric)
338     {
339     char *host, *host_port;
340 niro 532
341 niro 816 /* Code which used "*" for INADDR_ANY is removed: it's ambiguous
342     * in IPv6, while "0.0.0.0" is not. */
343    
344     host = numeric ? xmalloc_sockaddr2dotted_noport(addr)
345     : xmalloc_sockaddr2host_noport(addr);
346    
347     host_port = xasprintf("%s:%s", host, get_sname(htons(port), proto, numeric));
348     free(host);
349     return host_port;
350 niro 532 }
351    
352 niro 816 struct inet_params {
353     int local_port, rem_port, state, uid;
354 niro 1123 union {
355     struct sockaddr sa;
356     struct sockaddr_in sin;
357 niro 816 #if ENABLE_FEATURE_IPV6
358 niro 1123 struct sockaddr_in6 sin6;
359 niro 532 #endif
360 niro 1123 } localaddr, remaddr;
361 niro 816 unsigned long rxq, txq, inode;
362     };
363 niro 532
364 niro 816 static int scan_inet_proc_line(struct inet_params *param, char *line)
365     {
366     int num;
367     char local_addr[64], rem_addr[64];
368 niro 532
369     num = sscanf(line,
370 niro 816 "%*d: %64[0-9A-Fa-f]:%X "
371     "%64[0-9A-Fa-f]:%X %X "
372     "%lX:%lX %*X:%*X "
373     "%*X %d %*d %ld ",
374     local_addr, &param->local_port,
375     rem_addr, &param->rem_port, &param->state,
376     &param->txq, &param->rxq,
377     &param->uid, &param->inode);
378     if (num < 9) {
379     return 1; /* error */
380     }
381 niro 532
382     if (strlen(local_addr) > 8) {
383 niro 816 #if ENABLE_FEATURE_IPV6
384 niro 1123 build_ipv6_addr(local_addr, &param->localaddr.sin6);
385     build_ipv6_addr(rem_addr, &param->remaddr.sin6);
386 niro 532 #endif
387     } else {
388 niro 1123 build_ipv4_addr(local_addr, &param->localaddr.sin);
389     build_ipv4_addr(rem_addr, &param->remaddr.sin);
390 niro 532 }
391 niro 816 return 0;
392     }
393 niro 532
394 niro 816 static void print_inet_line(struct inet_params *param,
395     const char *state_str, const char *proto, int is_connected)
396     {
397     if ((is_connected && (flags & NETSTAT_CONNECTED))
398     || (!is_connected && (flags & NETSTAT_LISTENING))
399     ) {
400     char *l = ip_port_str(
401 niro 1123 &param->localaddr.sa, param->local_port,
402 niro 816 proto, flags & NETSTAT_NUMERIC);
403     char *r = ip_port_str(
404 niro 1123 &param->remaddr.sa, param->rem_port,
405 niro 816 proto, flags & NETSTAT_NUMERIC);
406     printf(net_conn_line,
407     proto, param->rxq, param->txq, l, r, state_str);
408     #if ENABLE_FEATURE_NETSTAT_PRG
409     if (option_mask32 & OPT_prg)
410     printf("%."PROGNAME_WIDTH_STR"s", prg_cache_get(param->inode));
411     #endif
412     bb_putchar('\n');
413     free(l);
414     free(r);
415 niro 532 }
416 niro 816 }
417 niro 532
418 niro 816 static int FAST_FUNC tcp_do_one(char *line)
419     {
420     struct inet_params param;
421    
422     if (scan_inet_proc_line(&param, line))
423     return 1;
424    
425     print_inet_line(&param, tcp_state[param.state], "tcp", param.rem_port);
426     return 0;
427     }
428    
429     #if ENABLE_FEATURE_IPV6
430 niro 1123 # define NOT_NULL_ADDR(A) ( \
431     ( (A.sa.sa_family == AF_INET6) \
432     && (A.sin6.sin6_addr.s6_addr32[0] | A.sin6.sin6_addr.s6_addr32[1] | \
433     A.sin6.sin6_addr.s6_addr32[2] | A.sin6.sin6_addr.s6_addr32[3]) \
434     ) || ( \
435     (A.sa.sa_family == AF_INET) \
436     && A.sin.sin_addr.s_addr != 0 \
437     ) \
438 niro 816 )
439 niro 532 #else
440 niro 1123 # define NOT_NULL_ADDR(A) (A.sin.sin_addr.s_addr)
441 niro 532 #endif
442    
443 niro 816 static int FAST_FUNC udp_do_one(char *line)
444     {
445     int have_remaddr;
446     const char *state_str;
447     struct inet_params param;
448 niro 532
449 niro 816 if (scan_inet_proc_line(&param, line))
450     return 1;
451 niro 532
452 niro 816 state_str = "UNKNOWN";
453     switch (param.state) {
454     case TCP_ESTABLISHED:
455     state_str = "ESTABLISHED";
456     break;
457     case TCP_CLOSE:
458     state_str = "";
459     break;
460 niro 532 }
461 niro 816
462 niro 1123 have_remaddr = NOT_NULL_ADDR(param.remaddr);
463 niro 816 print_inet_line(&param, state_str, "udp", have_remaddr);
464     return 0;
465 niro 532 }
466    
467 niro 816 static int FAST_FUNC raw_do_one(char *line)
468     {
469     int have_remaddr;
470     struct inet_params param;
471 niro 532
472 niro 816 if (scan_inet_proc_line(&param, line))
473     return 1;
474    
475 niro 1123 have_remaddr = NOT_NULL_ADDR(param.remaddr);
476 niro 816 print_inet_line(&param, itoa(param.state), "raw", have_remaddr);
477     return 0;
478     }
479    
480     static int FAST_FUNC unix_do_one(char *line)
481 niro 532 {
482     unsigned long refcnt, proto, unix_flags;
483 niro 816 unsigned long inode;
484     int type, state;
485     int num, path_ofs;
486     const char *ss_proto, *ss_state, *ss_type;
487     char ss_flags[32];
488 niro 532
489 niro 816 /* 2.6.15 may report lines like "... @/tmp/fam-user-^@^@^@^@^@^@^@..."
490     * Other users report long lines filled by NUL bytes.
491     * (those ^@ are NUL bytes too). We see them as empty lines. */
492     if (!line[0])
493     return 0;
494    
495     path_ofs = 0; /* paranoia */
496     num = sscanf(line, "%*p: %lX %lX %lX %X %X %lu %n",
497     &refcnt, &proto, &unix_flags, &type, &state, &inode, &path_ofs);
498 niro 532 if (num < 6) {
499 niro 816 return 1; /* error */
500 niro 532 }
501 niro 816 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) != (NETSTAT_LISTENING|NETSTAT_CONNECTED)) {
502 niro 532 if ((state == SS_UNCONNECTED) && (unix_flags & SO_ACCEPTCON)) {
503 niro 816 if (!(flags & NETSTAT_LISTENING))
504     return 0;
505 niro 532 } else {
506 niro 816 if (!(flags & NETSTAT_CONNECTED))
507     return 0;
508 niro 532 }
509     }
510    
511     switch (proto) {
512     case 0:
513     ss_proto = "unix";
514     break;
515     default:
516     ss_proto = "??";
517     }
518    
519     switch (type) {
520     case SOCK_STREAM:
521     ss_type = "STREAM";
522     break;
523     case SOCK_DGRAM:
524     ss_type = "DGRAM";
525     break;
526     case SOCK_RAW:
527     ss_type = "RAW";
528     break;
529     case SOCK_RDM:
530     ss_type = "RDM";
531     break;
532     case SOCK_SEQPACKET:
533     ss_type = "SEQPACKET";
534     break;
535     default:
536     ss_type = "UNKNOWN";
537     }
538    
539     switch (state) {
540     case SS_FREE:
541     ss_state = "FREE";
542     break;
543     case SS_UNCONNECTED:
544     /*
545     * Unconnected sockets may be listening
546     * for something.
547     */
548     if (unix_flags & SO_ACCEPTCON) {
549     ss_state = "LISTENING";
550     } else {
551     ss_state = "";
552     }
553     break;
554     case SS_CONNECTING:
555     ss_state = "CONNECTING";
556     break;
557     case SS_CONNECTED:
558     ss_state = "CONNECTED";
559     break;
560     case SS_DISCONNECTING:
561     ss_state = "DISCONNECTING";
562     break;
563     default:
564     ss_state = "UNKNOWN";
565     }
566    
567     strcpy(ss_flags, "[ ");
568     if (unix_flags & SO_ACCEPTCON)
569     strcat(ss_flags, "ACC ");
570     if (unix_flags & SO_WAITDATA)
571     strcat(ss_flags, "W ");
572     if (unix_flags & SO_NOSPACE)
573     strcat(ss_flags, "N ");
574     strcat(ss_flags, "]");
575    
576 niro 816 printf("%-5s %-6ld %-11s %-10s %-13s %6lu ",
577     ss_proto, refcnt, ss_flags, ss_type, ss_state, inode
578     );
579    
580     #if ENABLE_FEATURE_NETSTAT_PRG
581     if (option_mask32 & OPT_prg)
582     printf("%-"PROGNAME_WIDTH_STR"s", prg_cache_get(inode));
583     #endif
584    
585     /* TODO: currently we stop at first NUL byte. Is it a problem? */
586     line += path_ofs;
587     *strchrnul(line, '\n') = '\0';
588     while (*line)
589     fputc_printable(*line++, stdout);
590     bb_putchar('\n');
591     return 0;
592 niro 532 }
593    
594 niro 816 static void do_info(const char *file, int FAST_FUNC (*proc)(char *))
595 niro 532 {
596 niro 816 int lnr;
597 niro 532 FILE *procinfo;
598 niro 816 char *buffer;
599 niro 532
600 niro 816 /* _stdin is just to save "r" param */
601     procinfo = fopen_or_warn_stdin(file);
602 niro 532 if (procinfo == NULL) {
603 niro 816 return;
604 niro 532 }
605 niro 816 lnr = 0;
606     /* Why xmalloc_fgets_str? because it doesn't stop on NULs */
607     while ((buffer = xmalloc_fgets_str(procinfo, "\n")) != NULL) {
608     /* line 0 is skipped */
609     if (lnr && proc(buffer))
610     bb_error_msg("%s: bogus data on line %d", file, lnr + 1);
611     lnr++;
612     free(buffer);
613     }
614     fclose(procinfo);
615 niro 532 }
616    
617 niro 816 int netstat_main(int argc, char **argv) MAIN_EXTERNALLY_VISIBLE;
618     int netstat_main(int argc UNUSED_PARAM, char **argv)
619 niro 532 {
620 niro 816 const char *net_conn_line_header = PRINT_NET_CONN_HEADER;
621 niro 532 unsigned opt;
622    
623 niro 816 INIT_G();
624    
625 niro 532 /* Option string must match NETSTAT_xxx constants */
626 niro 816 opt = getopt32(argv, NETSTAT_OPTS);
627 niro 532 if (opt & 0x1) { // -l
628     flags &= ~NETSTAT_CONNECTED;
629     flags |= NETSTAT_LISTENING;
630     }
631     if (opt & 0x2) flags |= NETSTAT_LISTENING | NETSTAT_CONNECTED; // -a
632     //if (opt & 0x4) // -e
633     if (opt & 0x8) flags |= NETSTAT_NUMERIC; // -n
634     //if (opt & 0x10) // -t: NETSTAT_TCP
635     //if (opt & 0x20) // -u: NETSTAT_UDP
636     //if (opt & 0x40) // -w: NETSTAT_RAW
637     //if (opt & 0x80) // -x: NETSTAT_UNIX
638 niro 816 if (opt & OPT_route) { // -r
639     #if ENABLE_ROUTE
640     bb_displayroutes(flags & NETSTAT_NUMERIC, !(opt & OPT_extended));
641 niro 532 return 0;
642     #else
643 niro 816 bb_show_usage();
644 niro 532 #endif
645     }
646 niro 816 if (opt & OPT_wide) { // -W
647     net_conn_line = PRINT_NET_CONN_WIDE;
648     net_conn_line_header = PRINT_NET_CONN_HEADER_WIDE;
649     }
650     #if ENABLE_FEATURE_NETSTAT_PRG
651     if (opt & OPT_prg) { // -p
652     prg_cache_load();
653     }
654     #endif
655 niro 532
656     opt &= NETSTAT_ALLPROTO;
657     if (opt) {
658     flags &= ~NETSTAT_ALLPROTO;
659     flags |= opt;
660     }
661     if (flags & (NETSTAT_TCP|NETSTAT_UDP|NETSTAT_RAW)) {
662     printf("Active Internet connections "); /* xxx */
663    
664 niro 816 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
665 niro 532 printf("(servers and established)");
666 niro 816 else if (flags & NETSTAT_LISTENING)
667     printf("(only servers)");
668     else
669     printf("(w/o servers)");
670     printf(net_conn_line_header, "Local Address", "Foreign Address");
671     print_progname_banner();
672     bb_putchar('\n');
673 niro 532 }
674 niro 816 if (flags & NETSTAT_TCP) {
675     do_info("/proc/net/tcp", tcp_do_one);
676     #if ENABLE_FEATURE_IPV6
677     do_info("/proc/net/tcp6", tcp_do_one);
678 niro 532 #endif
679 niro 816 }
680     if (flags & NETSTAT_UDP) {
681     do_info("/proc/net/udp", udp_do_one);
682     #if ENABLE_FEATURE_IPV6
683     do_info("/proc/net/udp6", udp_do_one);
684 niro 532 #endif
685 niro 816 }
686     if (flags & NETSTAT_RAW) {
687     do_info("/proc/net/raw", raw_do_one);
688     #if ENABLE_FEATURE_IPV6
689     do_info("/proc/net/raw6", raw_do_one);
690 niro 532 #endif
691 niro 816 }
692     if (flags & NETSTAT_UNIX) {
693 niro 532 printf("Active UNIX domain sockets ");
694 niro 816 if ((flags & (NETSTAT_LISTENING|NETSTAT_CONNECTED)) == (NETSTAT_LISTENING|NETSTAT_CONNECTED))
695 niro 532 printf("(servers and established)");
696 niro 816 else if (flags & NETSTAT_LISTENING)
697     printf("(only servers)");
698     else
699     printf("(w/o servers)");
700     printf("\nProto RefCnt Flags Type State I-Node ");
701     print_progname_banner();
702     printf("Path\n");
703     do_info("/proc/net/unix", unix_do_one);
704 niro 532 }
705 niro 816 prg_cache_clear();
706 niro 532 return 0;
707     }