Initial import from FreeBSD RELENG_4:
[dragonfly.git] / contrib / ntp / ntpq / ntpq_ops.c
1 /*
2  * ntpdc_ops.c - subroutines which are called to perform operations by ntpdc
3  */
4
5 #include <stdio.h>
6
7 #include "ntpq.h"
8 #include "ntp_stdlib.h"
9
10 #include <ctype.h>
11 #include <netdb.h>
12
13 extern char *   chosts[];
14 extern char currenthost[];
15 extern int      numhosts;
16 int     maxhostlen;
17
18 /*
19  * Declarations for command handlers in here
20  */
21 static  int checkassocid        P((u_int32));
22 static  char *  strsave         P((char *));
23 static  struct varlist *findlistvar P((struct varlist *, char *));
24 static  void    doaddvlist      P((struct varlist *, char *));
25 static  void    dormvlist       P((struct varlist *, char *));
26 static  void    doclearvlist    P((struct varlist *));
27 static  void    makequerydata   P((struct varlist *, int *, char *));
28 static  int doquerylist P((struct varlist *, int, int, int, u_short *, int *, char **));
29 static  void    doprintvlist    P((struct varlist *, FILE *));
30 static  void    addvars         P((struct parse *, FILE *));
31 static  void    rmvars          P((struct parse *, FILE *));
32 static  void    clearvars       P((struct parse *, FILE *));
33 static  void    showvars        P((struct parse *, FILE *));
34 static  int dolist              P((struct varlist *, int, int, int, FILE *));
35 static  void    readlist        P((struct parse *, FILE *));
36 static  void    writelist       P((struct parse *, FILE *));
37 static  void    readvar         P((struct parse *, FILE *));
38 static  void    writevar        P((struct parse *, FILE *));
39 static  void    clocklist       P((struct parse *, FILE *));
40 static  void    clockvar        P((struct parse *, FILE *));
41 static  int findassidrange      P((u_int32, u_int32, int *, int *));
42 static  void    mreadlist       P((struct parse *, FILE *));
43 static  void    mreadvar        P((struct parse *, FILE *));
44 static  int dogetassoc  P((FILE *));
45 static  void    printassoc      P((int, FILE *));
46 static  void    associations    P((struct parse *, FILE *));
47 static  void    lassociations   P((struct parse *, FILE *));
48 static  void    passociations   P((struct parse *, FILE *));
49 static  void    lpassociations  P((struct parse *, FILE *));
50
51 #ifdef  UNUSED
52 static  void    radiostatus P((struct parse *, FILE *));
53 #endif  /* UNUSED */
54
55 static  void    pstatus         P((struct parse *, FILE *));
56 static  long    when            P((l_fp *, l_fp *, l_fp *));
57 static  char *  prettyinterval  P((char *, long));
58 static  int doprintpeers        P((struct varlist *, int, int, int, char *, FILE *));
59 static  int dogetpeers  P((struct varlist *, int, FILE *));
60 static  void    dopeers         P((int, FILE *));
61 static  void    peers           P((struct parse *, FILE *));
62 static  void    lpeers          P((struct parse *, FILE *));
63 static  void    doopeers        P((int, FILE *));
64 static  void    opeers          P((struct parse *, FILE *));
65 static  void    lopeers         P((struct parse *, FILE *));
66
67
68 /*
69  * Commands we understand.      Ntpdc imports this.
70  */
71 struct xcmd opcmds[] = {
72         { "associations", associations, {  NO, NO, NO, NO },
73           { "", "", "", "" },
74           "print list of association ID's and statuses for the server's peers" },
75         { "passociations", passociations,   {  NO, NO, NO, NO },
76           { "", "", "", "" },
77           "print list of associations returned by last associations command" },
78         { "lassociations", lassociations,   {  NO, NO, NO, NO },
79           { "", "", "", "" },
80           "print list of associations including all client information" },
81         { "lpassociations", lpassociations, {  NO, NO, NO, NO },
82           { "", "", "", "" },
83           "print last obtained list of associations, including client information" },
84         { "addvars",    addvars,    { STR, NO, NO, NO },
85           { "name[=value][,...]", "", "", "" },
86           "add variables to the variable list or change their values" },
87         { "rmvars", rmvars,     { STR, NO, NO, NO },
88           { "name[,...]", "", "", "" },
89           "remove variables from the variable list" },
90         { "clearvars",  clearvars,  { NO, NO, NO, NO },
91           { "", "", "", "" },
92           "remove all variables from the variable list" },
93         { "showvars",   showvars,   { NO, NO, NO, NO },
94           { "", "", "", "" },
95           "print variables on the variable list" },
96         { "readlist",   readlist,   { OPT|UINT, NO, NO, NO },
97           { "assocID", "", "", "" },
98           "read the system or peer variables included in the variable list" },
99         { "rl",     readlist,   { OPT|UINT, NO, NO, NO },
100           { "assocID", "", "", "" },
101           "read the system or peer variables included in the variable list" },
102         { "writelist",  writelist,  { OPT|UINT, NO, NO, NO },
103           { "assocID", "", "", "" },
104           "write the system or peer variables included in the variable list" },
105         { "readvar",    readvar,    { OPT|UINT, OPT|STR, NO, NO },
106           { "assocID", "name=value[,...]", "", "" },
107           "read system or peer variables" },
108         { "rv",     readvar,    { OPT|UINT, OPT|STR, NO, NO },
109           { "assocID", "name=value[,...]", "", "" },
110           "read system or peer variables" },
111         { "writevar",   writevar,   { UINT, STR, NO, NO },
112           { "assocID", "name=value,[...]", "", "" },
113           "write system or peer variables" },
114         { "mreadlist",  mreadlist,  { UINT, UINT, NO, NO },
115           { "assocID", "assocID", "", "" },
116           "read the peer variables in the variable list for multiple peers" },
117         { "mrl",    mreadlist,  { UINT, UINT, NO, NO },
118           { "assocID", "assocID", "", "" },
119           "read the peer variables in the variable list for multiple peers" },
120         { "mreadvar",   mreadvar,   { UINT, UINT, OPT|STR, NO },
121           { "assocID", "assocID", "name=value[,...]", "" },
122           "read peer variables from multiple peers" },
123         { "mrv",    mreadvar,   { UINT, UINT, OPT|STR, NO },
124           { "assocID", "assocID", "name=value[,...]", "" },
125           "read peer variables from multiple peers" },
126         { "clocklist",  clocklist,  { OPT|UINT, NO, NO, NO },
127           { "assocID", "", "", "" },
128           "read the clock variables included in the variable list" },
129         { "cl",     clocklist,  { OPT|UINT, NO, NO, NO },
130           { "assocID", "", "", "" },
131           "read the clock variables included in the variable list" },
132         { "clockvar",   clockvar,   { OPT|UINT, OPT|STR, NO, NO },
133           { "assocID", "name=value[,...]", "", "" },
134           "read clock variables" },
135         { "cv",     clockvar,   { OPT|UINT, OPT|STR, NO, NO },
136           { "assocID", "name=value[,...]", "", "" },
137           "read clock variables" },
138         { "pstatus",    pstatus,    { UINT, NO, NO, NO },
139           { "assocID", "", "", "" },
140           "print status information returned for a peer" },
141         { "peers",  peers,      { NO, NO, NO, NO },
142           { "", "", "", "" },
143           "obtain and print a list of the server's peers" },
144         { "lpeers", lpeers,     { NO, NO, NO, NO },
145           { "", "", "", "" },
146           "obtain and print a list of all peers and clients" },
147         { "opeers", opeers,     { NO, NO, NO, NO },
148           { "", "", "", "" },
149           "print peer list the old way, with dstadr shown rather than refid" },
150         { "lopeers",    lopeers,    { NO, NO, NO, NO },
151           { "", "", "", "" },
152           "obtain and print a list of all peers and clients showing dstadr" },
153         { 0,            0,              { NO, NO, NO, NO },
154           { "", "", "", "" }, "" }
155 };
156
157
158 /*
159  * Variable list data space
160  */
161 #define MAXLIST         64      /* maximum number of variables in list */
162 #define LENHOSTNAME 256 /* host name is 256 characters long */
163 /*
164  * Old CTL_PST defines for version 2.
165  */
166 #define OLD_CTL_PST_CONFIG                      0x80
167 #define OLD_CTL_PST_AUTHENABLE          0x40
168 #define OLD_CTL_PST_AUTHENTIC           0x20
169 #define OLD_CTL_PST_REACH                       0x10
170 #define OLD_CTL_PST_SANE                        0x08
171 #define OLD_CTL_PST_DISP                        0x04
172 #define OLD_CTL_PST_SEL_REJECT          0
173 #define OLD_CTL_PST_SEL_SELCAND         1
174 #define OLD_CTL_PST_SEL_SYNCCAND        2
175 #define OLD_CTL_PST_SEL_SYSPEER         3
176
177
178 char flash2[] = " .+*    "; /* flash decode for version 2 */
179 char flash3[] = " x.-+#*o"; /* flash decode for peer status version 3 */
180
181 struct varlist {
182         char *name;
183         char *value;
184 } varlist[MAXLIST] = { { 0, 0 } };
185
186 /*
187  * Imported from ntpq.c
188  */
189 extern int showhostnames;
190 extern int rawmode;
191 extern struct servent *server_entry;
192 extern struct association assoc_cache[];
193 extern int numassoc;
194 extern u_char pktversion;
195 extern struct ctl_var peer_var[];
196
197 /*
198  * For quick string comparisons
199  */
200 #define STREQ(a, b) (*(a) == *(b) && strcmp((a), (b)) == 0)
201
202
203 /*
204  * checkassocid - return the association ID, checking to see if it is valid
205  */
206 static int
207 checkassocid(
208         u_int32 value
209         )
210 {
211         if (value == 0 || value >= 65536) {
212                 (void) fprintf(stderr, "***Invalid association ID specified\n");
213                 return 0;
214         }
215         return (int)value;
216 }
217
218
219 /*
220  * strsave - save a string
221  * XXX - should be in libntp.a
222  */
223 static char *
224 strsave(
225         char *str
226         )
227 {
228         char *cp;
229         u_int len;
230
231         len = strlen(str) + 1;
232         if ((cp = (char *)malloc(len)) == NULL) {
233                 (void) fprintf(stderr, "Malloc failed!!\n");
234                 exit(1);
235         }
236
237         memmove(cp, str, len);
238         return (cp);
239 }
240
241
242 /*
243  * findlistvar - look for the named variable in a list and return if found
244  */
245 static struct varlist *
246 findlistvar(
247         struct varlist *list,
248         char *name
249         )
250 {
251         register struct varlist *vl;
252
253         for (vl = list; vl < list + MAXLIST && vl->name != 0; vl++)
254                 if (STREQ(name, vl->name))
255                 return vl;
256         if (vl < list + MAXLIST)
257                 return vl;
258         return (struct varlist *)0;
259 }
260
261
262 /*
263  * doaddvlist - add variable(s) to the variable list
264  */
265 static void
266 doaddvlist(
267         struct varlist *vlist,
268         char *vars
269         )
270 {
271         register struct varlist *vl;
272         int len;
273         char *name;
274         char *value;
275
276         len = strlen(vars);
277         while (nextvar(&len, &vars, &name, &value)) {
278                 vl = findlistvar(vlist, name);
279                 if (vl == 0) {
280                         (void) fprintf(stderr, "Variable list full\n");
281                         return;
282                 }
283
284                 if (vl->name == 0) {
285                         vl->name = strsave(name);
286                 } else if (vl->value != 0) {
287                         free(vl->value);
288                         vl->value = 0;
289                 }
290
291                 if (value != 0)
292                         vl->value = strsave(value);
293         }
294 }
295
296
297 /*
298  * dormvlist - remove variable(s) from the variable list
299  */
300 static void
301 dormvlist(
302         struct varlist *vlist,
303         char *vars
304         )
305 {
306         register struct varlist *vl;
307         int len;
308         char *name;
309         char *value;
310
311         len = strlen(vars);
312         while (nextvar(&len, &vars, &name, &value)) {
313                 vl = findlistvar(vlist, name);
314                 if (vl == 0 || vl->name == 0) {
315                         (void) fprintf(stderr, "Variable `%s' not found\n",
316                                        name);
317                 } else {
318                         free((void *)vl->name);
319                         if (vl->value != 0)
320                             free(vl->value);
321                         for ( ; (vl+1) < (varlist+MAXLIST)
322                                       && (vl+1)->name != 0; vl++) {
323                                 vl->name = (vl+1)->name;
324                                 vl->value = (vl+1)->value;
325                         }
326                         vl->name = vl->value = 0;
327                 }
328         }
329 }
330
331
332 /*
333  * doclearvlist - clear a variable list
334  */
335 static void
336 doclearvlist(
337         struct varlist *vlist
338         )
339 {
340         register struct varlist *vl;
341
342         for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
343                 free((void *)vl->name);
344                 vl->name = 0;
345                 if (vl->value != 0) {
346                         free(vl->value);
347                         vl->value = 0;
348                 }
349         }
350 }
351
352
353 /*
354  * makequerydata - form a data buffer to be included with a query
355  */
356 static void
357 makequerydata(
358         struct varlist *vlist,
359         int *datalen,
360         char *data
361         )
362 {
363         register struct varlist *vl;
364         register char *cp, *cpend;
365         register int namelen, valuelen;
366         register int totallen;
367
368         cp = data;
369         cpend = data + *datalen;
370
371         for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
372                 namelen = strlen(vl->name);
373                 if (vl->value == 0)
374                         valuelen = 0;
375                 else
376                         valuelen = strlen(vl->value);
377                 totallen = namelen + valuelen + (valuelen != 0) + (cp != data);
378                 if (cp + totallen > cpend)
379                         break;
380
381                 if (cp != data)
382                         *cp++ = ',';
383                 memmove(cp, vl->name, (unsigned)namelen);
384                 cp += namelen;
385                 if (valuelen != 0) {
386                         *cp++ = '=';
387                         memmove(cp, vl->value, (unsigned)valuelen);
388                         cp += valuelen;
389                 }
390         }
391         *datalen = cp - data;
392 }
393
394
395 /*
396  * doquerylist - send a message including variables in a list
397  */
398 static int
399 doquerylist(
400         struct varlist *vlist,
401         int op,
402         int associd,
403         int auth,
404         u_short *rstatus,
405         int *dsize,
406         char **datap
407         )
408 {
409         char data[CTL_MAX_DATA_LEN];
410         int datalen;
411
412         datalen = sizeof(data);
413         makequerydata(vlist, &datalen, data);
414
415         return doquery(op, associd, auth, datalen, data, rstatus,
416                            dsize, datap);
417 }
418
419
420 /*
421  * doprintvlist - print the variables on a list
422  */
423 static void
424 doprintvlist(
425         struct varlist *vlist,
426         FILE *fp
427         )
428 {
429         register struct varlist *vl;
430
431         if (vlist->name == 0) {
432                 (void) fprintf(fp, "No variables on list\n");
433         } else {
434                 for (vl = vlist; vl < vlist + MAXLIST && vl->name != 0; vl++) {
435                         if (vl->value == 0) {
436                                 (void) fprintf(fp, "%s\n", vl->name);
437                         } else {
438                                 (void) fprintf(fp, "%s=%s\n",
439                                                    vl->name, vl->value);
440                         }
441                 }
442         }
443 }
444
445
446 /*
447  * addvars - add variables to the variable list
448  */
449 /*ARGSUSED*/
450 static void
451 addvars(
452         struct parse *pcmd,
453         FILE *fp
454         )
455 {
456         doaddvlist(varlist, pcmd->argval[0].string);
457 }
458
459
460 /*
461  * rmvars - remove variables from the variable list
462  */
463 /*ARGSUSED*/
464 static void
465 rmvars(
466         struct parse *pcmd,
467         FILE *fp
468         )
469 {
470         dormvlist(varlist, pcmd->argval[0].string);
471 }
472
473
474 /*
475  * clearvars - clear the variable list
476  */
477 /*ARGSUSED*/
478 static void
479 clearvars(
480         struct parse *pcmd,
481         FILE *fp
482         )
483 {
484         doclearvlist(varlist);
485 }
486
487
488 /*
489  * showvars - show variables on the variable list
490  */
491 /*ARGSUSED*/
492 static void
493 showvars(
494         struct parse *pcmd,
495         FILE *fp
496         )
497 {
498         doprintvlist(varlist, fp);
499 }
500
501
502 /*
503  * dolist - send a request with the given list of variables
504  */
505 static int
506 dolist(
507         struct varlist *vlist,
508         int associd,
509         int op,
510         int type,
511         FILE *fp
512         )
513 {
514         char *datap;
515         int res;
516         int dsize;
517         u_short rstatus;
518
519         res = doquerylist(vlist, op, associd, 0, &rstatus, &dsize, &datap);
520
521         if (res != 0)
522                 return 0;
523
524         if (dsize == 0) {
525                 if (associd == 0)
526                         (void) fprintf(fp, "No system%s variables returned\n",
527                                    (type == TYPE_CLOCK) ? " clock" : "");
528                 else
529                         (void) fprintf(fp,
530                                    "No information returned for%s association %u\n",
531                                    (type == TYPE_CLOCK) ? " clock" : "", associd);
532                 return 1;
533         }
534
535         printvars(dsize, datap, (int)rstatus, type, fp);
536         return 1;
537 }
538
539
540 /*
541  * readlist - send a read variables request with the variables on the list
542  */
543 static void
544 readlist(
545         struct parse *pcmd,
546         FILE *fp
547         )
548 {
549         int associd;
550
551         if (pcmd->nargs == 0) {
552                 associd = 0;
553         } else {
554           /* HMS: I think we want the u_int32 target here, not the u_long */
555                 if (pcmd->argval[0].uval == 0)
556                         associd = 0;
557                 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
558                         return;
559         }
560
561         (void) dolist(varlist, associd, CTL_OP_READVAR,
562                           (associd == 0) ? TYPE_SYS : TYPE_PEER, fp);
563 }
564
565
566 /*
567  * writelist - send a write variables request with the variables on the list
568  */
569 static void
570 writelist(
571         struct parse *pcmd,
572         FILE *fp
573         )
574 {
575         char *datap;
576         int res;
577         int associd;
578         int dsize;
579         u_short rstatus;
580
581         if (pcmd->nargs == 0) {
582                 associd = 0;
583         } else {
584                 /* HMS: Do we really want uval here? */
585                 if (pcmd->argval[0].uval == 0)
586                         associd = 0;
587                 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
588                         return;
589         }
590
591         res = doquerylist(varlist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
592                           &dsize, &datap);
593
594         if (res != 0)
595                 return;
596
597         if (dsize == 0)
598                 (void) fprintf(fp, "done! (no data returned)\n");
599         else
600                 printvars(dsize, datap, (int)rstatus,
601                           (associd != 0) ? TYPE_PEER : TYPE_SYS, fp);
602         return;
603 }
604
605
606 /*
607  * readvar - send a read variables request with the specified variables
608  */
609 static void
610 readvar(
611         struct parse *pcmd,
612         FILE *fp
613         )
614 {
615         int associd;
616         struct varlist tmplist[MAXLIST];
617
618         /* HMS: uval? */
619         if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
620                 associd = 0;
621         else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
622                 return;
623
624         memset((char *)tmplist, 0, sizeof(tmplist));
625         if (pcmd->nargs >= 2)
626                 doaddvlist(tmplist, pcmd->argval[1].string);
627
628         (void) dolist(tmplist, associd, CTL_OP_READVAR,
629                           (associd == 0) ? TYPE_SYS : TYPE_PEER, fp);
630
631         doclearvlist(tmplist);
632 }
633
634
635 /*
636  * writevar - send a write variables request with the specified variables
637  */
638 static void
639 writevar(
640         struct parse *pcmd,
641         FILE *fp
642         )
643 {
644         char *datap;
645         int res;
646         int associd;
647         int dsize;
648         u_short rstatus;
649         struct varlist tmplist[MAXLIST];
650
651         /* HMS: uval? */
652         if (pcmd->argval[0].uval == 0)
653                 associd = 0;
654         else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
655                 return;
656
657         memset((char *)tmplist, 0, sizeof(tmplist));
658         doaddvlist(tmplist, pcmd->argval[1].string);
659
660         res = doquerylist(tmplist, CTL_OP_WRITEVAR, associd, 1, &rstatus,
661                           &dsize, &datap);
662
663         doclearvlist(tmplist);
664
665         if (res != 0)
666                 return;
667
668         if (dsize == 0)
669                 (void) fprintf(fp, "done! (no data returned)\n");
670         else
671                 printvars(dsize, datap, (int)rstatus,
672                           (associd != 0) ? TYPE_PEER : TYPE_SYS, fp);
673         return;
674 }
675
676
677 /*
678  * clocklist - send a clock variables request with the variables on the list
679  */
680 static void
681 clocklist(
682         struct parse *pcmd,
683         FILE *fp
684         )
685 {
686         int associd;
687
688         /* HMS: uval? */
689         if (pcmd->nargs == 0) {
690                 associd = 0;
691         } else {
692                 if (pcmd->argval[0].uval == 0)
693                         associd = 0;
694                 else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
695                         return;
696         }
697
698         (void) dolist(varlist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
699 }
700
701
702 /*
703  * clockvar - send a clock variables request with the specified variables
704  */
705 static void
706 clockvar(
707         struct parse *pcmd,
708         FILE *fp
709         )
710 {
711         int associd;
712         struct varlist tmplist[MAXLIST];
713
714         /* HMS: uval? */
715         if (pcmd->nargs == 0 || pcmd->argval[0].uval == 0)
716                 associd = 0;
717         else if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
718                 return;
719
720         memset((char *)tmplist, 0, sizeof(tmplist));
721         if (pcmd->nargs >= 2)
722                 doaddvlist(tmplist, pcmd->argval[1].string);
723
724         (void) dolist(tmplist, associd, CTL_OP_READCLOCK, TYPE_CLOCK, fp);
725
726         doclearvlist(tmplist);
727 }
728
729
730 /*
731  * findassidrange - verify a range of association ID's
732  */
733 static int
734 findassidrange(
735         u_int32 assid1,
736         u_int32 assid2,
737         int *from,
738         int *to
739         )
740 {
741         register int i;
742         int f, t;
743
744         if (assid1 == 0 || assid1 > 65535) {
745                 (void) fprintf(stderr,
746                                    "***Invalid association ID %lu specified\n", (u_long)assid1);
747                 return 0;
748         }
749
750         if (assid2 == 0 || assid2 > 65535) {
751                 (void) fprintf(stderr,
752                                    "***Invalid association ID %lu specified\n", (u_long)assid2);
753                 return 0;
754         }
755
756         f = t = -1;
757         for (i = 0; i < numassoc; i++) {
758                 if (assoc_cache[i].assid == assid1) {
759                         f = i;
760                         if (t != -1)
761                                 break;
762                 }
763                 if (assoc_cache[i].assid == assid2) {
764                         t = i;
765                         if (f != -1)
766                                 break;
767                 }
768         }
769
770         if (f == -1 || t == -1) {
771                 (void) fprintf(stderr,
772                                    "***Association ID %lu not found in list\n",
773                                    (f == -1) ? (u_long)assid1 : (u_long)assid2);
774                 return 0;
775         }
776
777         if (f < t) {
778                 *from = f;
779                 *to = t;
780         } else {
781                 *from = t;
782                 *to = f;
783         }
784         return 1;
785 }
786
787
788
789 /*
790  * mreadlist - send a read variables request for multiple associations
791  */
792 static void
793 mreadlist(
794         struct parse *pcmd,
795         FILE *fp
796         )
797 {
798         int i;
799         int from;
800         int to;
801
802         /* HMS: uval? */
803         if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
804                                 &from, &to))
805                 return;
806
807         for (i = from; i <= to; i++) {
808                 if (i != from)
809                         (void) fprintf(fp, "\n");
810                 if (!dolist(varlist, (int)assoc_cache[i].assid,
811                                 CTL_OP_READVAR, TYPE_PEER, fp))
812                         return;
813         }
814         return;
815 }
816
817
818 /*
819  * mreadvar - send a read variables request for multiple associations
820  */
821 static void
822 mreadvar(
823         struct parse *pcmd,
824         FILE *fp
825         )
826 {
827         int i;
828         int from;
829         int to;
830         struct varlist tmplist[MAXLIST];
831
832         /* HMS: uval? */
833         if (!findassidrange(pcmd->argval[0].uval, pcmd->argval[1].uval,
834                                 &from, &to))
835                 return;
836
837         memset((char *)tmplist, 0, sizeof(tmplist));
838         if (pcmd->nargs >= 3)
839                 doaddvlist(tmplist, pcmd->argval[2].string);
840
841         for (i = from; i <= to; i++) {
842                 if (i != from)
843                         (void) fprintf(fp, "\n");
844                 if (!dolist(varlist, (int)assoc_cache[i].assid,
845                                 CTL_OP_READVAR, TYPE_PEER, fp))
846                         break;
847         }
848         doclearvlist(tmplist);
849         return;
850 }
851
852
853 /*
854  * dogetassoc - query the host for its list of associations
855  */
856 static int
857 dogetassoc(
858         FILE *fp
859         )
860 {
861         u_short *datap;
862         int res;
863         int dsize;
864         u_short rstatus;
865
866         res = doquery(CTL_OP_READSTAT, 0, 0, 0, (char *)0, &rstatus,
867                           &dsize, (char **)&datap);
868
869         if (res != 0)
870                 return 0;
871
872         if (dsize == 0) {
873                 (void) fprintf(fp, "No association ID's returned\n");
874                 return 0;
875         }
876
877         if (dsize & 0x3) {
878                 (void) fprintf(stderr,
879                                    "***Server returned %d octets, should be multiple of 4\n",
880                                    dsize);
881                 return 0;
882         }
883
884         numassoc = 0;
885         while (dsize > 0) {
886                 assoc_cache[numassoc].assid = ntohs(*datap);
887                 datap++;
888                 assoc_cache[numassoc].status = ntohs(*datap);
889                 datap++;
890                 if (++numassoc >= MAXASSOC)
891                         break;
892                 dsize -= sizeof(u_short) + sizeof(u_short);
893         }
894         sortassoc();
895         return 1;
896 }
897
898
899 /*
900  * printassoc - print the current list of associations
901  */
902 static void
903 printassoc(
904         int showall,
905         FILE *fp
906         )
907 {
908         register char *bp;
909         int i;
910         u_char statval;
911         int event;
912         u_long event_count;
913         const char *conf;
914         const char *reach;
915         const char *auth;
916         const char *condition = "";
917         const char *last_event;
918         const char *cnt;
919         char buf[128];
920
921         if (numassoc == 0) {
922                 (void) fprintf(fp, "No association ID's in list\n");
923                 return;
924         }
925
926         /*
927          * Output a header
928          */
929         (void) fprintf(fp,
930                            "ind assID status  conf reach auth condition  last_event cnt\n");
931         (void) fprintf(fp,
932                            "===========================================================\n");
933         for (i = 0; i < numassoc; i++) {
934                 statval = CTL_PEER_STATVAL(assoc_cache[i].status);
935                 if (!showall && !(statval & (CTL_PST_CONFIG|CTL_PST_REACH)))
936                         continue;
937                 event = CTL_PEER_EVENT(assoc_cache[i].status);
938                 event_count = CTL_PEER_NEVNT(assoc_cache[i].status);
939                 if (statval & CTL_PST_CONFIG)
940                         conf = "yes";
941                 else
942                         conf = "no";
943                 if (statval & CTL_PST_REACH || 1) {
944                         reach = "yes";
945                         if (statval & CTL_PST_AUTHENABLE) {
946                                 if (statval & CTL_PST_AUTHENTIC)
947                                         auth = "ok ";
948                                 else
949                                         auth = "bad";
950                         } else
951                                 auth = "none";
952
953                         if (pktversion > NTP_OLDVERSION)
954                                 switch (statval & 0x7) {
955                                 case CTL_PST_SEL_REJECT:
956                                         condition = "reject";
957                                         break;
958                                 case CTL_PST_SEL_SANE:
959                                         condition = "falsetick";
960                                         break;
961                                 case CTL_PST_SEL_CORRECT:
962                                         condition = "excess";
963                                         break;
964                                 case CTL_PST_SEL_SELCAND:
965                                         condition = "outlyer";
966                                         break;
967                                 case CTL_PST_SEL_SYNCCAND:
968                                         condition = "candidat";
969                                         break;
970                                 case CTL_PST_SEL_DISTSYSPEER:
971                                         condition = "selected";
972                                         break;
973                                 case CTL_PST_SEL_SYSPEER:
974                                         condition = "sys.peer";
975                                         break;
976                                 case CTL_PST_SEL_PPS:
977                                         condition = "pps.peer";
978                                         break;
979                                 }
980                         else
981                                 switch (statval & 0x3) {
982                                 case OLD_CTL_PST_SEL_REJECT:
983                                         if (!(statval & OLD_CTL_PST_SANE))
984                                         condition = "insane";
985                                         else if (!(statval & OLD_CTL_PST_DISP))
986                                         condition = "hi_disp";
987                                         else
988                                         condition = "";
989                                         break;
990                                 case OLD_CTL_PST_SEL_SELCAND:
991                                         condition = "sel_cand";
992                                         break;
993                                 case OLD_CTL_PST_SEL_SYNCCAND:
994                                         condition = "sync_cand";
995                                         break;
996                                 case OLD_CTL_PST_SEL_SYSPEER:
997                                         condition = "sys_peer";
998                                         break;
999                                 }
1000
1001                 } else {
1002                         reach = "no";
1003                         auth = condition = "";
1004                 }
1005
1006                 switch (PEER_EVENT|event) {
1007                         case EVNT_PEERIPERR:
1008                         last_event = "IP error";
1009                         break;
1010                         case EVNT_PEERAUTH:
1011                         last_event = "auth fail";
1012                         break;
1013                         case EVNT_UNREACH:
1014                         last_event = "lost reach";
1015                         break;
1016                         case EVNT_REACH:
1017                         last_event = "reachable";
1018                         break;
1019                         case EVNT_PEERCLOCK:
1020                         last_event = "clock expt";
1021                         break;
1022 #if 0
1023                         case EVNT_PEERSTRAT:
1024                         last_event = "stratum chg";
1025                         break;
1026 #endif
1027                         default:
1028                         last_event = "";
1029                         break;
1030                 }
1031
1032                 if (event_count != 0)
1033                         cnt = uinttoa(event_count);
1034                 else
1035                         cnt = "";
1036                 (void) sprintf(buf,
1037                                    "%3d %5u  %04x   %3.3s  %4s  %4.4s %9.9s %11s %2s",
1038                                    i+1, assoc_cache[i].assid, assoc_cache[i].status,
1039                                    conf, reach, auth, condition, last_event, cnt);
1040                 bp = &buf[strlen(buf)];
1041                 while (bp > buf && *(bp-1) == ' ')
1042                         *(--bp) = '\0';
1043                 (void) fprintf(fp, "%s\n", buf);
1044         }
1045 }
1046
1047
1048
1049 /*
1050  * associations - get, record and print a list of associations
1051  */
1052 /*ARGSUSED*/
1053 static void
1054 associations(
1055         struct parse *pcmd,
1056         FILE *fp
1057         )
1058 {
1059         if (dogetassoc(fp))
1060                 printassoc(0, fp);
1061 }
1062
1063
1064 /*
1065  * lassociations - get, record and print a long list of associations
1066  */
1067 /*ARGSUSED*/
1068 static void
1069 lassociations(
1070         struct parse *pcmd,
1071         FILE *fp
1072         )
1073 {
1074         if (dogetassoc(fp))
1075                 printassoc(1, fp);
1076 }
1077
1078
1079 /*
1080  * passociations - print the association list
1081  */
1082 /*ARGSUSED*/
1083 static void
1084 passociations(
1085         struct parse *pcmd,
1086         FILE *fp
1087         )
1088 {
1089         printassoc(0, fp);
1090 }
1091
1092
1093 /*
1094  * lpassociations - print the long association list
1095  */
1096 /*ARGSUSED*/
1097 static void
1098 lpassociations(
1099         struct parse *pcmd,
1100         FILE *fp
1101         )
1102 {
1103         printassoc(1, fp);
1104 }
1105
1106
1107 #ifdef  UNUSED
1108 /*
1109  * radiostatus - print the radio status returned by the server
1110  */
1111 /*ARGSUSED*/
1112 static void
1113 radiostatus(
1114         struct parse *pcmd,
1115         FILE *fp
1116         )
1117 {
1118         char *datap;
1119         int res;
1120         int dsize;
1121         u_short rstatus;
1122
1123         res = doquery(CTL_OP_READCLOCK, 0, 0, 0, (char *)0, &rstatus,
1124                           &dsize, &datap);
1125
1126         if (res != 0)
1127                 return;
1128
1129         if (dsize == 0) {
1130                 (void) fprintf(fp, "No radio status string returned\n");
1131                 return;
1132         }
1133
1134         asciize(dsize, datap, fp);
1135 }
1136 #endif  /* UNUSED */
1137
1138 /*
1139  * pstatus - print peer status returned by the server
1140  */
1141 static void
1142 pstatus(
1143         struct parse *pcmd,
1144         FILE *fp
1145         )
1146 {
1147         char *datap;
1148         int res;
1149         int associd;
1150         int dsize;
1151         u_short rstatus;
1152
1153         /* HMS: uval? */
1154         if ((associd = checkassocid(pcmd->argval[0].uval)) == 0)
1155                 return;
1156
1157         res = doquery(CTL_OP_READSTAT, associd, 0, 0, (char *)0, &rstatus,
1158                           &dsize, &datap);
1159
1160         if (res != 0)
1161                 return;
1162
1163         if (dsize == 0) {
1164                 (void) fprintf(fp,
1165                                    "No information returned for association %u\n",
1166                                    associd);
1167                 return;
1168         }
1169
1170         printvars(dsize, datap, (int)rstatus, TYPE_PEER, fp);
1171 }
1172
1173
1174 /*
1175  * when - print how long its been since his last packet arrived
1176  */
1177 static long
1178 when(
1179         l_fp *ts,
1180         l_fp *rec,
1181         l_fp *reftime
1182         )
1183 {
1184         l_fp *lasttime;
1185
1186         if (rec->l_ui != 0)
1187                 lasttime = rec;
1188         else if (reftime->l_ui != 0)
1189                 lasttime = reftime;
1190         else
1191                 return 0;
1192
1193         return (ts->l_ui - lasttime->l_ui);
1194 }
1195
1196
1197 /*
1198  * Pretty-print an interval into the given buffer, in a human-friendly format.
1199  */
1200 static char *
1201 prettyinterval(
1202         char *buf,
1203         long diff
1204         )
1205 {
1206         if (diff <= 0) {
1207                 buf[0] = '-';
1208                 buf[1] = 0;
1209                 return buf;
1210         }
1211
1212         if (diff <= 2048) {
1213                 (void) sprintf(buf, "%ld", (long int)diff);
1214                 return buf;
1215         }
1216
1217         diff = (diff + 29) / 60;
1218         if (diff <= 300) {
1219                 (void) sprintf(buf, "%ldm", (long int)diff);
1220                 return buf;
1221         }
1222
1223         diff = (diff + 29) / 60;
1224         if (diff <= 96) {
1225                 (void) sprintf(buf, "%ldh", (long int)diff);
1226                 return buf;
1227         }
1228
1229         diff = (diff + 11) / 24;
1230         (void) sprintf(buf, "%ldd", (long int)diff);
1231         return buf;
1232 }
1233
1234
1235 /*
1236  * A list of variables required by the peers command
1237  */
1238 struct varlist opeervarlist[] = {
1239         { "srcadr", 0 },    /* 0 */
1240         { "dstadr", 0 },    /* 1 */
1241         { "stratum",    0 },    /* 2 */
1242         { "hpoll",  0 },    /* 3 */
1243         { "ppoll",  0 },    /* 4 */
1244         { "reach",  0 },    /* 5 */
1245         { "delay",  0 },    /* 6 */
1246         { "offset", 0 },    /* 7 */
1247         { "jitter", 0 },    /* 8 */
1248         { "dispersion", 0 },    /* 9 */
1249         { "rec",    0 },    /* 10 */
1250         { "reftime",    0 },    /* 11 */
1251         { "srcport",    0 },    /* 12 */
1252         { 0,            0 }
1253 };
1254
1255 struct varlist peervarlist[] = {
1256         { "srcadr", 0 },    /* 0 */
1257         { "refid",  0 },    /* 1 */
1258         { "stratum",    0 },    /* 2 */
1259         { "hpoll",  0 },    /* 3 */
1260         { "ppoll",  0 },    /* 4 */
1261         { "reach",  0 },    /* 5 */
1262         { "delay",  0 },    /* 6 */
1263         { "offset", 0 },    /* 7 */
1264         { "jitter", 0 },    /* 8 */
1265         { "dispersion", 0 },    /* 9 */
1266         { "rec",    0 },    /* 10 */
1267         { "reftime",    0 },    /* 11 */
1268         { "srcport",    0 },    /* 12 */
1269         { 0,            0 }
1270 };
1271
1272 #define HAVE_SRCADR 0
1273 #define HAVE_DSTADR 1
1274 #define HAVE_REFID      1
1275 #define HAVE_STRATUM    2
1276 #define HAVE_HPOLL      3
1277 #define HAVE_PPOLL      4
1278 #define HAVE_REACH      5
1279 #define HAVE_DELAY      6
1280 #define HAVE_OFFSET 7
1281 #define HAVE_JITTER 8
1282 #define HAVE_DISPERSION 9
1283 #define HAVE_REC        10
1284 #define HAVE_REFTIME    11
1285 #define HAVE_SRCPORT    12
1286 #define MAXHAVE         13
1287
1288 /*
1289  * Decode an incoming data buffer and print a line in the peer list
1290  */
1291 static int
1292 doprintpeers(
1293         struct varlist *pvl,
1294         int associd,
1295         int rstatus,
1296         int datalen,
1297         char *data,
1298         FILE *fp
1299         )
1300 {
1301         char *name;
1302         char *value;
1303         int i;
1304         int c;
1305
1306         u_int32 srcadr;
1307         u_int32 dstadr;
1308         u_long srcport;
1309         const char *dstadr_refid = "0.0.0.0";
1310         u_long stratum;
1311         long ppoll;
1312         long hpoll;
1313         u_long reach;
1314         l_fp estoffset;
1315         l_fp estdelay;
1316         l_fp estjitter;
1317         l_fp estdisp;
1318         l_fp reftime;
1319         l_fp rec;
1320         l_fp ts;
1321         u_char havevar[MAXHAVE];
1322         u_long poll_sec;
1323         char type = '?';
1324         char refid_string[10];
1325         char whenbuf[8], pollbuf[8];
1326
1327         memset((char *)havevar, 0, sizeof(havevar));
1328         get_systime(&ts);
1329
1330         while (nextvar(&datalen, &data, &name, &value)) {
1331                 u_int32 dummy;
1332
1333                 i = findvar(name, peer_var);
1334                 if (i == 0)
1335                         continue;       /* don't know this one */
1336                 switch (i) {
1337                         case CP_SRCADR:
1338                         if (decodenetnum(value, &srcadr))
1339                                 havevar[HAVE_SRCADR] = 1;
1340                         break;
1341                         case CP_DSTADR:
1342                         if (decodenetnum(value, &dummy)) {
1343                                 dummy = ntohl(dummy);
1344                                 type = ((dummy&0xf0000000)==0xe0000000) ? 'm' :
1345                                         ((dummy&0x000000ff)==0x000000ff) ? 'b' :
1346                                         ((dummy&0xffffffff)==0x7f000001) ? 'l' :
1347                                         ((dummy&0xffffffe0)==0x00000000) ? '-' :
1348                                         'u';
1349                         }
1350                         if (pvl == opeervarlist) {
1351                                 if (decodenetnum(value, &dstadr)) {
1352                                         havevar[HAVE_DSTADR] = 1;
1353                                         dstadr_refid = numtoa(dstadr);
1354                                 }
1355                         }
1356                         break;
1357                         case CP_REFID:
1358                         if (pvl == peervarlist) {
1359                                 havevar[HAVE_REFID] = 1;
1360                                 if (*value == '\0') {
1361                                         dstadr_refid = "0.0.0.0";
1362                                 } else if (decodenetnum(value, &dstadr)) {
1363                                         if (dstadr == 0)
1364                                                 dstadr_refid = "0.0.0.0";
1365                                         else
1366                                                 dstadr_refid = nntohost(dstadr);
1367                                 } else if ((int)strlen(value) <= 4) {
1368                                         refid_string[0] = '.';
1369                                         (void) strcpy(&refid_string[1], value);
1370                                         i = strlen(refid_string);
1371                                         refid_string[i] = '.';
1372                                         refid_string[i+1] = '\0';
1373                                         dstadr_refid = refid_string;
1374                                 } else {
1375                                         havevar[HAVE_REFID] = 0;
1376                                 }
1377                         }
1378                         break;
1379                         case CP_STRATUM:
1380                         if (decodeuint(value, &stratum))
1381                                 havevar[HAVE_STRATUM] = 1;
1382                         break;
1383                         case CP_HPOLL:
1384                         if (decodeint(value, &hpoll)) {
1385                                 havevar[HAVE_HPOLL] = 1;
1386                                 if (hpoll < 0)
1387                                         hpoll = NTP_MINPOLL;
1388                         }
1389                         break;
1390                         case CP_PPOLL:
1391                         if (decodeint(value, &ppoll)) {
1392                                 havevar[HAVE_PPOLL] = 1;
1393                                 if (ppoll < 0)
1394                                         ppoll = NTP_MINPOLL;
1395                         }
1396                         break;
1397                         case CP_REACH:
1398                         if (decodeuint(value, &reach))
1399                                 havevar[HAVE_REACH] = 1;
1400                         break;
1401                         case CP_DELAY:
1402                         if (decodetime(value, &estdelay))
1403                                 havevar[HAVE_DELAY] = 1;
1404                         break;
1405                         case CP_OFFSET:
1406                         if (decodetime(value, &estoffset))
1407                                 havevar[HAVE_OFFSET] = 1;
1408                         break;
1409                         case CP_JITTER:
1410                         if (decodetime(value, &estjitter))
1411                                 havevar[HAVE_JITTER] = 1;
1412                         break;
1413                         case CP_DISPERSION:
1414                         if (decodetime(value, &estdisp))
1415                                 havevar[HAVE_DISPERSION] = 1;
1416                         break;
1417                         case CP_REC:
1418                         if (decodets(value, &rec))
1419                                 havevar[HAVE_REC] = 1;
1420                         break;
1421                         case CP_SRCPORT:
1422                         if (decodeuint(value, &srcport))
1423                                 havevar[HAVE_SRCPORT] = 1;
1424                         break;
1425                         case CP_REFTIME:
1426                         havevar[HAVE_REFTIME] = 1;
1427                         if (!decodets(value, &reftime))
1428                                 L_CLR(&reftime);
1429                         break;
1430                         default:
1431                         break;
1432                 }
1433         }
1434
1435         /*
1436          * Check to see if the srcport is NTP's port.  If not this probably
1437          * isn't a valid peer association.
1438          */
1439         if (havevar[HAVE_SRCPORT] && srcport != NTP_PORT)
1440                 return (1);
1441
1442         /*
1443          * Got everything, format the line
1444          */
1445         poll_sec = 1<<max(min3(ppoll, hpoll, NTP_MAXPOLL), NTP_MINPOLL);
1446         if (pktversion > NTP_OLDVERSION)
1447                 c = flash3[CTL_PEER_STATVAL(rstatus) & 0x7];
1448         else
1449                 c = flash2[CTL_PEER_STATVAL(rstatus) & 0x3];
1450         if (numhosts > 1)
1451                 (void) fprintf(fp, "%-*s ", maxhostlen, currenthost);
1452         (void) fprintf(fp,
1453                 "%c%-15.15s %-15.15s %2ld %c %4.4s %4.4s  %3lo  %7.7s %8.7s %7.7s\n",
1454                 c, nntohost(srcadr), dstadr_refid, stratum, type,
1455                 prettyinterval(whenbuf, when(&ts, &rec, &reftime)),
1456                 prettyinterval(pollbuf, (int)poll_sec), reach,
1457                 lfptoms(&estdelay, 3), lfptoms(&estoffset, 3),
1458                 havevar[HAVE_JITTER] ? lfptoms(&estjitter, 3) :
1459                 lfptoms(&estdisp, 3));
1460         return (1);
1461 }
1462
1463 #undef  HAVE_SRCADR
1464 #undef  HAVE_DSTADR
1465 #undef  HAVE_STRATUM
1466 #undef  HAVE_PPOLL
1467 #undef  HAVE_HPOLL
1468 #undef  HAVE_REACH
1469 #undef  HAVE_ESTDELAY
1470 #undef  HAVE_ESTOFFSET
1471 #undef  HAVE_JITTER
1472 #undef  HAVE_ESTDISP
1473 #undef  HAVE_REFID
1474 #undef  HAVE_REC
1475 #undef  HAVE_SRCPORT
1476 #undef  HAVE_REFTIME
1477 #undef  MAXHAVE
1478
1479
1480 /*
1481  * dogetpeers - given an association ID, read and print the spreadsheet
1482  *              peer variables.
1483  */
1484 static int
1485 dogetpeers(
1486         struct varlist *pvl,
1487         int associd,
1488         FILE *fp
1489         )
1490 {
1491         char *datap;
1492         int res;
1493         int dsize;
1494         u_short rstatus;
1495
1496 #ifdef notdef
1497         res = doquerylist(pvl, CTL_OP_READVAR, associd, 0, &rstatus,
1498                           &dsize, &datap);
1499 #else
1500         /*
1501          * Damn fuzzballs
1502          */
1503         res = doquery(CTL_OP_READVAR, associd, 0, 0, (char *)0, &rstatus,
1504                           &dsize, &datap);
1505 #endif
1506
1507         if (res != 0)
1508                 return 0;
1509
1510         if (dsize == 0) {
1511                 (void) fprintf(stderr,
1512                                    "***No information returned for association %d\n",
1513                                    associd);
1514                 return 0;
1515         }
1516
1517
1518         return doprintpeers(pvl, associd, (int)rstatus, dsize, datap, fp);
1519 }
1520
1521
1522 /*
1523  * peers - print a peer spreadsheet
1524  */
1525 static void
1526 dopeers(
1527         int showall,
1528         FILE *fp
1529         )
1530 {
1531         register int i;
1532         char fullname[LENHOSTNAME];
1533         u_int32 netnum;
1534
1535         if (!dogetassoc(fp))
1536                 return;
1537
1538         maxhostlen = 0;
1539         if (numhosts > 1) {
1540                 for (i = 0; i < numhosts; ++i)
1541                 { if(getnetnum(chosts[i],&netnum,fullname))
1542                         if ((int)strlen(fullname) > maxhostlen)
1543                         maxhostlen = strlen(fullname);
1544                 }
1545                 (void) fprintf(fp, "%-*.*s ", maxhostlen, maxhostlen, "host");
1546         }
1547         fprintf(fp,
1548            "     remote           refid      st t when poll reach   delay   offset  jitter\n");
1549         if (numhosts > 1)
1550                 for (i = 0; i <= maxhostlen; ++i)
1551                 (void) fprintf(fp, "=");
1552         (void) fprintf(fp,
1553                            "==============================================================================\n");
1554
1555         for (i = 0; i < numassoc; i++) {
1556                 if (!showall &&
1557                         !(CTL_PEER_STATVAL(assoc_cache[i].status)
1558                           & (CTL_PST_CONFIG|CTL_PST_REACH)))
1559                         continue;
1560                 if (!dogetpeers(peervarlist, (int)assoc_cache[i].assid, fp)) {
1561                         return;
1562                 }
1563         }
1564         return;
1565 }
1566
1567
1568 /*
1569  * peers - print a peer spreadsheet
1570  */
1571 /*ARGSUSED*/
1572 static void
1573 peers(
1574         struct parse *pcmd,
1575         FILE *fp
1576         )
1577 {
1578         dopeers(0, fp);
1579 }
1580
1581
1582 /*
1583  * lpeers - print a peer spreadsheet including all fuzzball peers
1584  */
1585 /*ARGSUSED*/
1586 static void
1587 lpeers(
1588         struct parse *pcmd,
1589         FILE *fp
1590         )
1591 {
1592         dopeers(1, fp);
1593 }
1594
1595
1596 /*
1597  * opeers - print a peer spreadsheet
1598  */
1599 static void
1600 doopeers(
1601         int showall,
1602         FILE *fp
1603         )
1604 {
1605         register int i;
1606
1607         if (!dogetassoc(fp))
1608                 return;
1609
1610         (void) fprintf(fp,
1611                            "     remote           local      st t when poll reach   delay   offset    disp\n");
1612         (void) fprintf(fp,
1613                            "                                      (s)  (s)          (ms)     (ms)     (ms)\n");
1614         (void) fprintf(fp,
1615                            "==============================================================================\n");
1616
1617         for (i = 0; i < numassoc; i++) {
1618                 if (!showall &&
1619                         !(CTL_PEER_STATVAL(assoc_cache[i].status)
1620                           & (CTL_PST_CONFIG|CTL_PST_REACH)))
1621                         continue;
1622                 if (!dogetpeers(opeervarlist, (int)assoc_cache[i].assid, fp)) {
1623                         return;
1624                 }
1625         }
1626         return;
1627 }
1628
1629
1630 /*
1631  * opeers - print a peer spreadsheet the old way
1632  */
1633 /*ARGSUSED*/
1634 static void
1635 opeers(
1636         struct parse *pcmd,
1637         FILE *fp
1638         )
1639 {
1640         doopeers(0, fp);
1641 }
1642
1643
1644 /*
1645  * lopeers - print a peer spreadsheet including all fuzzball peers
1646  */
1647 /*ARGSUSED*/
1648 static void
1649 lopeers(
1650         struct parse *pcmd,
1651         FILE *fp
1652         )
1653 {
1654         doopeers(1, fp);
1655 }