2 * snmptable.c - walk a table and print it nicely
4 * Update: 1999-10-26 <rs-snmp@revelstone.com>
5 * Added ability to use MIB to query tables with non-sequential column OIDs
6 * Added code to handle sparse tables
8 * Update: 1998-07-17 <jhy@gsu.edu>
9 * Added text <special options> to usage().
11 /**********************************************************************
12 Copyright 1997 Niels Baggesen
16 Permission to use, copy, modify, and distribute this software and its
17 documentation for any purpose and without fee is hereby granted,
18 provided that the above copyright notice appear in all copies.
20 I DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING
21 ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL
22 I BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR
23 ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS,
24 WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION,
25 ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS
27 ******************************************************************/
28 #include <net-snmp/net-snmp-config.h>
41 #include <sys/types.h>
43 # include <netinet/in.h>
45 #if TIME_WITH_SYS_TIME
47 # include <sys/timeb.h>
49 # include <sys/time.h>
54 # include <sys/time.h>
60 #include <sys/select.h>
70 #include <arpa/inet.h>
73 #include <net-snmp/net-snmp-includes.h>
82 static char **data
= NULL
;
83 static char **indices
= NULL
;
84 static int index_width
= sizeof("index") - 1;
88 static int headers_only
= 0;
89 static int no_headers
= 0;
90 static int max_width
= 0;
92 static int show_index
= 0;
93 static char *field_separator
= NULL
;
94 static char *table_name
;
95 static oid name
[MAX_OID_LEN
];
96 static size_t name_length
;
97 static oid root
[MAX_OID_LEN
];
98 static size_t rootlen
;
99 static int localdebug
;
100 static int exitval
= 0;
101 static int use_getbulk
= 1;
102 static int max_getbulk
= 25;
105 void get_field_names(char *);
106 void get_table_entries(netsnmp_session
* ss
);
107 void getbulk_table_entries(netsnmp_session
* ss
);
108 void print_table(void);
111 optProc(int argc
, char *const *argv
, int opt
)
116 * Handle new '-C' command-specific meta-options
123 max_width
= atoi(argv
[optind
]);
124 if (max_width
== 0) {
126 fprintf(stderr
, "Bad -Cw option: %s\n",
133 fprintf(stderr
, "Bad -Cw option: no argument given\n");
140 field_separator
= argv
[optind
];
143 fprintf(stderr
, "Bad -Cf option: no argument given\n");
164 fprintf(stderr
, "Bad option after -C: %c\n", optarg
[-1]);
175 fprintf(stderr
, "USAGE: snmptable ");
176 snmp_parse_args_usage(stderr
);
177 fprintf(stderr
, " TABLE-OID\n\n");
178 snmp_parse_args_descriptions(stderr
);
180 " -C APPOPTS\t\tSet various application specific behaviours:\n");
181 fprintf(stderr
, "\t\t\t b: brief field names\n");
182 fprintf(stderr
, "\t\t\t B: do not use GETBULK requests\n");
183 fprintf(stderr
, "\t\t\t f<STR>: print table delimitied with <STR>\n");
184 fprintf(stderr
, "\t\t\t h: print only the column headers\n");
185 fprintf(stderr
, "\t\t\t H: print no column headers\n");
186 fprintf(stderr
, "\t\t\t i: print index values\n");
187 fprintf(stderr
, "\t\t\t w<NUM>: print table in parts of <NUM> chars width\n");
196 for (i
= 0; i
< fields
/ 2; i
++) {
197 memcpy(&tmp
, &(column
[i
]), sizeof(struct column
));
198 memcpy(&(column
[i
]), &(column
[fields
- 1 - i
]),
199 sizeof(struct column
));
200 memcpy(&(column
[fields
- 1 - i
]), &tmp
, sizeof(struct column
));
205 main(int argc
, char *argv
[])
207 netsnmp_session session
, *ss
;
210 setvbuf(stdout
, NULL
, _IOLBF
, 1024);
211 netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID
,
212 NETSNMP_DS_LIB_QUICK_PRINT
, 1);
215 * get the common command line arguments
217 switch (snmp_parse_args(argc
, argv
, &session
, "C:", optProc
)) {
228 * get the initial object and subtree
231 * specified on the command line
233 if (optind
+ 1 != argc
) {
234 fprintf(stderr
, "Must have exactly one table name\n");
238 rootlen
= MAX_OID_LEN
;
239 if (!snmp_parse_oid(argv
[optind
], root
, &rootlen
)) {
240 snmp_perror(argv
[optind
]);
243 localdebug
= netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID
,
244 NETSNMP_DS_LIB_DUMP_PACKET
);
245 tblname
= strrchr(argv
[optind
], '.');
247 tblname
= strrchr(argv
[optind
], ':');
251 tblname
= argv
[optind
];
253 get_field_names(tblname
);
257 * open an SNMP session
260 ss
= snmp_open(&session
);
263 * diagnose snmp_open errors with the input netsnmp_session pointer
265 snmp_sess_perror("snmptable", &session
);
270 if (ss
->version
== SNMP_VERSION_1
)
274 getbulk_table_entries(ss
);
276 get_table_entries(ss
);
284 if (entries
|| headers_only
)
287 printf("%s: No entries\n", table_name
);
295 int entry
, field
, first_field
, last_field
=
298 char string_buf
[SPRINT_MAX_LEN
];
299 char *index_fmt
= NULL
;
301 if (!no_headers
&& !headers_only
)
302 printf("SNMP table: %s\n\n", table_name
);
304 for (field
= 0; field
< fields
; field
++) {
305 if (field_separator
== NULL
)
306 sprintf(string_buf
, "%%%ds", column
[field
].width
+ 1);
307 else if (field
== 0 && !show_index
)
308 sprintf(string_buf
, "%%s");
310 sprintf(string_buf
, "%s%%s", field_separator
);
311 column
[field
].fmt
= strdup(string_buf
);
314 if (field_separator
== NULL
)
315 sprintf(string_buf
, "%%%ds", index_width
);
317 sprintf(string_buf
, "%%s");
318 index_fmt
= strdup(string_buf
);
321 while (last_field
!= fields
) {
323 if (part
!= 1 && !no_headers
)
324 printf("\nSNMP table %s, part %d\n\n", table_name
, part
);
325 first_field
= last_field
;
327 if (show_index
&& !no_headers
) {
329 printf(index_fmt
, "index");
332 for (field
= first_field
; field
< fields
; field
++) {
333 width
+= column
[field
].width
+ 1;
334 if (field
!= first_field
&& width
> max_width
338 printf(column
[field
].fmt
, column
[field
].label
);
343 for (entry
= 0; entry
< entries
; entry
++) {
345 printf(index_fmt
, indices
[entry
]);
346 for (field
= first_field
; field
< last_field
; field
++) {
347 printf(column
[field
].fmt
, dp
[field
] ? dp
[field
] : "?");
356 get_field_names(char *tblname
)
358 u_char
*buf
= NULL
, *name_p
= NULL
;
359 size_t buf_len
= 0, out_len
= 0;
360 struct tree
*tbl
= NULL
;
363 tbl
= find_tree_node(tblname
, -1);
365 tbl
= tbl
->child_list
;
367 root
[rootlen
++] = tbl
->subid
;
368 tbl
= tbl
->child_list
;
374 if (sprint_realloc_objid
375 (&buf
, &buf_len
, &out_len
, 1, root
, rootlen
- 1)) {
378 buf_len
= out_len
= 0;
380 table_name
= strdup("[TRUNCATED]");
388 if (tbl
->access
== MIB_ACCESS_NOACCESS
) {
390 tbl
= tbl
->next_peer
;
396 root
[rootlen
] = tbl
->subid
;
397 tbl
= tbl
->next_peer
;
401 root
[rootlen
] = fields
;
404 if (sprint_realloc_objid
405 (&buf
, &buf_len
, &out_len
, 1, root
, rootlen
+ 1)) {
406 name_p
= strrchr(buf
, '.');
407 if (name_p
== NULL
) {
408 name_p
= strrchr(buf
, ':');
410 if (name_p
== NULL
) {
419 printf("%s %c\n", buf
, name_p
[0]);
421 if ('0' <= name_p
[0] && name_p
[0] <= '9') {
426 column
= (struct column
*) malloc(sizeof(*column
));
429 (struct column
*) realloc(column
,
430 fields
* sizeof(*column
));
432 column
[fields
- 1].label
= strdup(name_p
);
433 column
[fields
- 1].width
= strlen(name_p
);
434 column
[fields
- 1].subid
= root
[rootlen
];
437 fprintf(stderr
, "Was that a table? %s\n", buf
);
441 memmove(name
, root
, rootlen
* sizeof(oid
));
442 name_length
= rootlen
+ 1;
443 name_p
= strrchr(buf
, '.');
444 if (name_p
== NULL
) {
445 name_p
= strrchr(buf
, ':');
447 if (name_p
!= NULL
) {
450 if (brief
&& fields
> 1) {
452 int common
= strlen(column
[0].label
);
454 for (field
= 1; field
< fields
; field
++) {
455 f1
= column
[field
- 1].label
;
456 f2
= column
[field
].label
;
457 while (*f1
&& *f1
++ == *f2
++);
458 len
= f2
- column
[field
].label
- 1;
463 for (field
= 0; field
< fields
; field
++) {
464 column
[field
].label
+= common
;
465 column
[field
].width
-= common
;
475 get_table_entries(netsnmp_session
* ss
)
478 netsnmp_pdu
*pdu
, *response
;
479 netsnmp_variable_list
*vars
;
485 size_t out_len
= 0, buf_len
= 0;
489 int end_of_table
= 0;
490 int have_current_index
;
494 * 1) Deal with multiple index fields
495 * 2) Deal with variable length index fields
496 * 3) optimize to remove a sparse column from get-requests
501 * create PDU for GETNEXT request and add object name to request
503 pdu
= snmp_pdu_create(SNMP_MSG_GETNEXT
);
504 for (i
= 1; i
<= fields
; i
++) {
505 name
[rootlen
] = column
[i
- 1].subid
;
506 snmp_add_null_var(pdu
, name
, name_length
);
512 status
= snmp_synch_response(ss
, pdu
, &response
);
513 if (status
== STAT_SUCCESS
) {
514 if (response
->errstat
== SNMP_ERR_NOERROR
) {
516 * check resulting variables
518 vars
= response
->variables
;
520 if (entries
>= allocated
) {
521 if (allocated
== 0) {
524 (char **) malloc(allocated
* fields
*
527 allocated
* fields
* sizeof(char *));
530 (char **) malloc(allocated
*
535 (char **) realloc(data
,
538 memset(data
+ entries
* fields
, 0,
540 entries
) * fields
* sizeof(char *));
543 (char **) realloc(indices
,
548 dp
= data
+ (entries
- 1) * fields
;
550 end_of_table
= 1; /* assume end of table */
551 have_current_index
= 0;
552 name_length
= rootlen
+ 1;
553 for (vars
= response
->variables
; vars
;
554 vars
= vars
->next_variable
) {
556 name
[rootlen
] = column
[col
].subid
;
557 if ((vars
->name_length
< name_length
) ||
558 ((int) vars
->name
[rootlen
] != column
[col
].subid
) ||
559 memcmp(name
, vars
->name
,
560 name_length
* sizeof(oid
)) != 0
561 || vars
->type
== SNMP_ENDOFMIBVIEW
) {
563 * not part of this subtree
566 fprint_variable(stderr
, vars
->name
,
567 vars
->name_length
, vars
);
568 fprintf(stderr
, " => ignored\n");
576 if (!have_current_index
) {
578 have_current_index
= 1;
579 name_length
= vars
->name_length
;
580 memcpy(name
, vars
->name
,
581 name_length
* sizeof(oid
));
583 if (!sprint_realloc_objid
584 (&buf
, &buf_len
, &out_len
, 1, vars
->name
,
585 vars
->name_length
)) {
588 i
= vars
->name_length
- rootlen
+ 1;
589 if (localdebug
|| show_index
) {
590 if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID
,
591 NETSNMP_DS_LIB_EXTENDED_INDEX
)) {
592 name_p
= strchr(buf
, '[');
594 switch (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID
,
595 NETSNMP_DS_LIB_OID_OUTPUT_FORMAT
)) {
596 case NETSNMP_OID_OUTPUT_MODULE
:
598 name_p
= strrchr(buf
, ':');
600 case NETSNMP_OID_OUTPUT_SUFFIX
:
603 case NETSNMP_OID_OUTPUT_FULL
:
604 case NETSNMP_OID_OUTPUT_NUMERIC
:
605 case NETSNMP_OID_OUTPUT_UCD
:
606 name_p
= buf
+ strlen(table_name
)+1;
607 name_p
= strchr(name_p
, '.')+1;
610 fprintf(stderr
, "Unrecognized -O option: %d\n",
611 netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID
,
612 NETSNMP_DS_LIB_OID_OUTPUT_FORMAT
));
615 name_p
= strchr(name_p
, '.') + 1;
619 printf("Name: %s Index: %s\n", buf
, name_p
);
622 indices
[entries
- 1] = strdup(name_p
);
629 if (localdebug
&& buf
) {
630 printf("%s => taken\n", buf
);
633 sprint_realloc_value(&buf
, &buf_len
, &out_len
, 1,
634 vars
->name
, vars
->name_length
,
636 for (cp
= buf
; *cp
; cp
++) {
645 if (i
> column
[col
].width
) {
646 column
[col
].width
= i
;
653 * not part of this subtree
656 printf("End of table: %s\n",
657 buf
? (char *) buf
: "[NIL]");
664 * error in response, print it
667 if (response
->errstat
== SNMP_ERR_NOSUCHNAME
) {
668 printf("End of MIB\n");
670 fprintf(stderr
, "Error in packet.\nReason: %s\n",
671 snmp_errstring(response
->errstat
));
672 if (response
->errindex
!= 0) {
673 fprintf(stderr
, "Failed object: ");
674 for (count
= 1, vars
= response
->variables
;
675 vars
&& count
!= response
->errindex
;
676 vars
= vars
->next_variable
, count
++)
679 fprint_objid(stderr
, vars
->name
,
682 fprintf(stderr
, "\n");
687 } else if (status
== STAT_TIMEOUT
) {
688 fprintf(stderr
, "Timeout: No Response from %s\n",
692 } else { /* status == STAT_ERROR */
693 snmp_sess_perror("snmptable", ss
);
698 snmp_free_pdu(response
);
703 getbulk_table_entries(netsnmp_session
* ss
)
706 netsnmp_pdu
*pdu
, *response
;
707 netsnmp_variable_list
*vars
, *last_var
;
713 size_t buf_len
= 0, out_len
= 0;
720 * create PDU for GETNEXT request and add object name to request
722 pdu
= snmp_pdu_create(SNMP_MSG_GETBULK
);
723 pdu
->non_repeaters
= 0;
724 pdu
->max_repetitions
= max_getbulk
;
725 snmp_add_null_var(pdu
, name
, name_length
);
730 status
= snmp_synch_response(ss
, pdu
, &response
);
731 if (status
== STAT_SUCCESS
) {
732 if (response
->errstat
== SNMP_ERR_NOERROR
) {
734 * check resulting variables
736 vars
= response
->variables
;
740 sprint_realloc_objid(&buf
, &buf_len
, &out_len
, 1,
741 vars
->name
, vars
->name_length
);
742 if (vars
->type
== SNMP_ENDOFMIBVIEW
||
743 memcmp(vars
->name
, name
,
744 rootlen
* sizeof(oid
)) != 0) {
746 printf("%s => end of table\n",
747 buf
? (char *) buf
: "[NIL]");
753 printf("%s => taken\n",
754 buf
? (char *) buf
: "[NIL]");
756 if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID
,
757 NETSNMP_DS_LIB_EXTENDED_INDEX
)) {
758 name_p
= strchr(buf
, '[');
760 switch (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID
,
761 NETSNMP_DS_LIB_OID_OUTPUT_FORMAT
)) {
762 case NETSNMP_OID_OUTPUT_MODULE
:
764 name_p
= strrchr(buf
, ':');
766 case NETSNMP_OID_OUTPUT_SUFFIX
:
769 case NETSNMP_OID_OUTPUT_FULL
:
770 case NETSNMP_OID_OUTPUT_NUMERIC
:
771 case NETSNMP_OID_OUTPUT_UCD
:
772 name_p
= buf
+ strlen(table_name
)+1;
773 name_p
= strchr(name_p
, '.')+1;
776 fprintf(stderr
, "Unrecognized -O option: %d\n",
777 netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID
,
778 NETSNMP_DS_LIB_OID_OUTPUT_FORMAT
));
781 name_p
= strchr(name_p
, '.') + 1;
783 for (row
= 0; row
< entries
; row
++)
784 if (strcmp(name_p
, indices
[row
]) == 0)
786 if (row
== entries
) {
788 if (entries
>= allocated
) {
789 if (allocated
== 0) {
792 (char **) malloc(allocated
* fields
*
798 (char **) malloc(allocated
*
803 (char **) realloc(data
,
806 memset(data
+ entries
* fields
, 0,
811 (char **) realloc(indices
,
816 indices
[row
] = strdup(name_p
);
821 dp
= data
+ row
* fields
;
823 sprint_realloc_value(&buf
, &buf_len
, &out_len
, 1,
824 vars
->name
, vars
->name_length
,
826 for (cp
= buf
; *cp
; cp
++)
829 for (col
= 0; col
< fields
; col
++)
830 if (column
[col
].subid
== vars
->name
[rootlen
])
836 if (i
> column
[col
].width
)
837 column
[col
].width
= i
;
839 vars
= vars
->next_variable
;
842 name_length
= last_var
->name_length
;
843 memcpy(name
, last_var
->name
,
844 name_length
* sizeof(oid
));
848 * error in response, print it
851 if (response
->errstat
== SNMP_ERR_NOSUCHNAME
) {
852 printf("End of MIB\n");
854 fprintf(stderr
, "Error in packet.\nReason: %s\n",
855 snmp_errstring(response
->errstat
));
856 if (response
->errstat
== SNMP_ERR_NOSUCHNAME
) {
858 "The request for this object identifier failed: ");
859 for (count
= 1, vars
= response
->variables
;
860 vars
&& count
!= response
->errindex
;
861 vars
= vars
->next_variable
, count
++)
864 fprint_objid(stderr
, vars
->name
,
867 fprintf(stderr
, "\n");
872 } else if (status
== STAT_TIMEOUT
) {
873 fprintf(stderr
, "Timeout: No Response from %s\n",
877 } else { /* status == STAT_ERROR */
878 snmp_sess_perror("snmptable", ss
);
883 snmp_free_pdu(response
);