rxdebug: Add rxgk support
[openafs.git] / src / rxdebug / rxdebug.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #include <afs/afsutil.h>
17 #include <afs/cmd.h>
18
19 #include <rx/rx_user.h>
20 #include <rx/rx_clock.h>
21 #include <rx/rx_queue.h>
22 #include <rx/rx.h>
23 #include <rx/rx_globals.h>
24
25 #ifdef ENABLE_RXGK
26 # include <rx/rxgk.h>
27 #endif
28
29
30 #define TIMEOUT     20
31
32 static short
33 PortNumber(char *aport)
34 {
35     int tc;
36     short total;
37
38     total = 0;
39     while ((tc = *aport++)) {
40         if (tc < '0' || tc > '9')
41             return -1;          /* bad port number */
42         total *= 10;
43         total += tc - (int)'0';
44     }
45     return htons(total);
46 }
47
48 static short
49 PortName(char *aname)
50 {
51     struct servent *ts;
52     ts = getservbyname(aname, NULL);
53     if (!ts)
54         return -1;
55     return ts->s_port;          /* returns it in network byte order */
56 }
57
58 int
59 MainCommand(struct cmd_syndesc *as, void *arock)
60 {
61     int i;
62     osi_socket s;
63     int j;
64     struct sockaddr_in taddr;
65     afs_int32 host;
66     struct in_addr hostAddr;
67     short port;
68     struct hostent *th;
69     afs_int32 code;
70     int nodally;
71     int allconns;
72     int rxstats;
73     int onlyClient, onlyServer;
74     afs_int32 onlyHost;
75     short onlyPort;
76     int onlyAuth;
77     int flag;
78     int dallyCounter;
79     int withSecStats;
80     int withAllConn;
81     int withRxStats;
82     int withWaiters;
83     int withIdleThreads;
84     int withWaited;
85     int withPeers;
86     int withPackets;
87     struct rx_debugStats tstats;
88     char *portName, *hostName;
89     char hoststr[20];
90     struct rx_debugConn tconn;
91     short noConns;
92     short showPeers;
93     short showLong;
94     int version_flag;
95     char version[64];
96     afs_int32 length = 64;
97
98     afs_uint32 supportedDebugValues = 0;
99     afs_uint32 supportedStatValues = 0;
100     afs_uint32 supportedConnValues = 0;
101     afs_uint32 supportedPeerValues = 0;
102     afs_int32 nextconn = 0;
103     afs_int32 nextpeer = 0;
104
105     nodally = (as->parms[2].items ? 1 : 0);
106     allconns = (as->parms[3].items ? 1 : 0);
107     rxstats = (as->parms[4].items ? 1 : 0);
108     onlyServer = (as->parms[5].items ? 1 : 0);
109     onlyClient = (as->parms[6].items ? 1 : 0);
110     version_flag = (as->parms[10].items ? 1 : 0);
111     noConns = (as->parms[11].items ? 1 : 0);
112     showPeers = (as->parms[12].items ? 1 : 0);
113     showLong = (as->parms[13].items ? 1 : 0);
114
115     if (as->parms[0].items)
116         hostName = as->parms[0].items->data;
117     else
118         hostName = NULL;
119
120     if (as->parms[1].items)
121         portName = as->parms[1].items->data;
122     else
123         portName = NULL;
124
125     if (as->parms[7].items) {
126         char *name = as->parms[7].items->data;
127         if ((onlyPort = PortNumber(name)) == -1)
128             onlyPort = PortName(name);
129         if (onlyPort == -1) {
130             printf("rxdebug: can't resolve port name %s\n", name);
131             exit(1);
132         }
133     } else
134         onlyPort = -1;
135
136     if (as->parms[8].items) {
137         char *name = as->parms[8].items->data;
138         struct hostent *th;
139         th = hostutil_GetHostByName(name);
140         if (!th) {
141             printf("rxdebug: host %s not found in host table\n", name);
142             exit(1);
143         }
144         memcpy(&onlyHost, th->h_addr, sizeof(afs_int32));
145     } else
146         onlyHost = -1;
147
148     if (as->parms[9].items) {
149         char *name = as->parms[9].items->data;
150         /* Note that this assumes that the security levels for rxkad and rxgk
151          * use the same constants (0, 1, and 2). Perhaps a little ugly, but the
152          * constants being identical makes it really convenient to do it this
153          * way. */
154         if (strcmp(name, "clear") == 0)
155             onlyAuth = 0;
156         else if (strcmp(name, "auth") == 0)
157             onlyAuth = 1;
158         else if (strcmp(name, "crypt") == 0)
159             onlyAuth = 2;
160         else if ((strcmp(name, "null") == 0) || (strcmp(name, "none") == 0)
161                  || (strncmp(name, "noauth", 6) == 0)
162                  || (strncmp(name, "unauth", 6) == 0))
163             onlyAuth = -1;
164         else {
165             fprintf(stderr, "Unknown authentication level: %s\n", name);
166             exit(1);
167         }
168     } else
169         onlyAuth = 999;
170
171     /* lookup host */
172     if (hostName) {
173         th = hostutil_GetHostByName(hostName);
174         if (!th) {
175             printf("rxdebug: host %s not found in host table\n", hostName);
176             exit(1);
177         }
178         memcpy(&host, th->h_addr, sizeof(afs_int32));
179     } else
180         host = htonl(0x7f000001);       /* IP localhost */
181
182     if (!portName)
183         port = htons(7000);     /* default is fileserver */
184     else {
185         if ((port = PortNumber(portName)) == -1)
186             port = PortName(portName);
187         if (port == -1) {
188             printf("rxdebug: can't resolve port name %s\n", portName);
189             exit(1);
190         }
191     }
192
193     dallyCounter = 0;
194
195     hostAddr.s_addr = host;
196     afs_inet_ntoa_r(hostAddr.s_addr, hoststr);
197     printf("Trying %s (port %d):\n", hoststr, ntohs(port));
198     s = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
199     if (s == OSI_NULLSOCKET) {
200 #ifdef AFS_NT40_ENV
201         fprintf(stderr, "socket() failed with error %u\n", WSAGetLastError());
202 #else
203         perror("socket");
204 #endif
205         exit(1);
206     }
207     taddr.sin_family = AF_INET;
208     taddr.sin_port = 0;
209     taddr.sin_addr.s_addr = 0;
210 #ifdef STRUCT_SOCKADDR_HAS_SA_LEN
211     taddr.sin_len = sizeof(struct sockaddr_in);
212 #endif
213     code = bind(s, (struct sockaddr *)&taddr, sizeof(struct sockaddr_in));
214     if (code) {
215 #ifdef AFS_NT40_ENV
216         fprintf(stderr, "bind() failed with error %u\n", WSAGetLastError());
217 #else
218         perror("bind");
219 #endif
220         exit(1);
221     }
222
223     if (version_flag) {
224         memset(version, 0, sizeof(version));
225
226         code = rx_GetServerVersion(s, host, port, length, version);
227         if (code < 0) {
228             printf("get version call failed with code %d, errno %d\n", code,
229                    errno);
230             exit(1);
231         }
232         version[sizeof(version) - 1] = '\0';
233         printf("AFS version: %s\n", version);
234         fflush(stdout);
235
236         exit(0);
237
238     }
239
240
241     code = rx_GetServerDebug(s, host, port, &tstats, &supportedDebugValues);
242     if (code < 0) {
243         printf("getstats call failed with code %d\n", code);
244         exit(1);
245     }
246
247     withSecStats = (supportedDebugValues & RX_SERVER_DEBUG_SEC_STATS);
248     withAllConn = (supportedDebugValues & RX_SERVER_DEBUG_ALL_CONN);
249     withRxStats = (supportedDebugValues & RX_SERVER_DEBUG_RX_STATS);
250     withWaiters = (supportedDebugValues & RX_SERVER_DEBUG_WAITER_CNT);
251     withIdleThreads = (supportedDebugValues & RX_SERVER_DEBUG_IDLE_THREADS);
252     withWaited = (supportedDebugValues & RX_SERVER_DEBUG_WAITED_CNT);
253     withPeers = (supportedDebugValues & RX_SERVER_DEBUG_ALL_PEER);
254     withPackets = (supportedDebugValues & RX_SERVER_DEBUG_PACKETS_CNT);
255
256     if (withPackets)
257         printf("Free packets: %d/%d, packet reclaims: %d, calls: %d, used FDs: %d\n",
258                tstats.nFreePackets, tstats.nPackets, tstats.packetReclaims,
259                tstats.callsExecuted, tstats.usedFDs);
260     else
261         printf("Free packets: %d, packet reclaims: %d, calls: %d, used FDs: %d\n",
262                tstats.nFreePackets, tstats.packetReclaims, tstats.callsExecuted,
263                tstats.usedFDs);
264     if (!tstats.waitingForPackets)
265         printf("not ");
266     printf("waiting for packets.\n");
267     if (withWaiters)
268         printf("%d calls waiting for a thread\n", tstats.nWaiting);
269     if (withIdleThreads)
270         printf("%d threads are idle\n", tstats.idleThreads);
271     if (withWaited)
272         printf("%d calls have waited for a thread\n", tstats.nWaited);
273
274     if (rxstats) {
275         if (!withRxStats) {
276           noRxStats:
277             withRxStats = 0;
278             fprintf(stderr,
279                     "WARNING: Server doesn't support retrieval of Rx statistics\n");
280         } else {
281             struct rx_statistics rxstats;
282
283             /* should gracefully handle the case where rx_stats grows */
284             code =
285                 rx_GetServerStats(s, host, port, &rxstats,
286                                   &supportedStatValues);
287             if (code < 0) {
288                 printf("rxstats call failed with code %d\n", code);
289                 exit(1);
290             }
291             if (code != sizeof(rxstats)) {
292                 struct rx_debugIn debug;
293                 memcpy(&debug, &rxstats, sizeof(debug));
294                 if (debug.type == RX_DEBUGI_BADTYPE)
295                     goto noRxStats;
296                 printf
297                     ("WARNING: returned Rx statistics of unexpected size (got %d)\n",
298                      code);
299                 /* handle other versions?... */
300             }
301
302             rx_PrintTheseStats(stdout, &rxstats, sizeof(rxstats),
303                                tstats.nFreePackets, tstats.version);
304         }
305     }
306
307     if (!noConns) {
308         if (allconns) {
309             if (!withAllConn)
310                 fprintf(stderr,
311                         "WARNING: Server doesn't support retrieval of all connections,\n         getting only interesting instead.\n");
312         }
313
314         if (onlyServer)
315             printf("Showing only server connections\n");
316         if (onlyClient)
317             printf("Showing only client connections\n");
318         if (onlyAuth != 999) {
319             static char *name[] =
320                 { "unauthenticated", "clear", "auth",
321                 "crypt"
322             };
323             printf("Showing only %s connections\n", name[onlyAuth + 1]);
324         }
325         if (onlyHost != -1) {
326             hostAddr.s_addr = onlyHost;
327             afs_inet_ntoa_r(hostAddr.s_addr, hoststr);
328             printf("Showing only connections from host %s\n",
329                    hoststr);
330         }
331         if (onlyPort != -1)
332             printf("Showing only connections on port %u\n", ntohs(onlyPort));
333
334         for (i = 0;; i++) {
335             code =
336                 rx_GetServerConnections(s, host, port, &nextconn, allconns,
337                                         supportedDebugValues, &tconn,
338                                         &supportedConnValues);
339             if (code < 0) {
340                 printf("getconn call failed with code %d\n", code);
341                 break;
342             }
343             if (tconn.cid == (afs_int32) 0xffffffff) {
344                 printf("Done.\n");
345                 break;
346             }
347
348             /* see if we're in nodally mode and all calls are dallying */
349             if (nodally) {
350                 flag = 0;
351                 for (j = 0; j < RX_MAXCALLS; j++) {
352                     if (tconn.callState[j] != RX_STATE_NOTINIT
353                         && tconn.callState[j] != RX_STATE_DALLY) {
354                         flag = 1;
355                         break;
356                     }
357                 }
358                 if (flag == 0) {
359                     /* this call looks too ordinary, bump skipped count and go
360                      * around again */
361                     dallyCounter++;
362                     continue;
363                 }
364             }
365             if ((onlyHost != -1) && (onlyHost != tconn.host))
366                 continue;
367             if ((onlyPort != -1) && (onlyPort != tconn.port))
368                 continue;
369             if (onlyServer && (tconn.type != RX_SERVER_CONNECTION))
370                 continue;
371             if (onlyClient && (tconn.type != RX_CLIENT_CONNECTION))
372                 continue;
373             if (onlyAuth != 999) {
374                 if (onlyAuth == -1) {
375                     if (tconn.securityIndex != RX_SECIDX_NULL)
376                         continue;
377                 } else {
378                     if (tconn.securityIndex != RX_SECIDX_KAD) {
379 #ifdef ENABLE_RXGK
380                         if (tconn.securityIndex != RX_SECIDX_GK)
381 #endif
382                             continue;
383                     }
384                     if (withSecStats && (tconn.secStats.type == RX_SECTYPE_KAD)
385                         && (tconn.secStats.level != onlyAuth))
386                         continue;
387 #ifdef ENABLE_RXGK
388                     if (withSecStats && (tconn.secStats.type == RX_SECTYPE_GK)
389                         && (tconn.secStats.level != onlyAuth))
390                         continue;
391 #endif
392                 }
393             }
394
395             /* now display the connection */
396             hostAddr.s_addr = tconn.host;
397             afs_inet_ntoa_r(hostAddr.s_addr, hoststr);
398             printf("Connection from host %s, port %hu, ", hoststr,
399                    ntohs(tconn.port));
400             if (tconn.epoch)
401                 printf("Cuid %x/%x", tconn.epoch, tconn.cid);
402             else
403                 printf("cid %x", tconn.cid);
404             if (tconn.error)
405                 printf(", error %d", tconn.error);
406             printf("\n  serial %d, ", tconn.serial);
407             printf(" natMTU %d, ", tconn.natMTU);
408
409             if (tconn.flags) {
410                 printf("flags");
411                 if (tconn.flags & RX_CONN_MAKECALL_WAITING)
412                     printf(" MAKECALL_WAITING");
413                 if (tconn.flags & RX_CONN_DESTROY_ME)
414                     printf(" DESTROYED");
415                 if (tconn.flags & RX_CONN_USING_PACKET_CKSUM)
416                     printf(" pktCksum");
417                 if (tconn.flags & RX_CONN_KNOW_WINDOW)
418                     printf(" knowWindow");
419                 if (tconn.flags & RX_CONN_RESET)
420                     printf(" reset");
421                 if (tconn.flags & RX_CONN_BUSY)
422                     printf(" busy");
423                 if (tconn.flags & RX_CONN_ATTACHWAIT)
424                     printf(" attachWait");
425                 printf(", ");
426             }
427             printf("security index %d, ", tconn.securityIndex);
428             if (tconn.type == RX_CLIENT_CONNECTION)
429                 printf("client conn\n");
430             else
431                 printf("server conn\n");
432
433             if (withSecStats) {
434                 switch ((int)tconn.secStats.type) {
435                 case RX_SECTYPE_UNK:
436                     if (tconn.securityIndex == RX_SECIDX_KAD)
437                         printf
438                             ("  no GetStats procedure for security object\n");
439                     break;
440                 case RX_SECTYPE_NULL:
441                     printf("  rxnull level=%d, flags=%d\n",
442                            tconn.secStats.level, tconn.secStats.flags);
443                     break;
444                 case RX_SECTYPE_VAB:
445                     printf("  rxvab level=%d, flags=%d\n",
446                            tconn.secStats.level, tconn.secStats.flags);
447                     break;
448                 case RX_SECTYPE_KAD:{
449                         char *level;
450                         char flags = tconn.secStats.flags;
451                         if (tconn.secStats.level == 0)
452                             level = "clear";
453                         else if (tconn.secStats.level == 1)
454                             level = "auth";
455                         else if (tconn.secStats.level == 2)
456                             level = "crypt";
457                         else
458                             level = "unknown";
459                         printf("  rxkad: level %s", level);
460                         if (flags)
461                             printf(", flags");
462                         if (flags & 1)
463                             printf(" unalloc");
464                         if (flags & 2)
465                             printf(" authenticated");
466                         if (flags & 4)
467                             printf(" expired");
468                         if (flags & 8)
469                             printf(" pktCksum");
470                         if (tconn.secStats.expires)
471                             /* Apparently due to a bug in the RT compiler that
472                              * prevents (afs_uint32)0xffffffff => (double) from working,
473                              * this code produces negative lifetimes when run on the
474                              * RT. */
475                             printf(", expires in %.1f hours",
476                                    ((afs_uint32) tconn.secStats.expires -
477                                     time(0)) / 3600.0);
478                         if (!(flags & 1)) {
479                             printf("\n  Received %u bytes in %u packets\n",
480                                    tconn.secStats.bytesReceived,
481                                    tconn.secStats.packetsReceived);
482                             printf("  Sent %u bytes in %u packets\n",
483                                    tconn.secStats.bytesSent,
484                                    tconn.secStats.packetsSent);
485                         } else
486                             printf("\n");
487                         break;
488                     }
489 #ifdef ENABLE_RXGK
490                 case RX_SECTYPE_GK: {
491                         char *level;
492                         char flags = tconn.secStats.flags;
493                         if (tconn.secStats.level == RXGK_LEVEL_CLEAR)
494                             level = "clear";
495                         else if (tconn.secStats.level == RXGK_LEVEL_AUTH)
496                             level = "auth";
497                         else if (tconn.secStats.level == RXGK_LEVEL_CRYPT)
498                             level = "crypt";
499                         else
500                             level = "unknown";
501                         printf("  rxgk: level %s", level);
502                         if (flags)
503                             printf(", flags");
504                         if ((flags & RXGK_STATS_UNALLOC))
505                             printf(" unalloc");
506                         if ((flags & RXGK_STATS_AUTH))
507                             printf(" authenticated");
508                         if (tconn.secStats.expires)
509                             printf(", expires in %.1f hours",
510                                    ((afs_uint32) tconn.secStats.expires -
511                                     time(0)) / 3600.0);
512                         if (!(flags & RXGK_STATS_UNALLOC)) {
513                             printf("\n  Received %u bytes in %u packets\n",
514                                    tconn.secStats.bytesReceived,
515                                    tconn.secStats.packetsReceived);
516                             printf("  Sent %u bytes in %u packets\n",
517                                    tconn.secStats.bytesSent,
518                                    tconn.secStats.packetsSent);
519                         } else
520                             printf("\n");
521                         break;
522                     }
523 #endif /* ENABLE_RXGK */
524
525                 default:
526                     printf("  unknown\n");
527                 }
528             }
529
530             for (j = 0; j < RX_MAXCALLS; j++) {
531                 printf("    call %d: # %d, state ", j, tconn.callNumber[j]);
532                 if (tconn.callState[j] == RX_STATE_NOTINIT) {
533                     printf("not initialized\n");
534                     continue;
535                 } else if (tconn.callState[j] == RX_STATE_PRECALL)
536                     printf("precall, ");
537                 else if (tconn.callState[j] == RX_STATE_ACTIVE)
538                     printf("active, ");
539                 else if (tconn.callState[j] == RX_STATE_DALLY)
540                     printf("dally, ");
541                 else if (tconn.callState[j] == RX_STATE_HOLD)
542                     printf("hold, ");
543                 else if (tconn.callState[j] == RX_STATE_RESET)
544                     printf("reset, ");
545                 printf("mode: ");
546                 if (tconn.callMode[j] == RX_MODE_SENDING)
547                     printf("sending");
548                 else if (tconn.callMode[j] == RX_MODE_RECEIVING)
549                     printf("receiving");
550                 else if (tconn.callMode[j] == RX_MODE_ERROR)
551                     printf("error");
552                 else if (tconn.callMode[j] == RX_MODE_EOF)
553                     printf("eof");
554                 else
555                     printf("unknown");
556                 if (tconn.callFlags[j]) {
557                     printf(", flags:");
558                     if (tconn.callFlags[j] & RX_CALL_READER_WAIT)
559                         printf(" reader_wait");
560                     if (tconn.callFlags[j] & RX_CALL_WAIT_WINDOW_ALLOC)
561                         printf(" window_alloc");
562                     if (tconn.callFlags[j] & RX_CALL_WAIT_WINDOW_SEND)
563                         printf(" window_send");
564                     if (tconn.callFlags[j] & RX_CALL_WAIT_PACKETS)
565                         printf(" wait_packets");
566                     if (tconn.callFlags[j] & RX_CALL_WAIT_PROC)
567                         printf(" waiting_for_process");
568                     if (tconn.callFlags[j] & RX_CALL_RECEIVE_DONE)
569                         printf(" receive_done");
570                     if (tconn.callFlags[j] & RX_CALL_CLEARED)
571                         printf(" call_cleared");
572                 }
573                 if (tconn.callOther[j] & RX_OTHER_IN)
574                     printf(", has_input_packets");
575                 if (tconn.callOther[j] & RX_OTHER_OUT)
576                     printf(", has_output_packets");
577                 printf("\n");
578             }
579         }
580         if (nodally)
581             printf("Skipped %d dallying connections.\n", dallyCounter);
582     }
583     if (showPeers && withPeers) {
584         for (i = 0;; i++) {
585             struct rx_debugPeer tpeer;
586             code =
587                 rx_GetServerPeers(s, host, port, &nextpeer, allconns, &tpeer,
588                                   &supportedPeerValues);
589             if (code < 0) {
590                 printf("getpeer call failed with code %d\n", code);
591                 break;
592             }
593             if (tpeer.host == 0xffffffff) {
594                 printf("Done.\n");
595                 break;
596             }
597
598             if ((onlyHost != -1) && (onlyHost != tpeer.host))
599                 continue;
600             if ((onlyPort != -1) && (onlyPort != tpeer.port))
601                 continue;
602
603             /* now display the peer */
604             hostAddr.s_addr = tpeer.host;
605             afs_inet_ntoa_r(hostAddr.s_addr, hoststr);
606             printf("Peer at host %s, port %hu\n", hoststr,
607                    ntohs(tpeer.port));
608             printf("\tifMTU %hu\tnatMTU %hu\tmaxMTU %hu\n", tpeer.ifMTU,
609                    tpeer.natMTU, tpeer.maxMTU);
610             printf("\tpackets sent %u\tpacket resends %u\n", tpeer.nSent,
611                    tpeer.reSends);
612             printf("\tbytes sent high %u low %u\n", tpeer.bytesSent.high,
613                    tpeer.bytesSent.low);
614             printf("\tbytes received high %u low %u\n",
615                    tpeer.bytesReceived.high, tpeer.bytesReceived.low);
616             printf("\trtt %u msec, rtt_dev %u msec\n", tpeer.rtt >> 3,
617                    tpeer.rtt_dev >> 2);
618             printf("\ttimeout %u.%03u sec\n", tpeer.timeout.sec,
619                    tpeer.timeout.usec / 1000);
620             if (!showLong)
621                 continue;
622
623             printf("\tin/out packet skew: %d/%d\n", tpeer.inPacketSkew,
624                     tpeer.outPacketSkew);
625             printf("\tcongestion window %d, MTU %d\n", tpeer.cwind,
626                    tpeer.MTU);
627             printf("\tcurrent/if/max jumbogram size: %d/%d/%d\n",
628                    tpeer.nDgramPackets, tpeer.ifDgramPackets,
629                    tpeer.maxDgramPackets);
630         }
631     }
632     exit(0);
633 }
634
635 /* simple main program */
636 #ifndef AFS_NT40_ENV
637 #include "AFS_component_version_number.c"
638 #endif
639 int
640 main(int argc, char **argv)
641 {
642     struct cmd_syndesc *ts;
643
644 #ifdef RXDEBUG
645     rxi_DebugInit();
646 #endif
647 #ifdef AFS_NT40_ENV
648     if (afs_winsockInit() < 0) {
649         printf("%s: Couldn't initialize winsock. Exiting...\n", argv[0]);
650         return 1;
651     }
652 #endif
653
654     ts = cmd_CreateSyntax(NULL, MainCommand, NULL, 0, "probe RX server");
655     cmd_AddParm(ts, "-servers", CMD_SINGLE, CMD_REQUIRED, "server machine");
656     cmd_AddParm(ts, "-port", CMD_SINGLE, CMD_OPTIONAL, "IP port");
657     cmd_AddParm(ts, "-nodally", CMD_FLAG, CMD_OPTIONAL,
658                 "don't show dallying conns");
659     cmd_AddParm(ts, "-allconnections", CMD_FLAG, CMD_OPTIONAL,
660                 "don't filter out uninteresting connections on server");
661     cmd_AddParm(ts, "-rxstats", CMD_FLAG, CMD_OPTIONAL, "show Rx statistics");
662     cmd_AddParm(ts, "-onlyserver", CMD_FLAG, CMD_OPTIONAL,
663                 "only show server conns");
664     cmd_AddParm(ts, "-onlyclient", CMD_FLAG, CMD_OPTIONAL,
665                 "only show client conns");
666     cmd_AddParm(ts, "-onlyport", CMD_SINGLE, CMD_OPTIONAL,
667                 "show only <port>");
668     cmd_AddParm(ts, "-onlyhost", CMD_SINGLE, CMD_OPTIONAL,
669                 "show only <host>");
670     cmd_AddParm(ts, "-onlyauth", CMD_SINGLE, CMD_OPTIONAL,
671                 "show only <auth level>");
672
673     cmd_AddParm(ts, "-version", CMD_FLAG, CMD_OPTIONAL,
674                 "show AFS version id");
675     cmd_AddParm(ts, "-noconns", CMD_FLAG, CMD_OPTIONAL,
676                 "show no connections");
677     cmd_AddParm(ts, "-peers", CMD_FLAG, CMD_OPTIONAL, "show peers");
678     cmd_AddParm(ts, "-long", CMD_FLAG, CMD_OPTIONAL, "detailed output");
679
680     cmd_Dispatch(argc, argv);
681     exit(0);
682 }