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