1 /*
2  * snmptable.c - walk a table and print it nicely
3  *
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
7  *
8  * Update: 1998-07-17 <jhy@gsu.edu>
9  * Added text <special options> to usage().
10  */
11 /**********************************************************************
12 	Copyright 1997 Niels Baggesen
13 
14                       All Rights Reserved
15 
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.
19 
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
26 SOFTWARE.
27 ******************************************************************/
28 #include <net-snmp/net-snmp-config.h>
29 
30 #if HAVE_STDLIB_H
31 #include <stdlib.h>
32 #endif
33 #if HAVE_UNISTD_H
34 #include <unistd.h>
35 #endif
36 #if HAVE_STRING_H
37 #include <string.h>
38 #else
39 #include <strings.h>
40 #endif
41 #include <sys/types.h>
42 #if HAVE_NETINET_IN_H
43 # include <netinet/in.h>
44 #endif
45 #if TIME_WITH_SYS_TIME
46 # include <sys/time.h>
47 # include <time.h>
48 #else
49 # if HAVE_SYS_TIME_H
50 #  include <sys/time.h>
51 # else
52 #  include <time.h>
53 # endif
54 #endif
55 #if HAVE_SYS_SELECT_H
56 #include <sys/select.h>
57 #endif
58 #include <stdio.h>
59 #if HAVE_NETDB_H
60 #include <netdb.h>
61 #endif
62 #if HAVE_ARPA_INET_H
63 #include <arpa/inet.h>
64 #endif
65 
66 #include <net-snmp/net-snmp-includes.h>
67 
68 struct column {
69     int             width;
70     oid             subid;
71     char           *label;
72     char           *fmt;
73 }              *column = NULL;
74 
75 static char   **data = NULL;
76 static char   **indices = NULL;
77 static int      index_width = sizeof("index ") - 1;
78 static int      fields;
79 static int      entries;
80 static int      allocated;
81 static int      end_of_table = 1;
82 static int      headers_only = 0;
83 static int      no_headers = 0;
84 static int      max_width = 0;
85 static int      column_width = 0;
86 static int      brief = 0;
87 static int      show_index = 0;
88 static const char    *left_justify_flag = "";
89 static char    *field_separator = NULL;
90 static char    *table_name;
91 static oid      name[MAX_OID_LEN];
92 static size_t   name_length;
93 static oid      root[MAX_OID_LEN];
94 static size_t   rootlen;
95 static int      localdebug;
96 static int      exitval = 1;
97 static int      use_getbulk = 1;
98 static int      max_getbulk = 10;
99 static int      extra_columns = 0;
100 
101 void            usage(void);
102 void            get_field_names(void);
103 void            get_table_entries(netsnmp_session * ss);
104 void            getbulk_table_entries(netsnmp_session * ss);
105 void            print_table(void);
106 
107 static void
optProc(int argc,char * const * argv,int opt)108 optProc(int argc, char *const *argv, int opt)
109 {
110     switch (opt) {
111     case 'C':
112         /*
113          * Handle new '-C' command-specific meta-options
114          */
115         while (*optarg) {
116             switch (*optarg++) {
117             case 'w':
118 		if (optind < argc) {
119 		    if (argv[optind]) {
120 			max_width = atoi(argv[optind]);
121 			if (max_width == 0) {
122 			    usage();
123 			    fprintf(stderr, "Bad -Cw option: %s\n",
124 				    argv[optind]);
125 			    exit(1);
126 			}
127 		    }
128 		} else {
129 		    usage();
130                     fprintf(stderr, "Bad -Cw option: no argument given\n");
131 		    exit(1);
132 		}
133 		optind++;
134                 break;
135             case 'c':
136 		if (optind < argc) {
137 		    if (argv[optind]) {
138 			column_width = atoi(argv[optind]);
139 			if (column_width <= 2) {
140 			    usage();
141 			    fprintf(stderr, "Bad -Cc option: %s\n",
142 				    argv[optind]);
143 			    exit(1);
144 			}
145                         /* Reduce by one for space at end of column */
146                         column_width -= 1;
147 		    }
148 		} else {
149 		    usage();
150                     fprintf(stderr, "Bad -Cc option: no argument given\n");
151 		    exit(1);
152 		}
153 		optind++;
154                 break;
155             case 'l':
156                 left_justify_flag = "-";
157                 break;
158             case 'f':
159 		if (optind < argc) {
160 		    field_separator = argv[optind];
161 		} else {
162                     usage();
163 		    fprintf(stderr, "Bad -Cf option: no argument given\n");
164 		    exit(1);
165 		}
166 		optind++;
167                 break;
168             case 'h':
169                 headers_only = 1;
170                 break;
171             case 'H':
172                 no_headers = 1;
173                 break;
174             case 'B':
175                 use_getbulk = 0;
176                 break;
177             case 'b':
178                 brief = 1;
179                 break;
180             case 'i':
181                 show_index = 1;
182                 break;
183             case 'r':
184 		if (optind < argc) {
185 		    if (argv[optind]) {
186 			max_getbulk = atoi(argv[optind]);
187 			if (max_getbulk == 0) {
188 			    usage();
189 			    fprintf(stderr, "Bad -Cr option: %s\n",
190 				    argv[optind]);
191 			    exit(1);
192 			}
193 		    }
194 		} else {
195 		    usage();
196                     fprintf(stderr, "Bad -Cr option: no argument given\n");
197 		    exit(1);
198 		}
199 		optind++;
200                 break;
201             default:
202                 fprintf(stderr, "Bad option after -C: %c\n", optarg[-1]);
203                 usage();
204                 exit(1);
205             }
206         }
207         break;
208     }
209 }
210 
211 void
usage(void)212 usage(void)
213 {
214     fprintf(stderr, "USAGE: snmptable ");
215     snmp_parse_args_usage(stderr);
216     fprintf(stderr, " TABLE-OID\n\n");
217     snmp_parse_args_descriptions(stderr);
218     fprintf(stderr,
219 	    "  -C APPOPTS\t\tSet various application specific behaviours:\n");
220     fprintf(stderr, "\t\t\t  b:       brief field names\n");
221     fprintf(stderr, "\t\t\t  B:       do not use GETBULK requests\n");
222     fprintf(stderr, "\t\t\t  c<NUM>:  print table in columns of <NUM> chars width\n");
223     fprintf(stderr, "\t\t\t  f<STR>:  print table delimitied with <STR>\n");
224     fprintf(stderr, "\t\t\t  h:       print only the column headers\n");
225     fprintf(stderr, "\t\t\t  H:       print no column headers\n");
226     fprintf(stderr, "\t\t\t  i:       print index values\n");
227     fprintf(stderr, "\t\t\t  l:       left justify output\n");
228     fprintf(stderr, "\t\t\t  r<NUM>:  for GETBULK: set max-repeaters to <NUM>\n");
229     fprintf(stderr, "\t\t\t           for GETNEXT: retrieve <NUM> entries at a time\n");
230     fprintf(stderr, "\t\t\t  w<NUM>:  print table in parts of <NUM> chars width\n");
231 }
232 
233 void
reverse_fields(void)234 reverse_fields(void)
235 {
236     struct column   tmp;
237     int             i;
238 
239     for (i = 0; i < fields / 2; i++) {
240         memcpy(&tmp, &(column[i]), sizeof(struct column));
241         memcpy(&(column[i]), &(column[fields - 1 - i]),
242                sizeof(struct column));
243         memcpy(&(column[fields - 1 - i]), &tmp, sizeof(struct column));
244     }
245 }
246 
247 int
main(int argc,char * argv[])248 main(int argc, char *argv[])
249 {
250     netsnmp_session session, *ss;
251     int            total_entries = 0;
252 
253     SOCK_STARTUP;
254 
255     netsnmp_set_line_buffering(stdout);
256 
257     netsnmp_ds_set_boolean(NETSNMP_DS_LIBRARY_ID,
258                            NETSNMP_DS_LIB_QUICK_PRINT, 1);
259 
260     /*
261      * get the common command line arguments
262      */
263     switch (snmp_parse_args(argc, argv, &session, "C:", optProc)) {
264     case NETSNMP_PARSE_ARGS_ERROR:
265         goto out;
266     case NETSNMP_PARSE_ARGS_SUCCESS_EXIT:
267         exitval = 0;
268         goto out;
269     case NETSNMP_PARSE_ARGS_ERROR_USAGE:
270         usage();
271         goto out;
272     default:
273         break;
274     }
275 
276     /*
277      * get the initial object and subtree
278      */
279     /*
280      * specified on the command line
281      */
282     if (optind + 1 != argc) {
283         fprintf(stderr, "Must have exactly one table name\n");
284         usage();
285         goto out;
286     }
287 
288     rootlen = MAX_OID_LEN;
289     if (!snmp_parse_oid(argv[optind], root, &rootlen)) {
290         snmp_perror(argv[optind]);
291         goto out;
292     }
293     localdebug = netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
294                                         NETSNMP_DS_LIB_DUMP_PACKET);
295 
296     get_field_names();
297     reverse_fields();
298 
299     /*
300      * open an SNMP session
301      */
302     ss = snmp_open(&session);
303     if (ss == NULL) {
304         /*
305          * diagnose snmp_open errors with the input netsnmp_session pointer
306          */
307         snmp_sess_perror("snmptable", &session);
308         goto out;
309     }
310 
311 #ifndef NETSNMP_DISABLE_SNMPV1
312     if (ss->version == SNMP_VERSION_1)
313         use_getbulk = 0;
314 #endif
315 
316     exitval = 0;
317 
318     do {
319         entries = 0;
320         allocated = 0;
321         if (!headers_only) {
322             if (use_getbulk)
323                 getbulk_table_entries(ss);
324             else
325                 get_table_entries(ss);
326         }
327 
328         if (exitval)
329             goto close_session;
330 
331         if (entries || headers_only)
332             print_table();
333 
334         if (data) {
335             int i, j;
336             for (i = 0; i < entries; i++)
337                 for (j = 0; j < fields; j++)
338                 free(data[i*fields+j]);
339             free (data);
340             data = NULL;
341         }
342 
343         if (indices) {
344             int i;
345             for (i = 0; i < entries; i++)
346                 free(indices[i]);
347             free (indices);
348             indices = NULL;
349         }
350 
351         total_entries += entries;
352 
353     } while (!end_of_table);
354 
355     if (total_entries == 0)
356         printf("%s: No entries\n", table_name);
357     if (extra_columns)
358 	printf("%s: WARNING: More columns on agent than in MIB\n", table_name);
359 
360     exitval = 0;
361 
362 close_session:
363     snmp_close(ss);
364 
365 out:
366     SOCK_CLEANUP;
367     return exitval;
368 }
369 
370 void
print_table(void)371 print_table(void)
372 {
373     int             entry, field, first_field, last_field = 0, width, part = 0;
374     char          **dp;
375     char            string_buf[SPRINT_MAX_LEN];
376     char           *index_fmt = NULL;
377     static int      first_pass = 1;
378 
379     if (!no_headers && !headers_only && first_pass)
380         printf("SNMP table: %s\n\n", table_name);
381 
382     for (field = 0; field < fields; field++) {
383         if (column_width != 0)
384             sprintf(string_buf, "%%%s%d.%ds", left_justify_flag,
385                     column_width + 1, column_width );
386         else if (field_separator == NULL)
387             sprintf(string_buf, "%%%s%ds", left_justify_flag,
388                     column[field].width + 1);
389         else if (field == 0 && !show_index)
390             sprintf(string_buf, "%%s");
391         else
392             sprintf(string_buf, "%s%%s", field_separator);
393         column[field].fmt = strdup(string_buf);
394     }
395     if (show_index) {
396         if (column_width)
397             sprintf(string_buf, "\nindex: %%s\n");
398         else if (field_separator == NULL)
399             sprintf(string_buf, "%%%s%ds", left_justify_flag, index_width);
400         else
401             sprintf(string_buf, "%%s");
402         index_fmt = strdup(string_buf);
403     }
404 
405     while (last_field != fields) {
406         part++;
407         if (part != 1 && !no_headers)
408             printf("\nSNMP table %s, part %d\n\n", table_name, part);
409         first_field = last_field;
410         dp = data;
411         if (show_index && !no_headers && !column_width && first_pass) {
412             width = index_width;
413             printf(index_fmt, "index");
414         } else
415             width = 0;
416         for (field = first_field; field < fields; field++) {
417             if (max_width)
418             {
419                 if (column_width) {
420                     if (!no_headers && first_pass) {
421                         width += column_width + 1;
422                         if (field != first_field && width > max_width) {
423                             printf("\n");
424                             width = column_width + 1;
425                         }
426                     }
427                 }
428                 else {
429                     width += column[field].width + 1;
430                     if (field != first_field && width > max_width)
431                         break;
432                 }
433             }
434             if (!no_headers && first_pass)
435                 printf(column[field].fmt, column[field].label);
436         }
437         last_field = field;
438         if (!no_headers && first_pass)
439             printf("\n");
440         for (entry = 0; entry < entries; entry++) {
441             width = 0;
442             if (show_index)
443             {
444                 if (!column_width)
445                     width = index_width;
446                 printf(index_fmt, indices[entry]);
447             }
448             for (field = first_field; field < last_field; field++) {
449                 if (column_width && max_width) {
450                     width += column_width + 1;
451                     if (field != first_field && width > max_width) {
452                         printf("\n");
453                         width = column_width + 1;
454                     }
455                 }
456                 printf(column[field].fmt, dp[field] ? dp[field] : "?");
457             }
458             dp += fields;
459             printf("\n");
460         }
461     }
462 
463     first_pass = 0;
464     if (index_fmt)
465         free(index_fmt);
466 }
467 
468 void
get_field_names(void)469 get_field_names(void)
470 {
471     char           *buf = NULL, *name_p = NULL;
472     size_t          buf_len = 0, out_len = 0;
473 #ifndef NETSNMP_DISABLE_MIB_LOADING
474     struct tree    *tbl = NULL;
475 #endif /* NETSNMP_DISABLE_MIB_LOADING */
476     int             going = 1;
477 
478 #ifndef NETSNMP_DISABLE_MIB_LOADING
479     tbl = get_tree(root, rootlen, get_tree_head());
480     if (tbl) {
481         tbl = tbl->child_list;
482         if (tbl) {
483             root[rootlen++] = tbl->subid;
484             tbl = tbl->child_list;
485         } else {
486             root[rootlen++] = 1;
487             going = 0;
488         }
489     }
490 #endif /* NETSNMP_DISABLE_MIB_LOADING */
491 
492     if (sprint_realloc_objid
493         ((u_char **)&buf, &buf_len, &out_len, 1, root, rootlen - 1)) {
494         table_name = buf;
495         buf = NULL;
496         buf_len = out_len = 0;
497     } else {
498         table_name = strdup("[TRUNCATED]");
499         out_len = 0;
500     }
501 
502     fields = 0;
503     while (going) {
504         fields++;
505 #ifndef NETSNMP_DISABLE_MIB_LOADING
506         if (tbl) {
507             if (tbl->access == MIB_ACCESS_NOACCESS) {
508                 fields--;
509                 tbl = tbl->next_peer;
510                 if (!tbl) {
511                     going = 0;
512                 }
513                 continue;
514             }
515             root[rootlen] = tbl->subid;
516             tbl = tbl->next_peer;
517             if (!tbl)
518                 going = 0;
519         } else {
520 #endif /* NETSNMP_DISABLE_MIB_LOADING */
521             root[rootlen] = fields;
522 #ifndef NETSNMP_DISABLE_MIB_LOADING
523         }
524 #endif /* NETSNMP_DISABLE_MIB_LOADING */
525         out_len = 0;
526         if (sprint_realloc_objid
527             ((u_char **)&buf, &buf_len, &out_len, 1, root, rootlen + 1)) {
528             name_p = strrchr(buf, '.');
529             if (name_p == NULL) {
530                 name_p = strrchr(buf, ':');
531             }
532             if (name_p == NULL) {
533                 name_p = buf;
534             } else {
535                 name_p++;
536             }
537         } else {
538             break;
539         }
540         if (localdebug) {
541             printf("%s %c\n", buf, name_p[0]);
542         }
543         if ('0' <= name_p[0] && name_p[0] <= '9') {
544             fields--;
545             break;
546         }
547         if (fields == 1) {
548             column = (struct column *) malloc(sizeof(*column));
549         } else {
550             column =
551                 (struct column *) realloc(column,
552                                           fields * sizeof(*column));
553         }
554         column[fields - 1].label = strdup(name_p);
555         column[fields - 1].width = strlen(name_p);
556         column[fields - 1].subid = root[rootlen];
557     }
558     if (fields == 0) {
559         fprintf(stderr, "Was that a table? %s\n", table_name);
560         exit(1);
561     }
562     if (name_p) {
563         *name_p = 0;
564         memmove(name, root, rootlen * sizeof(oid));
565         name_length = rootlen + 1;
566         name_p = strrchr(buf, '.');
567         if (name_p == NULL) {
568             name_p = strrchr(buf, ':');
569         }
570         if (name_p != NULL) {
571             *name_p = 0;
572         }
573     }
574     if (brief && fields > 1) {
575         char           *f1, *f2;
576         int             common = strlen(column[0].label);
577         int             field, len;
578         for (field = 1; field < fields; field++) {
579             f1 = column[field - 1].label;
580             f2 = column[field].label;
581             while (*f1 && *f1++ == *f2++);
582             len = f2 - column[field].label - 1;
583             if (len < common)
584                 common = len;
585         }
586         if (common) {
587             for (field = 0; field < fields; field++) {
588                 column[field].label += common;
589                 column[field].width -= common;
590             }
591         }
592     }
593     if (buf != NULL) {
594         free(buf);
595     }
596 }
597 
598 void
get_table_entries(netsnmp_session * ss)599 get_table_entries(netsnmp_session * ss)
600 {
601     int             running = 1;
602     netsnmp_pdu    *pdu, *response;
603     netsnmp_variable_list *vars;
604     int             count;
605     int             status;
606     int             i;
607     int             col;
608     char           *buf = NULL;
609     size_t          out_len = 0, buf_len = 0;
610     char           *cp;
611     char           *name_p = NULL;
612     char          **dp;
613     int             have_current_index;
614 
615     /*
616      * TODO:
617      *   1) Deal with multiple index fields
618      *   2) Deal with variable length index fields
619      *   3) optimize to remove a sparse column from get-requests
620      */
621 
622     while (running &&
623            ((max_width && !column_width) || (entries < max_getbulk))) {
624         /*
625          * create PDU for GETNEXT request and add object name to request
626          */
627         pdu = snmp_pdu_create(SNMP_MSG_GETNEXT);
628         for (i = 1; i <= fields; i++) {
629             name[rootlen] = column[i - 1].subid;
630             snmp_add_null_var(pdu, name, name_length);
631         }
632 
633         /*
634          * do the request
635          */
636         status = snmp_synch_response(ss, pdu, &response);
637         if (status == STAT_SUCCESS) {
638             if (response->errstat == SNMP_ERR_NOERROR) {
639                 /*
640                  * check resulting variables
641                  */
642                 vars = response->variables;
643                 entries++;
644                 if (entries >= allocated) {
645                     if (allocated == 0) {
646                         allocated = 10;
647                         data =
648                             (char **) malloc(allocated * fields *
649                                              sizeof(char *));
650                         memset(data, 0,
651                                allocated * fields * sizeof(char *));
652                         if (show_index)
653                             indices =
654                                 (char **) malloc(allocated *
655                                                  sizeof(char *));
656                     } else {
657                         allocated += 10;
658                         data =
659                             (char **) realloc(data,
660                                               allocated * fields *
661                                               sizeof(char *));
662                         memset(data + entries * fields, 0,
663                                (allocated -
664                                 entries) * fields * sizeof(char *));
665                         if (show_index)
666                             indices =
667                                 (char **) realloc(indices,
668                                                   allocated *
669                                                   sizeof(char *));
670                     }
671                 }
672                 dp = data + (entries - 1) * fields;
673                 col = -1;
674                 end_of_table = 1;       /* assume end of table */
675                 have_current_index = 0;
676                 name_length = rootlen + 1;
677                 for (vars = response->variables; vars;
678                      vars = vars->next_variable) {
679                     col++;
680                     name[rootlen] = column[col].subid;
681                     if ((vars->name_length < name_length) ||
682                         (vars->name[rootlen] != column[col].subid) ||
683                         memcmp(name, vars->name,
684                                name_length * sizeof(oid)) != 0
685                         || vars->type == SNMP_ENDOFMIBVIEW) {
686                         /*
687                          * not part of this subtree
688                          */
689                         if (localdebug) {
690                             fprint_variable(stderr, vars->name,
691                                             vars->name_length, vars);
692                             fprintf(stderr, " => ignored\n");
693                         }
694                         continue;
695                     }
696 
697                     /*
698                      * save index off
699                      */
700                     if (!have_current_index) {
701                         end_of_table = 0;
702                         have_current_index = 1;
703                         name_length = vars->name_length;
704                         memcpy(name, vars->name,
705                                name_length * sizeof(oid));
706                         out_len = 0;
707                         if (!sprint_realloc_objid
708                             ((u_char **)&buf, &buf_len, &out_len, 1, vars->name,
709                              vars->name_length)) {
710                             break;
711                         }
712                         if (localdebug || show_index) {
713                             if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
714                                               NETSNMP_DS_LIB_EXTENDED_INDEX)) {
715                                 name_p = strchr(buf, '[');
716                             } else {
717                                 switch (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
718                                                           NETSNMP_DS_LIB_OID_OUTPUT_FORMAT)) {
719                                 case NETSNMP_OID_OUTPUT_MODULE:
720 				case 0:
721                                     name_p = strchr(buf, ':');
722                                     break;
723                                 case NETSNMP_OID_OUTPUT_SUFFIX:
724                                     name_p = buf;
725                                     break;
726                                 case NETSNMP_OID_OUTPUT_FULL:
727                                 case NETSNMP_OID_OUTPUT_NUMERIC:
728                                 case NETSNMP_OID_OUTPUT_UCD:
729                                     name_p = buf + strlen(table_name)+1;
730                                     name_p = strchr(name_p, '.')+1;
731                                     break;
732 				default:
733 				    fprintf(stderr, "Unrecognized -O option: %d\n",
734 					    netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
735 							      NETSNMP_DS_LIB_OID_OUTPUT_FORMAT));
736 				    exit(1);
737                                 }
738                                 name_p = strchr(name_p, '.') + 1;
739                             }
740                         }
741                         if (localdebug) {
742                             printf("Name: %s Index: %s\n", buf, name_p);
743                         }
744                         if (show_index) {
745                             indices[entries - 1] = strdup(name_p);
746                             i = strlen(name_p);
747                             if (i > index_width)
748                                 index_width = i;
749                         }
750                     }
751 
752                     if (localdebug && buf) {
753                         printf("%s => taken\n", buf);
754                     }
755                     if (dp[col]) {
756                         fprintf(stderr, "OID not increasing: %s\n", buf);
757                         running = 0;
758                         vars = NULL;
759                         end_of_table = 1;
760                         exitval = 2;
761                         break;
762                     }
763                     out_len = 0;
764                     sprint_realloc_value((u_char **)&buf, &buf_len, &out_len, 1,
765                                          vars->name, vars->name_length,
766                                          vars);
767                     for (cp = buf; *cp; cp++) {
768                         if (*cp == '\n') {
769                             *cp = ' ';
770                         }
771                     }
772                     dp[col] = buf;
773                     i = out_len;
774                     buf = NULL;
775                     buf_len = 0;
776                     if (i > column[col].width) {
777                         column[col].width = i;
778                     }
779                 }
780                 if (buf) {
781                     free(buf);
782                     buf = NULL;
783                     buf_len = 0;
784                 }
785 
786                 if (end_of_table) {
787                     --entries;
788                     /*
789                      * not part of this subtree
790                      */
791                     if (localdebug) {
792                         printf("End of table\n");
793                     }
794                     snmp_free_pdu(response);
795                     running = 0;
796                     continue;
797                 }
798             } else {
799                 /*
800                  * error in response, print it
801                  */
802                 running = 0;
803                 if (response->errstat == SNMP_ERR_NOSUCHNAME) {
804                     printf("End of MIB\n");
805                     end_of_table = 1;
806                 } else {
807                     fprintf(stderr, "Error in packet.\nReason: %s\n",
808                             snmp_errstring(response->errstat));
809                     if (response->errindex != 0) {
810                         fprintf(stderr, "Failed object: ");
811                         for (count = 1, vars = response->variables;
812                              vars && count != response->errindex;
813                              vars = vars->next_variable, count++)
814                             /*EMPTY*/;
815                         if (vars) {
816                             fprint_objid(stderr, vars->name,
817                                          vars->name_length);
818                         }
819                         fprintf(stderr, "\n");
820                     }
821                     exitval = 2;
822                 }
823             }
824         } else if (status == STAT_TIMEOUT) {
825             fprintf(stderr, "Timeout: No Response from %s\n",
826                     ss->peername);
827             running = 0;
828             exitval = 1;
829         } else {                /* status == STAT_ERROR */
830             snmp_sess_perror("snmptable", ss);
831             running = 0;
832             exitval = 1;
833         }
834         if (response)
835             snmp_free_pdu(response);
836     }
837 }
838 
839 void
getbulk_table_entries(netsnmp_session * ss)840 getbulk_table_entries(netsnmp_session * ss)
841 {
842     int             running = 1;
843     netsnmp_pdu    *pdu, *response;
844     netsnmp_variable_list *vars, *last_var;
845     int             count;
846     int             status;
847     int             i;
848     int             row, col;
849     char           *buf = NULL;
850     size_t          buf_len = 0, out_len = 0;
851     char           *cp;
852     char           *name_p = NULL;
853     char          **dp;
854 
855     while (running) {
856         /*
857          * create PDU for GETBULK request and add object name to request
858          */
859         pdu = snmp_pdu_create(SNMP_MSG_GETBULK);
860         pdu->non_repeaters = 0;
861         pdu->max_repetitions = max_getbulk;
862         snmp_add_null_var(pdu, name, name_length);
863 
864         /*
865          * do the request
866          */
867         status = snmp_synch_response(ss, pdu, &response);
868         if (status == STAT_SUCCESS) {
869             if (response->errstat == SNMP_ERR_NOERROR) {
870                 /*
871                  * check resulting variables
872                  */
873                 vars = response->variables;
874                 last_var = NULL;
875                 while (vars) {
876                     out_len = 0;
877                     sprint_realloc_objid((u_char **)&buf, &buf_len, &out_len, 1,
878                                          vars->name, vars->name_length);
879                     if (vars->type == SNMP_ENDOFMIBVIEW ||
880                         memcmp(vars->name, name,
881                                rootlen * sizeof(oid)) != 0) {
882                         if (localdebug) {
883                             printf("%s => end of table\n",
884                                    buf ? (char *) buf : "[NIL]");
885                         }
886                         running = 0;
887                         break;
888                     }
889                     if (localdebug) {
890                         printf("%s => taken\n",
891                                buf ? (char *) buf : "[NIL]");
892                     }
893                     for (col = 0; col < fields; col++)
894                         if (column[col].subid == vars->name[rootlen])
895                             break;
896 		    if (col == fields) {
897 			extra_columns = 1;
898 			last_var = vars;
899 			vars = vars->next_variable;
900 			continue;
901 		    }
902                     if (netsnmp_ds_get_boolean(NETSNMP_DS_LIBRARY_ID,
903                                               NETSNMP_DS_LIB_EXTENDED_INDEX)) {
904                         name_p = strchr(buf, '[');
905                         if (name_p == NULL) {
906                             running = 0;
907                             break;
908                         }
909                     } else {
910                         switch (netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
911                                                   NETSNMP_DS_LIB_OID_OUTPUT_FORMAT)) {
912                         case NETSNMP_OID_OUTPUT_MODULE:
913 			case 0:
914                             name_p = strchr(buf, ':')+1;
915                             break;
916                         case NETSNMP_OID_OUTPUT_SUFFIX:
917                             name_p = buf;
918                             break;
919                         case NETSNMP_OID_OUTPUT_FULL:
920                         case NETSNMP_OID_OUTPUT_NUMERIC:
921                         case NETSNMP_OID_OUTPUT_UCD:
922                             name_p = buf + strlen(table_name)+1;
923                             name_p = strchr(name_p, '.')+1;
924                             break;
925 			default:
926 			    fprintf(stderr, "Unrecognized -O option: %d\n",
927 				    netsnmp_ds_get_int(NETSNMP_DS_LIBRARY_ID,
928 					              NETSNMP_DS_LIB_OID_OUTPUT_FORMAT));
929 			    exit(1);
930                         }
931                         name_p = strchr(name_p, '.');
932                         if ( name_p == NULL ) {
933                             /* The 'strchr' call above failed, i.e. the results
934                              * don't seem to include instance subidentifiers! */
935                             running = 0;
936                             break;
937                         }
938                         name_p++;  /* Move on to the instance identifier */
939                     }
940                     for (row = 0; row < entries; row++)
941                         if (strcmp(name_p, indices[row]) == 0)
942                             break;
943                     if (row == entries) {
944                         entries++;
945                         if (entries >= allocated) {
946                             if (allocated == 0) {
947                                 allocated = 10;
948                                 data =
949                                     (char **) malloc(allocated * fields *
950                                                      sizeof(char *));
951                                 memset(data, 0,
952                                        allocated * fields *
953                                        sizeof(char *));
954                                 indices =
955                                     (char **) malloc(allocated *
956                                                      sizeof(char *));
957                             } else {
958                                 allocated += 10;
959                                 data =
960                                     (char **) realloc(data,
961                                                       allocated * fields *
962                                                       sizeof(char *));
963                                 memset(data + entries * fields, 0,
964                                        (allocated -
965                                         entries) * fields *
966                                        sizeof(char *));
967                                 indices =
968                                     (char **) realloc(indices,
969                                                       allocated *
970                                                       sizeof(char *));
971                             }
972                         }
973                         indices[row] = strdup(name_p);
974                         i = strlen(name_p);
975                         if (i > index_width)
976                             index_width = i;
977                     }
978                     dp = data + row * fields;
979                     if (dp[col]) {
980                         fprintf(stderr, "OID not increasing: %s\n", buf);
981                         exitval = 2;
982                         end_of_table = 1;
983                         running = 0;
984                         break;
985                     }
986                     out_len = 0;
987                     sprint_realloc_value((u_char **)&buf, &buf_len, &out_len, 1,
988                                          vars->name, vars->name_length,
989                                          vars);
990                     for (cp = buf; *cp; cp++)
991                         if (*cp == '\n')
992                             *cp = ' ';
993                     dp[col] = buf;
994                     i = out_len;
995                     buf = NULL;
996                     buf_len = 0;
997                     if (i > column[col].width)
998                         column[col].width = i;
999                     last_var = vars;
1000                     vars = vars->next_variable;
1001                 }
1002                 if (last_var) {
1003                     name_length = last_var->name_length;
1004                     memcpy(name, last_var->name,
1005                            name_length * sizeof(oid));
1006                 }
1007                 if (buf) {
1008                     free(buf);
1009                     buf = NULL;
1010                     buf_len = 0;
1011                 }
1012             } else {
1013                 /*
1014                  * error in response, print it
1015                  */
1016                 running = 0;
1017                 if (response->errstat == SNMP_ERR_NOSUCHNAME) {
1018                     printf("End of MIB\n");
1019                 } else {
1020                     fprintf(stderr, "Error in packet.\nReason: %s\n",
1021                             snmp_errstring(response->errstat));
1022                     if (response->errstat == SNMP_ERR_NOSUCHNAME) {
1023                         fprintf(stderr,
1024                                 "The request for this object identifier failed: ");
1025                         for (count = 1, vars = response->variables;
1026                              vars && count != response->errindex;
1027                              vars = vars->next_variable, count++)
1028                             /*EMPTY*/;
1029                         if (vars) {
1030                             fprint_objid(stderr, vars->name,
1031                                          vars->name_length);
1032                         }
1033                         fprintf(stderr, "\n");
1034                     }
1035                     exitval = 2;
1036                 }
1037             }
1038         } else if (status == STAT_TIMEOUT) {
1039             fprintf(stderr, "Timeout: No Response from %s\n",
1040                     ss->peername);
1041             running = 0;
1042             exitval = 1;
1043         } else {                /* status == STAT_ERROR */
1044             snmp_sess_perror("snmptable", ss);
1045             running = 0;
1046             exitval = 1;
1047         }
1048         if (response)
1049             snmp_free_pdu(response);
1050     }
1051 }
1052