source: MondoRescue/branches/3.3/mindi-busybox/networking/netstat.c@ 3734

Last change on this file since 3734 was 3621, checked in by Bruno Cornec, 10 years ago

New 3?3 banch for incorporation of latest busybox 1.25. Changing minor version to handle potential incompatibilities.

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