349d10837bde749c89972f4183dc9a13788c362e
[openafs.git] / src / venus / fs.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
13 RCSID
14     ("$Header$");
15
16 #include <afs/afs_args.h>
17 #include <rx/xdr.h>
18 #include <sys/ioctl.h>
19 #include <sys/socket.h>
20 #include <netdb.h>
21 #include <errno.h>
22 #include <stdio.h>
23 #include <netinet/in.h>
24 #include <sys/stat.h>
25 #include <afs/stds.h>
26 #include <afs/vice.h>
27 #include <afs/venus.h>
28 #include <afs/com_err.h>
29 #ifdef  AFS_AIX32_ENV
30 #include <signal.h>
31 #endif
32
33 #ifdef HAVE_STRING_H
34 #include <string.h>
35 #else
36 #ifdef HAVE_STRINGS_H
37 #include <strings.h>
38 #endif
39 #endif
40
41 #undef VIRTUE
42 #undef VICE
43 #include "afs/prs_fs.h"
44 #include <afs/afsint.h>
45 #include <afs/auth.h>
46 #include <afs/cellconfig.h>
47 #include <ubik.h>
48 #include <rx/rxkad.h>
49 #include <rx/rx_globals.h>
50 #include <afs/vldbint.h>
51 #include <afs/volser.h>
52 #include <afs/vlserver.h>
53 #include <afs/cmd.h>
54 #include <afs/afsutil.h>
55 #include <stdlib.h>
56 #include <assert.h>
57 #include <afs/ptclient.h>
58
59
60 #define MAXHOSTS 13
61 #define OMAXHOSTS 8
62 #define MAXCELLHOSTS 8
63 #define MAXNAME 100
64 #define MAXSIZE 2048
65 #define MAXINSIZE 1300          /* pioctl complains if data is larger than this */
66 #define VMSGSIZE 128            /* size of msg buf in volume hdr */
67
68 static char space[MAXSIZE];
69 static char tspace[1024];
70 static struct ubik_client *uclient;
71
72 static int GetClientAddrsCmd(), SetClientAddrsCmd(), FlushMountCmd();
73 static int RxStatProcCmd(), RxStatPeerCmd(), GetFidCmd();
74
75 extern char *hostutil_GetNameByINet();
76 extern struct hostent *hostutil_GetHostByName();
77
78
79 extern struct cmd_syndesc *cmd_CreateSyntax();
80 static char pn[] = "fs";
81 static int rxInitDone = 0;
82
83 static void ZapList();
84 static int PruneList();
85 static CleanAcl();
86 static SetVolCmd();
87 static GetCellName();
88 static VLDBInit();
89 static void Die();
90
91 /*
92  * Character to use between name and rights in printed representation for
93  * DFS ACL's.
94  */
95 #define DFS_SEPARATOR   ' '
96
97 typedef char sec_rgy_name_t[1025];      /* A DCE definition */
98
99 struct Acl {
100     int dfs;                    /* Originally true if a dfs acl; now also the type
101                                  * of the acl (1, 2, or 3, corresponding to object,
102                                  * initial dir, or initial object). */
103     sec_rgy_name_t cell;        /* DFS cell name */
104     int nplus;
105     int nminus;
106     struct AclEntry *pluslist;
107     struct AclEntry *minuslist;
108 };
109
110 struct AclEntry {
111     struct AclEntry *next;
112     char name[MAXNAME];
113     afs_int32 rights;
114 };
115
116 struct vcxstat2 {
117     afs_int32 callerAccess;
118     afs_int32 cbExpires;
119     afs_int32 anyAccess;
120     char mvstat;
121 };
122
123 static void
124 ZapAcl(acl)
125      struct Acl *acl;
126 {
127     if (!acl)
128         return;
129     ZapList(acl->pluslist);
130     ZapList(acl->minuslist);
131     free(acl);
132 }
133
134 static
135 foldcmp(a, b)
136      char *a;
137      char *b;
138 {
139     char t, u;
140     while (1) {
141         t = *a++;
142         u = *b++;
143         if (t >= 'A' && t <= 'Z')
144             t += 0x20;
145         if (u >= 'A' && u <= 'Z')
146             u += 0x20;
147         if (t != u)
148             return 1;
149         if (t == 0)
150             return 0;
151     }
152 }
153
154 /*
155  * Mods for the AFS/DFS protocol translator.
156  *
157  * DFS rights. It's ugly to put these definitions here, but they
158  * *cannot* change, because they're part of the wire protocol.
159  * In any event, the protocol translator will guarantee these
160  * assignments for AFS cache managers.
161  */
162 #define DFS_READ          0x01
163 #define DFS_WRITE         0x02
164 #define DFS_EXECUTE       0x04
165 #define DFS_CONTROL       0x08
166 #define DFS_INSERT        0x10
167 #define DFS_DELETE        0x20
168
169 /* the application definable ones (backwards from AFS) */
170 #define DFS_USR0 0x80000000     /* "A" bit */
171 #define DFS_USR1 0x40000000     /* "B" bit */
172 #define DFS_USR2 0x20000000     /* "C" bit */
173 #define DFS_USR3 0x10000000     /* "D" bit */
174 #define DFS_USR4 0x08000000     /* "E" bit */
175 #define DFS_USR5 0x04000000     /* "F" bit */
176 #define DFS_USR6 0x02000000     /* "G" bit */
177 #define DFS_USR7 0x01000000     /* "H" bit */
178 #define DFS_USRALL      (DFS_USR0 | DFS_USR1 | DFS_USR2 | DFS_USR3 |\
179                          DFS_USR4 | DFS_USR5 | DFS_USR6 | DFS_USR7)
180
181 /*
182  * Offset of -id switch in command structure for various commands.
183  * The -if switch is the next switch always.
184  */
185 static int parm_setacl_id, parm_copyacl_id, parm_listacl_id;
186
187 /*
188  * Determine whether either the -id or -if switches are present, and
189  * return 0, 1 or 2, as appropriate. Abort if both switches are present.
190  */
191 /*    int id;   Offset of -id switch; -if is next switch */
192 static int
193 getidf(struct cmd_syndesc *as, int id)
194 {
195     int idf = 0;
196
197     if (as->parms[id].items) {
198         idf |= 1;
199     }
200     if (as->parms[id + 1].items) {
201         idf |= 2;
202     }
203     if (idf == 3) {
204         fprintf(stderr,
205                 "%s: you may specify either -id or -if, but not both switches\n",
206                 pn);
207         exit(1);
208     }
209     return idf;
210 }
211
212 static int
213 PRights(afs_int32 arights, int dfs)
214 {
215     if (!dfs) {
216         if (arights & PRSFS_READ)
217             printf("r");
218         if (arights & PRSFS_LOOKUP)
219             printf("l");
220         if (arights & PRSFS_INSERT)
221             printf("i");
222         if (arights & PRSFS_DELETE)
223             printf("d");
224         if (arights & PRSFS_WRITE)
225             printf("w");
226         if (arights & PRSFS_LOCK)
227             printf("k");
228         if (arights & PRSFS_ADMINISTER)
229             printf("a");
230         if (arights & PRSFS_USR0)
231             printf("A");
232         if (arights & PRSFS_USR1)
233             printf("B");
234         if (arights & PRSFS_USR2)
235             printf("C");
236         if (arights & PRSFS_USR3)
237             printf("D");
238         if (arights & PRSFS_USR4)
239             printf("E");
240         if (arights & PRSFS_USR5)
241             printf("F");
242         if (arights & PRSFS_USR6)
243             printf("G");
244         if (arights & PRSFS_USR7)
245             printf("H");
246     } else {
247         if (arights & DFS_READ)
248             printf("r");
249         else
250             printf("-");
251         if (arights & DFS_WRITE)
252             printf("w");
253         else
254             printf("-");
255         if (arights & DFS_EXECUTE)
256             printf("x");
257         else
258             printf("-");
259         if (arights & DFS_CONTROL)
260             printf("c");
261         else
262             printf("-");
263         if (arights & DFS_INSERT)
264             printf("i");
265         else
266             printf("-");
267         if (arights & DFS_DELETE)
268             printf("d");
269         else
270             printf("-");
271         if (arights & (DFS_USRALL))
272             printf("+");
273         if (arights & DFS_USR0)
274             printf("A");
275         if (arights & DFS_USR1)
276             printf("B");
277         if (arights & DFS_USR2)
278             printf("C");
279         if (arights & DFS_USR3)
280             printf("D");
281         if (arights & DFS_USR4)
282             printf("E");
283         if (arights & DFS_USR5)
284             printf("F");
285         if (arights & DFS_USR6)
286             printf("G");
287         if (arights & DFS_USR7)
288             printf("H");
289     }
290 }
291
292 /* this function returns TRUE (1) if the file is in AFS, otherwise false (0) */
293 static int
294 InAFS(char *apath)
295 {
296     struct ViceIoctl blob;
297     afs_int32 code;
298
299     blob.in_size = 0;
300     blob.out_size = MAXSIZE;
301     blob.out = space;
302
303     code = pioctl(apath, VIOC_FILE_CELL_NAME, &blob, 1);
304     if (code) {
305         if ((errno == EINVAL) || (errno == ENOENT))
306             return 0;
307     }
308     return 1;
309 }
310
311 /* return a static pointer to a buffer */
312 static char *
313 Parent(char *apath)
314 {
315     char *tp;
316     strcpy(tspace, apath);
317     tp = strrchr(tspace, '/');
318     if (tp) {
319         *tp = 0;
320     } else
321         strcpy(tspace, ".");
322     return tspace;
323 }
324
325 enum rtype { add, destroy, deny };
326
327 static afs_int32
328 Convert(char *arights, int dfs, enum rtype *rtypep)
329 {
330     int i, len;
331     afs_int32 mode;
332     char tc;
333
334     *rtypep = add;              /* add rights, by default */
335
336     if (dfs) {
337         if (!strcmp(arights, "null")) {
338             *rtypep = deny;
339             return 0;
340         }
341         if (!strcmp(arights, "read"))
342             return DFS_READ | DFS_EXECUTE;
343         if (!strcmp(arights, "write"))
344             return DFS_READ | DFS_EXECUTE | DFS_INSERT | DFS_DELETE |
345                 DFS_WRITE;
346         if (!strcmp(arights, "all"))
347             return DFS_READ | DFS_EXECUTE | DFS_INSERT | DFS_DELETE |
348                 DFS_WRITE | DFS_CONTROL;
349     } else {
350         if (!strcmp(arights, "read"))
351             return PRSFS_READ | PRSFS_LOOKUP;
352         if (!strcmp(arights, "write"))
353             return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE |
354                 PRSFS_WRITE | PRSFS_LOCK;
355         if (!strcmp(arights, "mail"))
356             return PRSFS_INSERT | PRSFS_LOCK | PRSFS_LOOKUP;
357         if (!strcmp(arights, "all"))
358             return PRSFS_READ | PRSFS_LOOKUP | PRSFS_INSERT | PRSFS_DELETE |
359                 PRSFS_WRITE | PRSFS_LOCK | PRSFS_ADMINISTER;
360     }
361     if (!strcmp(arights, "none")) {
362         *rtypep = destroy;      /* Remove entire entry */
363         return 0;
364     }
365     len = strlen(arights);
366     mode = 0;
367     for (i = 0; i < len; i++) {
368         tc = *arights++;
369         if (dfs) {
370             if (tc == '-')
371                 continue;
372             else if (tc == 'r')
373                 mode |= DFS_READ;
374             else if (tc == 'w')
375                 mode |= DFS_WRITE;
376             else if (tc == 'x')
377                 mode |= DFS_EXECUTE;
378             else if (tc == 'c')
379                 mode |= DFS_CONTROL;
380             else if (tc == 'i')
381                 mode |= DFS_INSERT;
382             else if (tc == 'd')
383                 mode |= DFS_DELETE;
384             else if (tc == 'A')
385                 mode |= DFS_USR0;
386             else if (tc == 'B')
387                 mode |= DFS_USR1;
388             else if (tc == 'C')
389                 mode |= DFS_USR2;
390             else if (tc == 'D')
391                 mode |= DFS_USR3;
392             else if (tc == 'E')
393                 mode |= DFS_USR4;
394             else if (tc == 'F')
395                 mode |= DFS_USR5;
396             else if (tc == 'G')
397                 mode |= DFS_USR6;
398             else if (tc == 'H')
399                 mode |= DFS_USR7;
400             else {
401                 fprintf(stderr, "%s: illegal DFS rights character '%c'.\n",
402                         pn, tc);
403                 exit(1);
404             }
405         } else {
406             if (tc == 'r')
407                 mode |= PRSFS_READ;
408             else if (tc == 'l')
409                 mode |= PRSFS_LOOKUP;
410             else if (tc == 'i')
411                 mode |= PRSFS_INSERT;
412             else if (tc == 'd')
413                 mode |= PRSFS_DELETE;
414             else if (tc == 'w')
415                 mode |= PRSFS_WRITE;
416             else if (tc == 'k')
417                 mode |= PRSFS_LOCK;
418             else if (tc == 'a')
419                 mode |= PRSFS_ADMINISTER;
420             else if (tc == 'A')
421                 mode |= PRSFS_USR0;
422             else if (tc == 'B')
423                 mode |= PRSFS_USR1;
424             else if (tc == 'C')
425                 mode |= PRSFS_USR2;
426             else if (tc == 'D')
427                 mode |= PRSFS_USR3;
428             else if (tc == 'E')
429                 mode |= PRSFS_USR4;
430             else if (tc == 'F')
431                 mode |= PRSFS_USR5;
432             else if (tc == 'G')
433                 mode |= PRSFS_USR6;
434             else if (tc == 'H')
435                 mode |= PRSFS_USR7;
436             else {
437                 fprintf(stderr, "%s: illegal rights character '%c'.\n", pn,
438                         tc);
439                 exit(1);
440             }
441         }
442     }
443     return mode;
444 }
445
446 static struct AclEntry *
447 FindList(struct AclEntry *alist, char *aname)
448 {
449     while (alist) {
450         if (!foldcmp(alist->name, aname))
451             return alist;
452         alist = alist->next;
453     }
454     return 0;
455 }
456
457 /* if no parm specified in a particular slot, set parm to be "." instead */
458 static void
459 SetDotDefault(struct cmd_item **aitemp)
460 {
461     struct cmd_item *ti;
462     if (*aitemp)
463         return;                 /* already has value */
464     /* otherwise, allocate an item representing "." */
465     ti = (struct cmd_item *)malloc(sizeof(struct cmd_item));
466     assert(ti);
467     ti->next = (struct cmd_item *)0;
468     ti->data = (char *)malloc(2);
469     assert(ti->data);
470     strcpy(ti->data, ".");
471     *aitemp = ti;
472 }
473
474 static void
475 ChangeList(struct Acl *al, afs_int32 plus, char *aname, afs_int32 arights)
476 {
477     struct AclEntry *tlist;
478     tlist = (plus ? al->pluslist : al->minuslist);
479     tlist = FindList(tlist, aname);
480     if (tlist) {
481         /* Found the item already in the list. */
482         tlist->rights = arights;
483         if (plus)
484             al->nplus -= PruneList(&al->pluslist, al->dfs);
485         else
486             al->nminus -= PruneList(&al->minuslist, al->dfs);
487         return;
488     }
489     /* Otherwise we make a new item and plug in the new data. */
490     tlist = (struct AclEntry *)malloc(sizeof(struct AclEntry));
491     assert(tlist);
492     strcpy(tlist->name, aname);
493     tlist->rights = arights;
494     if (plus) {
495         tlist->next = al->pluslist;
496         al->pluslist = tlist;
497         al->nplus++;
498         if (arights == 0 || arights == -1)
499             al->nplus -= PruneList(&al->pluslist, al->dfs);
500     } else {
501         tlist->next = al->minuslist;
502         al->minuslist = tlist;
503         al->nminus++;
504         if (arights == 0)
505             al->nminus -= PruneList(&al->minuslist, al->dfs);
506     }
507 }
508
509 static void
510 ZapList(struct AclEntry *alist)
511 {
512     struct AclEntry *tp, *np;
513     for (tp = alist; tp; tp = np) {
514         np = tp->next;
515         free(tp);
516     }
517 }
518
519 static int
520 PruneList(struct AclEntry **ae, int dfs)
521 {
522     struct AclEntry **lp;
523     struct AclEntry *te, *ne;
524     afs_int32 ctr;
525     ctr = 0;
526     lp = ae;
527     for (te = *ae; te; te = ne) {
528         if ((!dfs && te->rights == 0) || te->rights == -1) {
529             *lp = te->next;
530             ne = te->next;
531             free(te);
532             ctr++;
533         } else {
534             ne = te->next;
535             lp = &te->next;
536         }
537     }
538     return ctr;
539 }
540
541 static char *
542 SkipLine(char *astr)
543 {
544     while (*astr != '\n')
545         astr++;
546     astr++;
547     return astr;
548 }
549
550 /*
551  * Create an empty acl, taking into account whether the acl pointed
552  * to by astr is an AFS or DFS acl. Only parse this minimally, so we
553  * can recover from problems caused by bogus ACL's (in that case, always
554  * assume that the acl is AFS: for DFS, the user can always resort to
555  * acl_edit, but for AFS there may be no other way out).
556  */
557 static struct Acl *
558 EmptyAcl(char *astr)
559 {
560     struct Acl *tp;
561     int junk;
562
563     tp = (struct Acl *)malloc(sizeof(struct Acl));
564     assert(tp);
565     tp->nplus = tp->nminus = 0;
566     tp->pluslist = tp->minuslist = 0;
567     tp->dfs = 0;
568     sscanf(astr, "%d dfs:%d %s", &junk, &tp->dfs, tp->cell);
569     return tp;
570 }
571
572 static struct Acl *
573 ParseAcl(char *astr)
574 {
575     int nplus, nminus, i, trights;
576     char tname[MAXNAME];
577     struct AclEntry *first, *last, *tl;
578     struct Acl *ta;
579
580     ta = (struct Acl *)malloc(sizeof(struct Acl));
581     assert(ta);
582     ta->dfs = 0;
583     sscanf(astr, "%d dfs:%d %s", &ta->nplus, &ta->dfs, ta->cell);
584     astr = SkipLine(astr);
585     sscanf(astr, "%d", &ta->nminus);
586     astr = SkipLine(astr);
587
588     nplus = ta->nplus;
589     nminus = ta->nminus;
590
591     last = 0;
592     first = 0;
593     for (i = 0; i < nplus; i++) {
594         sscanf(astr, "%100s %d", tname, &trights);
595         astr = SkipLine(astr);
596         tl = (struct AclEntry *)malloc(sizeof(struct AclEntry));
597         assert(tl);
598         if (!first)
599             first = tl;
600         strcpy(tl->name, tname);
601         tl->rights = trights;
602         tl->next = 0;
603         if (last)
604             last->next = tl;
605         last = tl;
606     }
607     ta->pluslist = first;
608
609     last = 0;
610     first = 0;
611     for (i = 0; i < nminus; i++) {
612         sscanf(astr, "%100s %d", tname, &trights);
613         astr = SkipLine(astr);
614         tl = (struct AclEntry *)malloc(sizeof(struct AclEntry));
615         assert(tl);
616         if (!first)
617             first = tl;
618         strcpy(tl->name, tname);
619         tl->rights = trights;
620         tl->next = 0;
621         if (last)
622             last->next = tl;
623         last = tl;
624     }
625     ta->minuslist = first;
626
627     return ta;
628 }
629
630 static int
631 PrintStatus(VolumeStatus * status, char *name, char *offmsg)
632 {
633     printf("Volume status for vid = %u named %s\n", status->Vid, name);
634     if (*offmsg != 0)
635         printf("Current offline message is %s\n", offmsg);
636     printf("Current disk quota is ");
637     if (status->MaxQuota != 0)
638         printf("%d\n", status->MaxQuota);
639     else
640         printf("unlimited\n");
641     printf("Current blocks used are %d\n", status->BlocksInUse);
642     printf("The partition has %d blocks available out of %d\n\n",
643            status->PartBlocksAvail, status->PartMaxBlocks);
644     return 0;
645 }
646
647 static int
648 QuickPrintStatus(VolumeStatus * status, char *name)
649 {
650     double QuotaUsed = 0.0;
651     double PartUsed = 0.0;
652     int WARN = 0;
653     printf("%-25.25s", name);
654
655     if (status->MaxQuota != 0) {
656         printf("%10d%10d", status->MaxQuota, status->BlocksInUse);
657         QuotaUsed =
658             ((((double)status->BlocksInUse) / status->MaxQuota) * 100.0);
659     } else {
660         printf("  no limit%10d", status->BlocksInUse);
661     }
662     if (QuotaUsed > 90.0) {
663         printf("%5.0f%%<<", QuotaUsed);
664         WARN = 1;
665     } else
666         printf("%5.0f%%  ", QuotaUsed);
667     PartUsed =
668         (100.0 -
669          ((((double)status->PartBlocksAvail) / status->PartMaxBlocks) *
670           100.0));
671     if (PartUsed > 97.0) {
672         printf("%9.0f%%<<", PartUsed);
673         WARN = 1;
674     } else
675         printf("%9.0f%%  ", PartUsed);
676     if (WARN) {
677         printf("  <<WARNING\n");
678     } else
679         printf("\n");
680     return 0;
681 }
682
683 static int
684 QuickPrintSpace(VolumeStatus * status, char *name)
685 {
686     double PartUsed = 0.0;
687     int WARN = 0;
688     printf("%-25.25s", name);
689
690     printf("%10d%10d%10d", status->PartMaxBlocks,
691            status->PartMaxBlocks - status->PartBlocksAvail,
692            status->PartBlocksAvail);
693
694     PartUsed =
695         (100.0 -
696          ((((double)status->PartBlocksAvail) / status->PartMaxBlocks) *
697           100.0));
698     if (PartUsed > 90.0) {
699         printf(" %4.0f%%<<", PartUsed);
700         WARN = 1;
701     } else
702         printf(" %4.0f%%  ", PartUsed);
703     if (WARN) {
704         printf("  <<WARNING\n");
705     } else
706         printf("\n");
707     return 0;
708 }
709
710 static char *
711 AclToString(struct Acl *acl)
712 {
713     static char mydata[MAXSIZE];
714     char tstring[MAXSIZE];
715     char dfsstring[30];
716     struct AclEntry *tp;
717
718     if (acl->dfs)
719         sprintf(dfsstring, " dfs:%d %s", acl->dfs, acl->cell);
720     else
721         dfsstring[0] = '\0';
722     sprintf(mydata, "%d%s\n%d\n", acl->nplus, dfsstring, acl->nminus);
723     for (tp = acl->pluslist; tp; tp = tp->next) {
724         sprintf(tstring, "%s %d\n", tp->name, tp->rights);
725         strcat(mydata, tstring);
726     }
727     for (tp = acl->minuslist; tp; tp = tp->next) {
728         sprintf(tstring, "%s %d\n", tp->name, tp->rights);
729         strcat(mydata, tstring);
730     }
731     return mydata;
732 }
733
734 static int
735 SetACLCmd(struct cmd_syndesc *as, char *arock)
736 {
737     afs_int32 code;
738     struct ViceIoctl blob;
739     struct Acl *ta = 0;
740     struct cmd_item *ti, *ui;
741     int plusp;
742     afs_int32 rights;
743     int clear;
744     int idf = getidf(as, parm_setacl_id);
745     int error = 0;
746
747     if (as->parms[2].items)
748         clear = 1;
749     else
750         clear = 0;
751     plusp = !(as->parms[3].items);
752     for (ti = as->parms[0].items; ti; ti = ti->next) {
753         blob.out_size = MAXSIZE;
754         blob.in_size = idf;
755         blob.in = blob.out = space;
756         code = pioctl(ti->data, VIOCGETAL, &blob, 1);
757         if (code) {
758             Die(errno, ti->data);
759             error = 1;
760             continue;
761         }
762
763         if (ta)
764             ZapAcl(ta);
765         ta = ParseAcl(space);
766         if (!plusp && ta->dfs) {
767             fprintf(stderr,
768                     "%s: %s: you may not use the -negative switch with DFS acl's.\n%s",
769                     pn, ti->data,
770                     "(you may specify \"null\" to revoke all rights, however)\n");
771             error = 1;
772             continue;
773         }
774
775         if (ta)
776             ZapAcl(ta);
777         if (clear)
778             ta = EmptyAcl(space);
779         else
780             ta = ParseAcl(space);
781         CleanAcl(ta, ti->data);
782         for (ui = as->parms[1].items; ui; ui = ui->next->next) {
783             enum rtype rtype;
784             if (!ui->next) {
785                 fprintf(stderr,
786                         "%s: Missing second half of user/access pair.\n", pn);
787                 ZapAcl(ta);
788                 return 1;
789             }
790             rights = Convert(ui->next->data, ta->dfs, &rtype);
791             if (rtype == destroy && !ta->dfs) {
792                 struct AclEntry *tlist;
793
794                 tlist = (plusp ? ta->pluslist : ta->minuslist);
795                 if (!FindList(tlist, ui->data))
796                     continue;
797             }
798             if (rtype == deny && !ta->dfs)
799                 plusp = 0;
800             if (rtype == destroy && ta->dfs)
801                 rights = -1;
802             ChangeList(ta, plusp, ui->data, rights);
803         }
804         blob.in = AclToString(ta);
805         blob.out_size = 0;
806         blob.in_size = 1 + strlen(blob.in);
807         code = pioctl(ti->data, VIOCSETAL, &blob, 1);
808         if (code) {
809             if (errno == EINVAL) {
810                 if (ta->dfs) {
811                     static char *fsenv = 0;
812                     if (!fsenv) {
813                         fsenv = (char *)getenv("FS_EXPERT");
814                     }
815                     fprintf(stderr,
816                             "%s: \"Invalid argument\" was returned when you tried to store a DFS access list.\n",
817                             pn);
818                     if (!fsenv) {
819                         fprintf(stderr,
820                                 "%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s%s",
821                                 "\nPossible reasons for this include:\n\n",
822                                 " -You may have specified an inappropriate combination of rights.\n",
823                                 "  For example, some DFS-supported filesystems may not allow you to\n",
824                                 "  drop the \"c\" right from \"user_obj\".\n\n",
825                                 " -A mask_obj may be required (it is likely required by the underlying\n",
826                                 "  filesystem if you try to set anything other than the basic \"user_obj\"\n",
827                                 "  \"mask_obj\", or \"group_obj\" entries). Unlike acl_edit, the fs command\n",
828                                 "  does not automatically create or update the mask_obj. Try setting\n",
829                                 "  the rights \"mask_obj all\" with \"fs sa\" before adding any explicit\n",
830                                 "  users or groups. You can do this with a single command, such as\n",
831                                 "  \"fs sa mask_obj all user:somename read\"\n\n",
832                                 " -A specified user or group may not exist.\n\n",
833                                 " -You may have tried to delete \"user_obj\", \"group_obj\", or \"other_obj\".\n",
834                                 "  This is probably not allowed by the underlying file system.\n\n",
835                                 " -If you add a user or group to a DFS ACL, remember that it must be\n",
836                                 "  fully specified as \"user:username\" or \"group:groupname\". In addition, there\n",
837                                 "  may be local requirements on the format of the user or group name.\n",
838                                 "  Check with your cell administrator.\n\n",
839                                 " -Or numerous other possibilities. It would be great if we could be more\n",
840                                 "  precise about the actual problem, but for various reasons, this is\n",
841                                 "  impractical via this interface.  If you can't figure it out, you\n",
842                                 "  might try logging into a DCE-equipped machine and use acl_edit (or\n",
843                                 "  whatever is provided). You may get better results. Good luck!\n\n",
844                                 " (You may inhibit this message by setting \"FS_EXPERT\" in your environment)\n");
845                     }
846                 } else {
847                     fprintf(stderr,
848                             "%s: Invalid argument, possible reasons include:\n",
849                             pn);
850                     fprintf(stderr, "\t-File not in AFS\n");
851                     fprintf(stderr,
852                             "\t-Too many users on access control list\n");
853                     fprintf(stderr,
854                             "\t-Tried to add non-existent user to access control list\n");
855                 }
856             } else {
857                 Die(errno, ti->data);
858             }
859             error = 1;
860         }
861     }
862     if (ta)
863         ZapAcl(ta);
864     return error;
865 }
866
867
868 static int
869 CopyACLCmd(struct cmd_syndesc *as, char *arock)
870 {
871     afs_int32 code;
872     struct ViceIoctl blob;
873     struct Acl *fa, *ta = 0;
874     struct AclEntry *tp;
875     struct cmd_item *ti, *ui;
876     int plusp;
877     afs_int32 rights;
878     int clear;
879     int idf = getidf(as, parm_copyacl_id);
880     int error = 0;
881
882     if (as->parms[2].items)
883         clear = 1;
884     else
885         clear = 0;
886     blob.out_size = MAXSIZE;
887     blob.in_size = idf;
888     blob.in = blob.out = space;
889     code = pioctl(as->parms[0].items->data, VIOCGETAL, &blob, 1);
890     if (code) {
891         Die(errno, as->parms[0].items->data);
892         return 1;
893     }
894     fa = ParseAcl(space);
895     CleanAcl(fa, as->parms[0].items->data);
896     for (ti = as->parms[1].items; ti; ti = ti->next) {
897         blob.out_size = MAXSIZE;
898         blob.in_size = idf;
899         blob.in = blob.out = space;
900         code = pioctl(ti->data, VIOCGETAL, &blob, 1);
901         if (code) {
902             Die(errno, ti->data);
903             error = 1;
904             continue;
905         }
906
907         if (ta)
908             ZapAcl(ta);
909         if (clear)
910             ta = EmptyAcl(space);
911         else
912             ta = ParseAcl(space);
913         CleanAcl(ta, ti->data);
914         if (ta->dfs != fa->dfs) {
915             fprintf(stderr,
916                     "%s: incompatible file system types: acl not copied to %s; aborted\n",
917                     pn, ti->data);
918             error = 1;
919             continue;
920         }
921         if (ta->dfs) {
922             if (!clear && strcmp(ta->cell, fa->cell) != 0) {
923                 fprintf(stderr,
924                         "%s: default DCE cell differs for file %s: use \"-clear\" switch; acl not merged\n",
925                         pn, ti->data);
926                 error = 1;
927                 continue;
928             }
929             strcpy(ta->cell, fa->cell);
930         }
931         for (tp = fa->pluslist; tp; tp = tp->next)
932             ChangeList(ta, 1, tp->name, tp->rights);
933         for (tp = fa->minuslist; tp; tp = tp->next)
934             ChangeList(ta, 0, tp->name, tp->rights);
935         blob.in = AclToString(ta);
936         blob.out_size = 0;
937         blob.in_size = 1 + strlen(blob.in);
938         code = pioctl(ti->data, VIOCSETAL, &blob, 1);
939         if (code) {
940             if (errno == EINVAL) {
941                 fprintf(stderr,
942                         "%s: Invalid argument, possible reasons include:\n",
943                         pn);
944                 fprintf(stderr, "\t-File not in AFS\n");
945             } else {
946                 Die(errno, ti->data);
947             }
948             error = 1;
949         }
950     }
951     if (ta)
952         ZapAcl(ta);
953     ZapAcl(fa);
954     return error;
955 }
956
957 /* pioctl() call to get the cellname of a pathname */
958 static afs_int32
959 GetCell(char *fname, char *cellname)
960 {
961     afs_int32 code;
962     struct ViceIoctl blob;
963
964     blob.in_size = 0;
965     blob.out_size = MAXCELLCHARS;
966     blob.out = cellname;
967
968     code = pioctl(fname, VIOC_FILE_CELL_NAME, &blob, 1);
969     return code ? errno : 0;
970 }
971
972 /* Check if a username is valid: If it contains only digits (or a
973  * negative sign), then it might be bad. We then query the ptserver
974  * to see.
975  */
976 static
977 BadName(char *aname, char *fname)
978 {
979     afs_int32 tc, code, id;
980     char *nm;
981     char cell[MAXCELLCHARS];
982
983     for (nm = aname; tc = *nm; nm++) {
984         /* all must be '-' or digit to be bad */
985         if (tc != '-' && (tc < '0' || tc > '9'))
986             return 0;
987     }
988
989     /* Go to the PRDB and see if this all number username is valid */
990     code = GetCell(fname, cell);
991     if (code)
992         return 0;
993
994     pr_Initialize(1, AFSDIR_CLIENT_ETC_DIRPATH, cell);
995     code = pr_SNameToId(aname, &id);
996     pr_End();
997
998     /* 1=>Not-valid; 0=>Valid */
999     return ((!code && (id == ANONYMOUSID)) ? 1 : 0);
1000 }
1001
1002
1003 /* clean up an access control list of its bad entries; return 1 if we made
1004    any changes to the list, and 0 otherwise */
1005 static
1006 CleanAcl(struct Acl *aa, char *fname)
1007 {
1008     struct AclEntry *te, **le, *ne;
1009     int changes;
1010
1011     /* Don't correct DFS ACL's for now */
1012     if (aa->dfs)
1013         return 0;
1014
1015     /* prune out bad entries */
1016     changes = 0;                /* count deleted entries */
1017     le = &aa->pluslist;
1018     for (te = aa->pluslist; te; te = ne) {
1019         ne = te->next;
1020         if (BadName(te->name, fname)) {
1021             /* zap this dude */
1022             *le = te->next;
1023             aa->nplus--;
1024             free(te);
1025             changes++;
1026         } else {
1027             le = &te->next;
1028         }
1029     }
1030     le = &aa->minuslist;
1031     for (te = aa->minuslist; te; te = ne) {
1032         ne = te->next;
1033         if (BadName(te->name, fname)) {
1034             /* zap this dude */
1035             *le = te->next;
1036             aa->nminus--;
1037             free(te);
1038             changes++;
1039         } else {
1040             le = &te->next;
1041         }
1042     }
1043     return changes;
1044 }
1045
1046
1047 /* clean up an acl to not have bogus entries */
1048 static int
1049 CleanACLCmd(struct cmd_syndesc *as, char *arock)
1050 {
1051     afs_int32 code;
1052     struct Acl *ta = 0;
1053     struct ViceIoctl blob;
1054     int changes;
1055     struct cmd_item *ti;
1056     struct AclEntry *te;
1057     int error = 0;
1058
1059     SetDotDefault(&as->parms[0].items);
1060     for (ti = as->parms[0].items; ti; ti = ti->next) {
1061         blob.out_size = MAXSIZE;
1062         blob.in_size = 0;
1063         blob.out = space;
1064         code = pioctl(ti->data, VIOCGETAL, &blob, 1);
1065         if (code) {
1066             Die(errno, ti->data);
1067             error = 1;
1068             continue;
1069         }
1070
1071         if (ta)
1072             ZapAcl(ta);
1073         ta = ParseAcl(space);
1074         if (ta->dfs) {
1075             fprintf(stderr,
1076                     "%s: cleanacl is not supported for DFS access lists.\n",
1077                     pn);
1078             error = 1;
1079             continue;
1080         }
1081
1082         changes = CleanAcl(ta, ti->data);
1083
1084         if (changes) {
1085             /* now set the acl */
1086             blob.in = AclToString(ta);
1087             blob.in_size = strlen(blob.in) + 1;
1088             blob.out_size = 0;
1089             code = pioctl(ti->data, VIOCSETAL, &blob, 1);
1090             if (code) {
1091                 if (errno == EINVAL) {
1092                     fprintf(stderr,
1093                             "%s: Invalid argument, possible reasons include\n",
1094                             pn);
1095                     fprintf(stderr, "%s: File not in vice or\n", pn);
1096                     fprintf(stderr,
1097                             "%s: Too many users on access control list or\n",
1098                             pn);
1099                 } else {
1100                     Die(errno, ti->data);
1101                 }
1102                 error = 1;
1103                 continue;
1104             }
1105
1106             /* now list the updated acl */
1107             printf("Access list for %s is now\n", ti->data);
1108             if (ta->nplus > 0) {
1109                 if (!ta->dfs)
1110                     printf("Normal rights:\n");
1111                 for (te = ta->pluslist; te; te = te->next) {
1112                     printf("  %s ", te->name);
1113                     PRights(te->rights, ta->dfs);
1114                     printf("\n");
1115                 }
1116             }
1117             if (ta->nminus > 0) {
1118                 printf("Negative rights:\n");
1119                 for (te = ta->minuslist; te; te = te->next) {
1120                     printf("  %s ", te->name);
1121                     PRights(te->rights, ta->dfs);
1122                     printf("\n");
1123                 }
1124             }
1125             if (ti->next)
1126                 printf("\n");
1127         } else
1128             printf("Access list for %s is fine.\n", ti->data);
1129     }
1130     if (ta)
1131         ZapAcl(ta);
1132     return error;
1133 }
1134
1135 static int
1136 ListACLCmd(struct cmd_syndesc *as, char *arock)
1137 {
1138     afs_int32 code;
1139     struct Acl *ta;
1140     struct ViceIoctl blob;
1141     struct AclEntry *te;
1142     struct cmd_item *ti;
1143     int idf = getidf(as, parm_listacl_id);
1144     int error = 0;
1145
1146     SetDotDefault(&as->parms[0].items);
1147     for (ti = as->parms[0].items; ti; ti = ti->next) {
1148         blob.out_size = MAXSIZE;
1149         blob.in_size = idf;
1150         blob.in = blob.out = space;
1151         code = pioctl(ti->data, VIOCGETAL, &blob, 1);
1152         if (code) {
1153             Die(errno, ti->data);
1154             error = 1;
1155             continue;
1156         }
1157         ta = ParseAcl(space);
1158         switch (ta->dfs) {
1159         case 0:
1160             printf("Access list for %s is\n", ti->data);
1161             break;
1162         case 1:
1163             printf("DFS access list for %s is\n", ti->data);
1164             break;
1165         case 2:
1166             printf("DFS initial directory access list of %s is\n", ti->data);
1167             break;
1168         case 3:
1169             printf("DFS initial file access list of %s is\n", ti->data);
1170             break;
1171         }
1172         if (ta->dfs) {
1173             printf("  Default cell = %s\n", ta->cell);
1174         }
1175         if (ta->nplus > 0) {
1176             if (!ta->dfs)
1177                 printf("Normal rights:\n");
1178             for (te = ta->pluslist; te; te = te->next) {
1179                 printf("  %s ", te->name);
1180                 PRights(te->rights, ta->dfs);
1181                 printf("\n");
1182             }
1183         }
1184         if (ta->nminus > 0) {
1185             printf("Negative rights:\n");
1186             for (te = ta->minuslist; te; te = te->next) {
1187                 printf("  %s ", te->name);
1188                 PRights(te->rights, ta->dfs);
1189                 printf("\n");
1190             }
1191         }
1192         if (ti->next)
1193             printf("\n");
1194         ZapAcl(ta);
1195     }
1196     return error;
1197 }
1198
1199 static int
1200 GetCallerAccess(struct cmd_syndesc *as, char *arock)
1201 {
1202     struct cmd_item *ti;
1203     int error = 0;
1204
1205     SetDotDefault(&as->parms[0].items);
1206     for (ti = as->parms[0].items; ti; ti = ti->next) {
1207         afs_int32 code;
1208         struct ViceIoctl blob;
1209         struct vcxstat2 stat;
1210         blob.out_size = sizeof(struct vcxstat2);
1211         blob.in_size = 0;
1212         blob.out = (void *)&stat;
1213         code = pioctl(ti->data, VIOC_GETVCXSTATUS2, &blob, 1);
1214         if (code) {
1215             Die(errno, ti->data);
1216             error = 1;
1217             continue;
1218         }
1219         printf("Callers access to %s is ", ti->data);
1220         PRights(stat.callerAccess, 0);
1221         printf("\n");
1222     }
1223     return error;
1224 }
1225
1226 static int
1227 FlushVolumeCmd(struct cmd_syndesc *as, char *arock)
1228 {
1229     afs_int32 code;
1230     struct ViceIoctl blob;
1231     struct cmd_item *ti;
1232     int error = 0;
1233
1234     SetDotDefault(&as->parms[0].items);
1235     for (ti = as->parms[0].items; ti; ti = ti->next) {
1236         blob.in_size = blob.out_size = 0;
1237         code = pioctl(ti->data, VIOC_FLUSHVOLUME, &blob, 0);
1238         if (code) {
1239             fprintf(stderr, "Error flushing volume ");
1240             perror(ti->data);
1241             error = 1;
1242             continue;
1243         }
1244     }
1245     return error;
1246 }
1247
1248 static int
1249 FlushCmd(struct cmd_syndesc *as, char *arock)
1250 {
1251     afs_int32 code;
1252     struct ViceIoctl blob;
1253     struct cmd_item *ti;
1254     int error = 0;
1255
1256     for (ti = as->parms[0].items; ti; ti = ti->next) {
1257         blob.in_size = blob.out_size = 0;
1258         code = pioctl(ti->data, VIOCFLUSH, &blob, 0);
1259         if (code) {
1260             if (errno == EMFILE) {
1261                 fprintf(stderr, "%s: Can't flush active file %s\n", pn,
1262                         ti->data);
1263             } else {
1264                 fprintf(stderr, "%s: Error flushing file ", pn);
1265                 perror(ti->data);
1266             }
1267             error = 1;
1268             continue;
1269         }
1270     }
1271     return error;
1272 }
1273
1274 /* all this command does is repackage its args and call SetVolCmd */
1275 static int
1276 SetQuotaCmd(struct cmd_syndesc *as, char *arock)
1277 {
1278     struct cmd_syndesc ts;
1279
1280     /* copy useful stuff from our command slot; we may later have to reorder */
1281     memcpy(&ts, as, sizeof(ts));        /* copy whole thing */
1282     return SetVolCmd(&ts);
1283 }
1284
1285 static int
1286 SetVolCmd(struct cmd_syndesc *as, char *arock)
1287 {
1288     afs_int32 code;
1289     struct ViceIoctl blob;
1290     struct cmd_item *ti;
1291     struct VolumeStatus *status;
1292     char *offmsg, *input;
1293     int error = 0;
1294
1295     SetDotDefault(&as->parms[0].items);
1296     for (ti = as->parms[0].items; ti; ti = ti->next) {
1297         /* once per file */
1298         blob.out_size = MAXSIZE;
1299         blob.in_size = sizeof(*status) + 3;     /* for the three terminating nulls */
1300         blob.out = space;
1301         blob.in = space;
1302         status = (VolumeStatus *) space;
1303         status->MinQuota = status->MaxQuota = -1;
1304         offmsg = NULL;
1305         if (as->parms[1].items) {
1306             code = util_GetInt32(as->parms[1].items->data, &status->MaxQuota);
1307             if (code) {
1308                 fprintf(stderr, "%s: bad integer specified for quota.\n", pn);
1309                 error = 1;
1310                 continue;
1311             }
1312         }
1313         if (as->parms[2].items)
1314             offmsg = as->parms[2].items->data;
1315         input = (char *)status + sizeof(*status);
1316         *(input++) = '\0';      /* never set name: this call doesn't change vldb */
1317         if (offmsg) {
1318             if (strlen(offmsg) >= VMSGSIZE) {
1319                 fprintf(stderr,
1320                         "%s: message must be shorter than %d characters\n",
1321                         pn, VMSGSIZE);
1322                 error = 1;
1323                 continue;
1324             }
1325             strcpy(input, offmsg);
1326             blob.in_size += strlen(offmsg);
1327             input += strlen(offmsg) + 1;
1328         } else
1329             *(input++) = '\0';
1330         *(input++) = '\0';      /* Pad for old style volume "motd" */
1331         code = pioctl(ti->data, VIOCSETVOLSTAT, &blob, 1);
1332         if (code) {
1333             Die(errno, ti->data);
1334             error = 1;
1335         }
1336     }
1337     return error;
1338 }
1339
1340 /*
1341  * Why is VenusFid declared in the kernel-only section of afs.h,
1342  * if it's the exported interface of the cache manager?
1343  */
1344 struct VenusFid {
1345     afs_int32 Cell;
1346     AFSFid Fid;
1347 };
1348
1349 static int
1350 ExamineCmd(struct cmd_syndesc *as, char *arock)
1351 {
1352     afs_int32 code;
1353     struct ViceIoctl blob;
1354     struct cmd_item *ti;
1355     struct VolumeStatus *status;
1356     char *name, *offmsg;
1357     int error = 0;
1358
1359     SetDotDefault(&as->parms[0].items);
1360     for (ti = as->parms[0].items; ti; ti = ti->next) {
1361         struct VenusFid vfid;
1362
1363         /* once per file */
1364         blob.out_size = MAXSIZE;
1365         blob.in_size = 0;
1366         blob.out = space;
1367         code = pioctl(ti->data, VIOCGETVOLSTAT, &blob, 1);
1368         if (code) {
1369             Die(errno, ti->data);
1370             error = 1;
1371             continue;
1372         }
1373         status = (VolumeStatus *) space;
1374         name = (char *)status + sizeof(*status);
1375         offmsg = name + strlen(name) + 1;
1376
1377         blob.out_size = sizeof(struct VenusFid);
1378         blob.out = (char *) &vfid;
1379         if (0 == pioctl(ti->data, VIOCGETFID, &blob, 1)) {
1380             printf("File %s (%u.%u.%u) contained in volume %u\n",
1381                    ti->data, vfid.Fid.Volume, vfid.Fid.Vnode, vfid.Fid.Unique,
1382                    vfid.Fid.Volume);
1383         }
1384
1385         PrintStatus(status, name, offmsg);
1386     }
1387     return error;
1388 }
1389
1390 static int
1391 ListQuotaCmd(struct cmd_syndesc *as, char *arock)
1392 {
1393     afs_int32 code;
1394     struct ViceIoctl blob;
1395     struct cmd_item *ti;
1396     struct VolumeStatus *status;
1397     char *name;
1398     int error = 0;
1399
1400     printf("%-25s%-10s%-10s%-7s%-11s\n", "Volume Name", "     Quota",
1401            "      Used", " %Used", "  Partition");
1402     SetDotDefault(&as->parms[0].items);
1403     for (ti = as->parms[0].items; ti; ti = ti->next) {
1404         /* once per file */
1405         blob.out_size = MAXSIZE;
1406         blob.in_size = 0;
1407         blob.out = space;
1408         code = pioctl(ti->data, VIOCGETVOLSTAT, &blob, 1);
1409         if (code) {
1410             Die(errno, ti->data);
1411             error = 1;
1412             continue;
1413         }
1414         status = (VolumeStatus *) space;
1415         name = (char *)status + sizeof(*status);
1416         QuickPrintStatus(status, name);
1417     }
1418     return error;
1419 }
1420
1421 static int
1422 WhereIsCmd(struct cmd_syndesc *as, char *arock)
1423 {
1424     afs_int32 code;
1425     struct ViceIoctl blob;
1426     struct cmd_item *ti;
1427     int j;
1428     afs_int32 *hosts;
1429     char *tp;
1430     int error = 0;
1431
1432     SetDotDefault(&as->parms[0].items);
1433     for (ti = as->parms[0].items; ti; ti = ti->next) {
1434         /* once per file */
1435         blob.out_size = MAXSIZE;
1436         blob.in_size = 0;
1437         blob.out = space;
1438         memset(space, 0, sizeof(space));
1439         code = pioctl(ti->data, VIOCWHEREIS, &blob, 1);
1440         if (code) {
1441             Die(errno, ti->data);
1442             error = 1;
1443             continue;
1444         }
1445         hosts = (afs_int32 *) space;
1446         printf("File %s is on host%s ", ti->data,
1447                (hosts[0] && !hosts[1]) ? "" : "s");
1448         for (j = 0; j < MAXHOSTS; j++) {
1449             if (hosts[j] == 0)
1450                 break;
1451             tp = hostutil_GetNameByINet(hosts[j]);
1452             printf("%s ", tp);
1453         }
1454         printf("\n");
1455     }
1456     return error;
1457 }
1458
1459
1460 static int
1461 DiskFreeCmd(struct cmd_syndesc *as, char *arock)
1462 {
1463     afs_int32 code;
1464     struct ViceIoctl blob;
1465     struct cmd_item *ti;
1466     char *name;
1467     struct VolumeStatus *status;
1468     int error = 0;
1469
1470     printf("%-25s%-10s%-10s%-10s%-6s\n", "Volume Name", "    kbytes",
1471            "      used", "     avail", " %used");
1472     SetDotDefault(&as->parms[0].items);
1473     for (ti = as->parms[0].items; ti; ti = ti->next) {
1474         /* once per file */
1475         blob.out_size = MAXSIZE;
1476         blob.in_size = 0;
1477         blob.out = space;
1478         code = pioctl(ti->data, VIOCGETVOLSTAT, &blob, 1);
1479         if (code) {
1480             Die(errno, ti->data);
1481             error = 1;
1482             continue;
1483         }
1484         status = (VolumeStatus *) space;
1485         name = (char *)status + sizeof(*status);
1486         QuickPrintSpace(status, name);
1487     }
1488     return error;
1489 }
1490
1491 static int
1492 QuotaCmd(struct cmd_syndesc *as, char *arock)
1493 {
1494     afs_int32 code;
1495     struct ViceIoctl blob;
1496     struct cmd_item *ti;
1497     double quotaPct;
1498     struct VolumeStatus *status;
1499     int error = 0;
1500
1501     SetDotDefault(&as->parms[0].items);
1502     for (ti = as->parms[0].items; ti; ti = ti->next) {
1503         /* once per file */
1504         blob.out_size = MAXSIZE;
1505         blob.in_size = 0;
1506         blob.out = space;
1507         code = pioctl(ti->data, VIOCGETVOLSTAT, &blob, 1);
1508         if (code) {
1509             Die(errno, ti->data);
1510             error = 1;
1511             continue;
1512         }
1513         status = (VolumeStatus *) space;
1514         if (status->MaxQuota)
1515             quotaPct =
1516                 ((((double)status->BlocksInUse) / status->MaxQuota) * 100.0);
1517         else
1518             quotaPct = 0.0;
1519         printf("%2.0f%% of quota used.\n", quotaPct);
1520     }
1521     return error;
1522 }
1523
1524 static int
1525 ListMountCmd(struct cmd_syndesc *as, char *arock)
1526 {
1527     afs_int32 code;
1528     struct ViceIoctl blob;
1529     struct cmd_item *ti;
1530     char orig_name[1024];       /*Original name, may be modified */
1531     char true_name[1024];       /*``True'' dirname (e.g., symlink target) */
1532     char parent_dir[1024];      /*Parent directory of true name */
1533     char *last_component;       /*Last component of true name */
1534     struct stat statbuff;       /*Buffer for status info */
1535     int link_chars_read;        /*Num chars read in readlink() */
1536     int thru_symlink;           /*Did we get to a mount point via a symlink? */
1537     int error = 0;
1538
1539     for (ti = as->parms[0].items; ti; ti = ti->next) {
1540         /* once per file */
1541         thru_symlink = 0;
1542         sprintf(orig_name, "%s%s", (ti->data[0] == '/') ? "" : "./",
1543                 ti->data);
1544
1545         if (lstat(orig_name, &statbuff) < 0) {
1546             /* if lstat fails, we should still try the pioctl, since it
1547              * may work (for example, lstat will fail, but pioctl will
1548              * work if the volume of offline (returning ENODEV). */
1549             statbuff.st_mode = S_IFDIR; /* lie like pros */
1550         }
1551
1552         /*
1553          * The lstat succeeded.  If the given file is a symlink, substitute
1554          * the file name with the link name.
1555          */
1556         if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
1557             thru_symlink = 1;
1558             /*
1559              * Read name of resolved file.
1560              */
1561             link_chars_read = readlink(orig_name, true_name, 1024);
1562             if (link_chars_read <= 0) {
1563                 fprintf(stderr,
1564                         "%s: Can't read target name for '%s' symbolic link!\n",
1565                         pn, orig_name);
1566                 error = 1;
1567                 continue;
1568             }
1569
1570             /*
1571              * Add a trailing null to what was read, bump the length.
1572              */
1573             true_name[link_chars_read++] = 0;
1574
1575             /*
1576              * If the symlink is an absolute pathname, we're fine.  Otherwise, we
1577              * have to create a full pathname using the original name and the
1578              * relative symlink name.  Find the rightmost slash in the original
1579              * name (we know there is one) and splice in the symlink value.
1580              */
1581             if (true_name[0] != '/') {
1582                 last_component = (char *)strrchr(orig_name, '/');
1583                 strcpy(++last_component, true_name);
1584                 strcpy(true_name, orig_name);
1585             }
1586         } else
1587             strcpy(true_name, orig_name);
1588
1589         /*
1590          * Find rightmost slash, if any.
1591          */
1592         last_component = (char *)strrchr(true_name, '/');
1593         if (last_component) {
1594             /*
1595              * Found it.  Designate everything before it as the parent directory,
1596              * everything after it as the final component.
1597              */
1598             strncpy(parent_dir, true_name, last_component - true_name);
1599             parent_dir[last_component - true_name] = 0;
1600             last_component++;   /*Skip the slash */
1601         } else {
1602             /*
1603              * No slash appears in the given file name.  Set parent_dir to the current
1604              * directory, and the last component as the given name.
1605              */
1606             strcpy(parent_dir, ".");
1607             last_component = true_name;
1608         }
1609
1610         if (strcmp(last_component, ".") == 0
1611             || strcmp(last_component, "..") == 0) {
1612             fprintf(stderr,
1613                     "%s: you may not use '.' or '..' as the last component\n",
1614                     pn);
1615             fprintf(stderr, "%s: of a name in the 'fs lsmount' command.\n",
1616                     pn);
1617             error = 1;
1618             continue;
1619         }
1620
1621         blob.in = last_component;
1622         blob.in_size = strlen(last_component) + 1;
1623         blob.out_size = MAXSIZE;
1624         blob.out = space;
1625         memset(space, 0, MAXSIZE);
1626
1627         code = pioctl(parent_dir, VIOC_AFS_STAT_MT_PT, &blob, 1);
1628
1629         if (code == 0) {
1630             printf("'%s' is a %smount point for volume '%s'\n", ti->data,
1631                    (thru_symlink ? "symbolic link, leading to a " : ""),
1632                    space);
1633         } else {
1634             if (errno == EINVAL) {
1635                 fprintf(stderr, "'%s' is not a mount point.\n", ti->data);
1636             } else {
1637                 Die(errno, (ti->data ? ti->data : parent_dir));
1638             }
1639             error = 1;
1640         }
1641     }
1642     return error;
1643 }
1644
1645 static
1646 MakeMountCmd(struct cmd_syndesc *as, char *arock)
1647 {
1648     afs_int32 code;
1649     char *cellName, *volName, *tmpName;
1650     struct afsconf_cell info;
1651     struct vldbentry vldbEntry;
1652     struct ViceIoctl blob;
1653
1654 /*
1655
1656 defect #3069
1657
1658     if (as->parms[5].items && !as->parms[2].items) {
1659         fprintf(stderr, "%s: must provide cell when creating cellular mount point.\n", pn);
1660         return 1;
1661     }
1662 */
1663
1664     if (as->parms[2].items)     /* cell name specified */
1665         cellName = as->parms[2].items->data;
1666     else
1667         cellName = NULL;
1668     volName = as->parms[1].items->data;
1669
1670     if (strlen(volName) >= 64) {
1671         fprintf(stderr,
1672                 "%s: volume name too long (length must be < 64 characters)\n",
1673                 pn);
1674         return 1;
1675     }
1676
1677     /* Check for a cellname in the volume specification, and complain
1678      * if it doesn't match what was specified with -cell */
1679     if (tmpName = strchr(volName, ':')) {
1680         *tmpName = '\0';
1681         if (cellName) {
1682             if (strcasecmp(cellName, volName)) {
1683                 fprintf(stderr, "%s: cellnames do not match.\n", pn);
1684                 return 1;
1685             }
1686         }
1687         cellName = volName;
1688         volName = ++tmpName;
1689     }
1690
1691     if (!InAFS(Parent(as->parms[0].items->data))) {
1692         fprintf(stderr,
1693                 "%s: mount points must be created within the AFS file system\n",
1694                 pn);
1695         return 1;
1696     }
1697
1698     if (!cellName) {
1699         blob.in_size = 0;
1700         blob.out_size = MAXSIZE;
1701         blob.out = space;
1702         code =
1703             pioctl(Parent(as->parms[0].items->data), VIOC_FILE_CELL_NAME,
1704                    &blob, 1);
1705     }
1706
1707     code = GetCellName(cellName ? cellName : space, &info);
1708     if (code) {
1709         return 1;
1710     }
1711     if (!(as->parms[4].items)) {
1712         /* not fast, check which cell the mountpoint is being created in */
1713         /* not fast, check name with VLDB */
1714         code = VLDBInit(1, &info);
1715         if (code == 0) {
1716             /* make the check.  Don't complain if there are problems with init */
1717             code =
1718                 ubik_VL_GetEntryByNameO(uclient, 0, volName,
1719                           &vldbEntry);
1720             if (code == VL_NOENT) {
1721                 fprintf(stderr,
1722                         "%s: warning, volume %s does not exist in cell %s.\n",
1723                         pn, volName, cellName ? cellName : space);
1724             }
1725         }
1726     }
1727
1728     if (as->parms[3].items)     /* if -rw specified */
1729         strcpy(space, "%");
1730     else
1731         strcpy(space, "#");
1732     if (cellName) {
1733         /* cellular mount point, prepend cell prefix */
1734         strcat(space, info.name);
1735         strcat(space, ":");
1736     }
1737     strcat(space, volName);     /* append volume name */
1738     strcat(space, ".");         /* stupid convention; these end with a period */
1739     code = symlink(space, as->parms[0].items->data);
1740     if (code) {
1741         Die(errno, as->parms[0].items->data);
1742         return 1;
1743     }
1744     return 0;
1745 }
1746
1747 /*
1748  * Delete AFS mount points.  Variables are used as follows:
1749  *      tbuffer: Set to point to the null-terminated directory name of the mount point
1750  *          (or ``.'' if none is provided)
1751  *      tp: Set to point to the actual name of the mount point to nuke.
1752  */
1753 static int
1754 RemoveMountCmd(struct cmd_syndesc *as, char *arock)
1755 {
1756     afs_int32 code = 0;
1757     struct ViceIoctl blob;
1758     struct cmd_item *ti;
1759     char tbuffer[1024];
1760     char lsbuffer[1024];
1761     char *tp;
1762     int error = 0;
1763
1764     for (ti = as->parms[0].items; ti; ti = ti->next) {
1765         /* once per file */
1766         tp = (char *)strrchr(ti->data, '/');
1767         if (tp) {
1768             strncpy(tbuffer, ti->data, code = tp - ti->data);   /* the dir name */
1769             tbuffer[code] = 0;
1770             tp++;               /* skip the slash */
1771         } else {
1772             strcpy(tbuffer, ".");
1773             tp = ti->data;
1774         }
1775         blob.in = tp;
1776         blob.in_size = strlen(tp) + 1;
1777         blob.out = lsbuffer;
1778         blob.out_size = sizeof(lsbuffer);
1779         code = pioctl(tbuffer, VIOC_AFS_STAT_MT_PT, &blob, 0);
1780         if (code) {
1781             if (errno == EINVAL) {
1782                 fprintf(stderr, "%s: '%s' is not a mount point.\n", pn,
1783                         ti->data);
1784             } else {
1785                 Die(errno, ti->data);
1786             }
1787             error = 1;
1788             continue;           /* don't bother trying */
1789         }
1790         blob.out_size = 0;
1791         blob.in = tp;
1792         blob.in_size = strlen(tp) + 1;
1793         code = pioctl(tbuffer, VIOC_AFS_DELETE_MT_PT, &blob, 0);
1794         if (code) {
1795             Die(errno, ti->data);
1796             error = 1;
1797         }
1798     }
1799     return error;
1800 }
1801
1802 /*
1803 */
1804
1805 static int
1806 CheckServersCmd(struct cmd_syndesc *as, char *arock)
1807 {
1808     afs_int32 code;
1809     struct ViceIoctl blob;
1810     afs_int32 j;
1811     afs_int32 temp;
1812     char *tp;
1813     char tbuffer[128];
1814     afs_int32 interval;
1815     struct afsconf_cell info;
1816     struct chservinfo checkserv;
1817
1818     memset(&checkserv, 0, sizeof(struct chservinfo));
1819     blob.in_size = sizeof(struct chservinfo);
1820     blob.in = (caddr_t) & checkserv;
1821
1822     blob.out_size = MAXSIZE;
1823     blob.out = space;
1824     memset(space, 0, sizeof(afs_int32));        /* so we assure zero when nothing is copied back */
1825
1826     /* prepare flags for checkservers command */
1827     temp = 2;                   /* default to checking local cell only */
1828     if (as->parms[2].items)
1829         temp |= 1;              /* set fast flag */
1830     if (as->parms[1].items)
1831         temp &= ~2;             /* turn off local cell check */
1832
1833     checkserv.magic = 0x12345678;       /* XXX */
1834     checkserv.tflags = temp;
1835
1836     /* now copy in optional cell name, if specified */
1837     if (as->parms[0].items) {
1838         code = GetCellName(as->parms[0].items->data, &info);
1839         if (code) {
1840             return 1;
1841         }
1842         strcpy(checkserv.tbuffer, info.name);
1843         checkserv.tsize = strlen(info.name) + 1;
1844     } else {
1845         strcpy(checkserv.tbuffer, "\0");
1846         checkserv.tsize = 0;
1847     }
1848
1849     if (as->parms[3].items) {
1850         checkserv.tinterval = atol(as->parms[3].items->data);
1851
1852         /* sanity check */
1853         if (checkserv.tinterval < 0) {
1854             printf
1855                 ("Warning: The negative -interval is ignored; treated as an inquiry\n");
1856             checkserv.tinterval = 0;
1857         } else if (checkserv.tinterval > 600) {
1858             printf
1859                 ("Warning: The maximum -interval value is 10 mins (600 secs)\n");
1860             checkserv.tinterval = 600;  /* 10 min max interval */
1861         }
1862     } else {
1863         checkserv.tinterval = -1;       /* don't change current interval */
1864     }
1865
1866     code = pioctl(0, VIOCCKSERV, &blob, 1);
1867     if (code) {
1868         if ((errno == EACCES) && (checkserv.tinterval > 0)) {
1869             printf("Must be root to change -interval\n");
1870             return 1;
1871         }
1872         Die(errno, 0);
1873         return 1;
1874     }
1875     memcpy(&temp, space, sizeof(afs_int32));
1876     if (checkserv.tinterval >= 0) {
1877         if (checkserv.tinterval > 0)
1878             printf
1879                 ("The new down server probe interval (%d secs) is now in effect (old interval was %d secs)\n",
1880                  checkserv.tinterval, temp);
1881         else
1882             printf("The current down server probe interval is %d secs\n",
1883                    temp);
1884         return 0;
1885     }
1886     if (temp == 0) {
1887         printf("All servers are running.\n");
1888     } else {
1889         printf
1890             ("These servers unavailable due to network or server problems: ");
1891         for (j = 0;; j++) {
1892             memcpy(&temp, space + j * sizeof(afs_int32), sizeof(afs_int32));
1893             if (temp == 0)
1894                 break;
1895             tp = hostutil_GetNameByINet(temp);
1896             printf(" %s", tp);
1897         }
1898         printf(".\n");
1899         code = 1;               /* XXX */
1900     }
1901     return code;
1902 }
1903
1904 static int
1905 MessagesCmd(struct cmd_syndesc *as, char *arock)
1906 {
1907     afs_int32 code = 0;
1908     struct ViceIoctl blob;
1909     afs_int32 j;
1910     afs_int32 temp;
1911     struct gaginfo gagflags;
1912     struct cmd_item *show;
1913
1914     memset(&gagflags, 0, sizeof(struct gaginfo));
1915     blob.in_size = sizeof(struct gaginfo);
1916     blob.in = (caddr_t) & gagflags;
1917     blob.out_size = MAXSIZE;
1918     blob.out = space;
1919     memset(space, 0, sizeof(afs_int32));        /* so we assure zero when nothing is copied back */
1920
1921     if (show = as->parms[0].items) {
1922         if (!strcasecmp(show->data, "user"))
1923             gagflags.showflags |= GAGUSER;
1924         else if (!strcasecmp(show->data, "console"))
1925             gagflags.showflags |= GAGCONSOLE;
1926         else if (!strcasecmp(show->data, "all"))
1927             gagflags.showflags |= GAGCONSOLE | GAGUSER;
1928         else if (!strcasecmp(show->data, "none"))
1929             /* do nothing */ ;
1930         else {
1931             fprintf(stderr,
1932                     "unrecognized flag %s: must be in {user,console,all,none}\n",
1933                     show->data);
1934             code = EINVAL;
1935         }
1936     }
1937
1938     if (code)
1939         return 1;
1940
1941     code = pioctl(0, VIOC_GAG, &blob, 1);
1942     if (code) {
1943         Die(errno, 0);
1944         return 1;
1945     }
1946
1947     return 0;
1948 }
1949
1950 static int
1951 CheckVolumesCmd(struct cmd_syndesc *as, char *arock)
1952 {
1953     afs_int32 code;
1954     struct ViceIoctl blob;
1955
1956     blob.in_size = 0;
1957     blob.out_size = 0;
1958     code = pioctl(0, VIOCCKBACK, &blob, 1);
1959     if (code) {
1960         Die(errno, 0);
1961         return 1;
1962     }
1963
1964     printf("All volumeID/name mappings checked.\n");
1965     return 0;
1966 }
1967
1968 static int
1969 SetCacheSizeCmd(struct cmd_syndesc *as, char *arock)
1970 {
1971     afs_int32 code;
1972     struct ViceIoctl blob;
1973     afs_int32 temp;
1974
1975     if (!as->parms[0].items && !as->parms[1].items) {
1976         fprintf(stderr, "%s: syntax error in setcachesize cmd.\n", pn);
1977         return 1;
1978     }
1979     if (as->parms[0].items) {
1980         code = util_GetInt32(as->parms[0].items->data, &temp);
1981         if (code) {
1982             fprintf(stderr, "%s: bad integer specified for cache size.\n",
1983                     pn);
1984             return 1;
1985         }
1986     } else
1987         temp = 0;
1988     blob.in = (char *)&temp;
1989     blob.in_size = sizeof(afs_int32);
1990     blob.out_size = 0;
1991     code = pioctl(0, VIOCSETCACHESIZE, &blob, 1);
1992     if (code) {
1993         if (errno == EROFS) {
1994             printf
1995                 ("'fs setcache' not allowed on memory cache based cache managers.\n");
1996         } else {
1997             Die(errno, NULL);
1998         }
1999         return 1;
2000     }
2001
2002     printf("New cache size set.\n");
2003     return 0;
2004 }
2005
2006 #define MAXGCSIZE       16
2007 static int
2008 GetCacheParmsCmd(struct cmd_syndesc *as, char *arock)
2009 {
2010     afs_int32 code, filesUsed;
2011     struct ViceIoctl blob;
2012     afs_int32 parms[MAXGCSIZE];
2013     double percentFiles, percentBlocks;
2014     afs_int32 flags = 0;
2015
2016     if (as->parms[0].items){ /* -files */
2017         flags = 1;
2018     } else if (as->parms[1].items){ /* -excessive */
2019         flags = 2;
2020     } else {
2021         flags = 0;
2022     }
2023
2024     memset(parms, '\0', sizeof parms);  /* avoid Purify UMR error */
2025     if (flags){
2026         blob.in = (char *)&flags;
2027         blob.in_size = sizeof(afs_int32);
2028     } else {    /* be backward compatible */
2029         blob.in = NULL;
2030         blob.in_size = 0;
2031     }
2032     blob.out_size = sizeof(parms);
2033     blob.out = (char *)parms;
2034     code = pioctl(0, VIOCGETCACHEPARMS, &blob, 1);
2035     if (code) {
2036         Die(errno, NULL);
2037         return 1;
2038     }
2039
2040     if (!flags){
2041         printf("AFS using %d of the cache's available %d 1K byte blocks.\n",
2042                 parms[1], parms[0]);
2043         if (parms[1] > parms[0])
2044                 printf("[Cache guideline temporarily deliberately exceeded; it will be adjusted down but you may wish to increase the cache size.]\n");
2045         return 0;
2046     }
2047
2048     percentBlocks = ((double)parms[1]/parms[0]) * 100;
2049     printf("AFS using %5.0f%% of cache blocks (%d of %d 1k blocks)\n",
2050            percentBlocks, parms[1], parms[0]);
2051
2052     if (parms[2] == 0)
2053         return 0;
2054
2055     filesUsed = parms[2] - parms[3];
2056     percentFiles = ((double)filesUsed/parms[2]) * 100;
2057     printf("          %5.0f%% of the cache files (%d of %d files)\n",
2058             percentFiles, filesUsed, parms[2]);
2059     if (flags == 2){
2060         printf("        afs_cacheFiles: %10d\n", parms[2]);
2061         printf("        IFFree:         %10d\n", parms[3]); 
2062         printf("        IFEverUsed:     %10d\n", parms[4]); 
2063         printf("        IFDataMod:      %10d\n", parms[5]); 
2064         printf("        IFDirtyPages:   %10d\n", parms[6]);
2065         printf("        IFAnyPages:     %10d\n", parms[7]); 
2066         printf("        IFDiscarded:    %10d\n", parms[8]);
2067         printf("        DCentries:  %10d\n", parms[9]);
2068         printf("          0k-   4K: %10d\n", parms[10]); 
2069         printf("          4k-  16k: %10d\n", parms[11]); 
2070         printf("         16k-  64k: %10d\n", parms[12]); 
2071         printf("         64k- 256k: %10d\n", parms[13]); 
2072         printf("        256k-   1M: %10d\n", parms[14]); 
2073         printf("              >=1M: %10d\n", parms[15]); 
2074     }
2075
2076     if (percentBlocks > 90)
2077         printf("[cache size usage over 90%, consider increasing cache size]\n");
2078     if (percentFiles > 90)
2079         printf("[cache file usage over 90%, consider increasing '-files' argument to afsd]\n");
2080          
2081     return 0;
2082 }
2083
2084 static int
2085 ListCellsCmd(struct cmd_syndesc *as, char *arock)
2086 {
2087     afs_int32 code;
2088     afs_int32 i, j;
2089     char *tcp, *tp;
2090     afs_int32 clear;
2091     struct ViceIoctl blob;
2092     int resolve;
2093
2094     resolve = !(as->parms[0].items);    /* -numeric */
2095
2096     for (i = 0;; i++) {
2097         tp = space;
2098         memcpy(tp, &i, sizeof(afs_int32));
2099         blob.out_size = MAXSIZE;
2100         blob.in_size = sizeof(afs_int32);
2101         blob.in = space;
2102         blob.out = space;
2103         code = pioctl(0, VIOCGETCELL, &blob, 1);
2104         if (code < 0) {
2105             if (errno == EDOM)
2106                 break;          /* done with the list */
2107             Die(errno, 0);
2108             return 1;
2109         }
2110         tp = space;
2111         printf("Cell %s on hosts", tp + MAXCELLHOSTS * sizeof(afs_int32));
2112         for (j = 0; j < MAXCELLHOSTS; j++) {
2113             afs_int32 addr;
2114             char *name, tbuffer[20];
2115
2116             memcpy(&addr, tp + j * sizeof(afs_int32), sizeof(afs_int32));
2117             if (addr == 0)
2118                 break;
2119
2120             if (resolve) {
2121                 name = hostutil_GetNameByINet(addr);
2122             } else {
2123                 addr = ntohl(addr);
2124                 sprintf(tbuffer, "%d.%d.%d.%d", (addr >> 24) & 0xff,
2125                         (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
2126                 name = tbuffer;
2127             }
2128             printf(" %s", name);
2129         }
2130         printf(".\n");
2131     }
2132     return 0;
2133 }
2134
2135 static int
2136 ListAliasesCmd(struct cmd_syndesc *as, char *arock)
2137 {
2138     afs_int32 code, i;
2139     char *tp, *aliasName, *realName;
2140     struct ViceIoctl blob;
2141
2142     for (i = 0;; i++) {
2143         tp = space;
2144         memcpy(tp, &i, sizeof(afs_int32));
2145         blob.out_size = MAXSIZE;
2146         blob.in_size = sizeof(afs_int32);
2147         blob.in = space;
2148         blob.out = space;
2149         code = pioctl(0, VIOC_GETALIAS, &blob, 1);
2150         if (code < 0) {
2151             if (errno == EDOM)
2152                 break;          /* done with the list */
2153             Die(errno, 0);
2154             return 1;
2155         }
2156         tp = space;
2157         aliasName = tp;
2158         tp += strlen(aliasName) + 1;
2159         realName = tp;
2160         printf("Alias %s for cell %s\n", aliasName, realName);
2161     }
2162     return 0;
2163 }
2164
2165 static int
2166 CallBackRxConnCmd(struct cmd_syndesc *as, char *arock)
2167 {
2168     afs_int32 code;
2169     struct ViceIoctl blob;
2170     struct cmd_item *ti;
2171     afs_int32 hostAddr;
2172     struct hostent *thp;
2173     char *tp;
2174     int setp;
2175     
2176     ti = as->parms[0].items;
2177     setp = 1;
2178     if (ti) {
2179         thp = hostutil_GetHostByName(ti->data);
2180         if (!thp) {
2181             fprintf(stderr, "host %s not found in host table.\n", ti->data);
2182             return 1;
2183         }
2184         else memcpy(&hostAddr, thp->h_addr, sizeof(afs_int32));
2185     } else {
2186         hostAddr = 0;   /* means don't set host */
2187         setp = 0;       /* aren't setting host */
2188     }
2189     
2190     /* now do operation */
2191     blob.in_size = sizeof(afs_int32);
2192     blob.out_size = sizeof(afs_int32);
2193     blob.in = (char *) &hostAddr;
2194     blob.out = (char *) &hostAddr;
2195     
2196     code = pioctl(0, VIOC_CBADDR, &blob, 1);
2197     if (code < 0) {
2198         Die(errno, 0);
2199         return 1;
2200     }
2201     return 0;
2202 }
2203
2204 static int
2205 NukeNFSCredsCmd(struct cmd_syndesc *as, char *arock)
2206 {
2207     afs_int32 code;
2208     struct ViceIoctl blob;
2209     struct cmd_item *ti;
2210     afs_int32 hostAddr;
2211     struct hostent *thp;
2212     
2213     ti = as->parms[0].items;
2214     thp = hostutil_GetHostByName(ti->data);
2215     if (!thp) {
2216         fprintf(stderr, "host %s not found in host table.\n", ti->data);
2217         return 1;
2218     }
2219     else memcpy(&hostAddr, thp->h_addr, sizeof(afs_int32));
2220     
2221     /* now do operation */
2222     blob.in_size = sizeof(afs_int32);
2223     blob.out_size = sizeof(afs_int32);
2224     blob.in = (char *) &hostAddr;
2225     blob.out = (char *) &hostAddr;
2226     
2227     code = pioctl(0, VIOC_NFS_NUKE_CREDS, &blob, 1);
2228     if (code < 0) {
2229         Die(errno, 0);
2230         return 1;
2231     }
2232     return 0;
2233 }
2234
2235 static int
2236 NewCellCmd(struct cmd_syndesc *as, char *arock)
2237 {
2238     afs_int32 code, linkedstate = 0, size = 0, *lp;
2239     struct ViceIoctl blob;
2240     struct cmd_item *ti;
2241     char *tp, *cellname = 0;
2242     struct hostent *thp;
2243     afs_int32 fsport = 0, vlport = 0;
2244     afs_int32 magic, scount;    /* Number of servers to pass in pioctl call */
2245
2246     /* Yuck!
2247      * With the NEWCELL pioctl call, 3.4 clients take an array of
2248      * MAXHOSTS (13) servers while 3.5 clients take an array of
2249      * MAXCELLHOSTS (8) servers. To determine which we are talking to,
2250      * do a GETCELL pioctl and pass it a magic number. If an array of
2251      * 8 comes back, its a 3.5 client. If not, its a 3.4 client.
2252      * If we get back EDOM, there are no cells in the kernel yet,
2253      * and we'll assume a 3.5 client.
2254      */
2255     tp = space;
2256     lp = (afs_int32 *) tp;
2257     *lp++ = 0;                  /* first cell entry */
2258     *lp = 0x12345678;           /* magic */
2259     blob.out_size = MAXSIZE;
2260     blob.in_size = sizeof(afs_int32) + sizeof(afs_int32);
2261     blob.in = space;
2262     blob.out = space;
2263     code = pioctl(0, VIOCGETCELL, &blob, 1);
2264     if (code < 0 && errno != EDOM) {
2265         Die(errno, 0);
2266         return 1;
2267     }
2268     if (code < 1 && errno == EDOM) {
2269         scount = MAXHOSTS;
2270     } else {
2271         tp = space;
2272         cellname = tp + MAXCELLHOSTS * sizeof(afs_int32);
2273         scount = ((cellname[0] != '\0') ? MAXCELLHOSTS : MAXHOSTS);
2274     }
2275
2276     /* Now setup and do the NEWCELL pioctl call */
2277     memset(space, 0, (scount + 1) * sizeof(afs_int32));
2278     tp = space;
2279     lp = (afs_int32 *) tp;
2280     *lp++ = 0x12345678;
2281     tp += sizeof(afs_int32);
2282     for (ti = as->parms[1].items; ti; ti = ti->next) {
2283         thp = hostutil_GetHostByName(ti->data);
2284         if (!thp) {
2285             fprintf(stderr,
2286                     "%s: Host %s not found in host table, skipping it.\n", pn,
2287                     ti->data);
2288         } else {
2289             memcpy(tp, thp->h_addr, sizeof(afs_int32));
2290             tp += sizeof(afs_int32);
2291         }
2292     }
2293     if (as->parms[2].items) {
2294         /*
2295          * Link the cell, for the purposes of volume location, to the specified
2296          * cell.
2297          */
2298         cellname = as->parms[2].items->data;
2299         linkedstate = 1;
2300     }
2301 #ifdef FS_ENABLE_SERVER_DEBUG_PORTS
2302     if (as->parms[3].items) {
2303         code = util_GetInt32(as->parms[3].items->data, &vlport);
2304         if (code) {
2305             fprintf(stderr,
2306                     "%s: bad integer specified for the fileserver port.\n",
2307                     pn);
2308             return 1;
2309         }
2310     }
2311     if (as->parms[4].items) {
2312         code = util_GetInt32(as->parms[4].items->data, &fsport);
2313         if (code) {
2314             fprintf(stderr,
2315                     "%s: bad integer specified for the vldb server port.\n",
2316                     pn);
2317             return 1;
2318         }
2319     }
2320 #endif
2321     tp = (char *)(space + (scount + 1) * sizeof(afs_int32));
2322     lp = (afs_int32 *) tp;
2323     *lp++ = fsport;
2324     *lp++ = vlport;
2325     *lp = linkedstate;
2326     strcpy(space + ((scount + 4) * sizeof(afs_int32)),
2327            as->parms[0].items->data);
2328     size = ((scount + 4) * sizeof(afs_int32))
2329         + strlen(as->parms[0].items->data)
2330         + 1 /* for null */ ;
2331     tp = (char *)(space + size);
2332     if (linkedstate) {
2333         strcpy(tp, cellname);
2334         size += strlen(cellname) + 1;
2335     }
2336     blob.in_size = size;
2337     blob.in = space;
2338     blob.out_size = 0;
2339     code = pioctl(0, VIOCNEWCELL, &blob, 1);
2340     if (code < 0) {
2341         Die(errno, 0);
2342         return 1;
2343     }
2344     return 0;
2345 }
2346
2347 static int
2348 NewAliasCmd(struct cmd_syndesc *as, char *arock)
2349 {
2350     afs_int32 code;
2351     struct ViceIoctl blob;
2352     char *tp;
2353     char *aliasName, *realName;
2354
2355     /* Now setup and do the NEWCELL pioctl call */
2356     aliasName = as->parms[0].items->data;
2357     realName = as->parms[1].items->data;
2358     tp = space;
2359     strcpy(tp, aliasName);
2360     tp += strlen(aliasName) + 1;
2361     strcpy(tp, realName);
2362     tp += strlen(realName) + 1;
2363
2364     blob.in_size = tp - space;
2365     blob.in = space;
2366     blob.out_size = 0;
2367     blob.out = space;
2368     code = pioctl(0, VIOC_NEWALIAS, &blob, 1);
2369     if (code < 0) {
2370         if (errno == EEXIST) {
2371             fprintf(stderr,
2372                     "%s: cell name `%s' in use by an existing cell.\n", pn,
2373                     aliasName);
2374         } else {
2375             Die(errno, 0);
2376         }
2377         return 1;
2378     }
2379     return 0;
2380 }
2381
2382 static int
2383 WhichCellCmd(struct cmd_syndesc *as, char *arock)
2384 {
2385     afs_int32 code;
2386     struct cmd_item *ti;
2387     int error = 0;
2388     char cell[MAXCELLCHARS];
2389
2390     SetDotDefault(&as->parms[0].items);
2391     for (ti = as->parms[0].items; ti; ti = ti->next) {
2392         code = GetCell(ti->data, cell);
2393         if (code) {
2394             if (errno == ENOENT)
2395                 fprintf(stderr, "%s: no such cell as '%s'\n", pn, ti->data);
2396             else
2397                 Die(errno, ti->data);
2398             error = 1;
2399             continue;
2400         }
2401
2402         printf("File %s lives in cell '%s'\n", ti->data, cell);
2403     }
2404     return error;
2405 }
2406
2407 static int
2408 WSCellCmd(struct cmd_syndesc *as, char *arock)
2409 {
2410     afs_int32 code;
2411     struct ViceIoctl blob;
2412
2413     blob.in_size = 0;
2414     blob.in = NULL;
2415     blob.out_size = MAXSIZE;
2416     blob.out = space;
2417
2418     code = pioctl(NULL, VIOC_GET_WS_CELL, &blob, 1);
2419     if (code) {
2420         Die(errno, NULL);
2421         return 1;
2422     }
2423
2424     printf("This workstation belongs to cell '%s'\n", space);
2425     return 0;
2426 }
2427
2428 /*
2429 static PrimaryCellCmd(as)
2430     struct cmd_syndesc *as;
2431 {
2432     fprintf(stderr, "This command is obsolete, as is the concept of a primary token.\n");
2433     return 0;
2434 }
2435 */
2436
2437 static int
2438 MonitorCmd(struct cmd_syndesc *as, char *arock)
2439 {
2440     afs_int32 code;
2441     struct ViceIoctl blob;
2442     struct cmd_item *ti;
2443     afs_int32 hostAddr;
2444     struct hostent *thp;
2445     char *tp;
2446     int setp;
2447
2448     ti = as->parms[0].items;
2449     setp = 1;
2450     if (ti) {
2451         /* set the host */
2452         if (!strcmp(ti->data, "off"))
2453             hostAddr = 0xffffffff;
2454         else {
2455             thp = hostutil_GetHostByName(ti->data);
2456             if (!thp) {
2457                 if (!strcmp(ti->data, "localhost")) {
2458                     fprintf(stderr,
2459                             "localhost not in host table, assuming 127.0.0.1\n");
2460                     hostAddr = htonl(0x7f000001);
2461                 } else {
2462                     fprintf(stderr, "host %s not found in host table.\n",
2463                             ti->data);
2464                     return 1;
2465                 }
2466             } else
2467                 memcpy(&hostAddr, thp->h_addr, sizeof(afs_int32));
2468         }
2469     } else {
2470         hostAddr = 0;           /* means don't set host */
2471         setp = 0;               /* aren't setting host */
2472     }
2473
2474     /* now do operation */
2475     blob.in_size = sizeof(afs_int32);
2476     blob.out_size = sizeof(afs_int32);
2477     blob.in = (char *)&hostAddr;
2478     blob.out = (char *)&hostAddr;
2479     code = pioctl(0, VIOC_AFS_MARINER_HOST, &blob, 1);
2480     if (code) {
2481         Die(errno, 0);
2482         return 1;
2483     }
2484     if (setp) {
2485         printf("%s: new monitor host set.\n", pn);
2486     } else {
2487         /* now decode old address */
2488         if (hostAddr == 0xffffffff) {
2489             printf("Cache monitoring is currently disabled.\n");
2490         } else {
2491             tp = hostutil_GetNameByINet(hostAddr);
2492             printf("Using host %s for monitor services.\n", tp);
2493         }
2494     }
2495     return 0;
2496 }
2497
2498 static int
2499 SysNameCmd(struct cmd_syndesc *as, char *arock)
2500 {
2501     afs_int32 code;
2502     struct ViceIoctl blob;
2503     struct cmd_item *ti;
2504     char *input = space;
2505     afs_int32 setp = 0;
2506
2507     ti = as->parms[0].items;
2508     blob.in = space;
2509     blob.out = space;
2510     blob.out_size = MAXSIZE;
2511     blob.in_size = sizeof(afs_int32);
2512     input += sizeof(afs_int32);
2513     for (; ti; ti = ti->next) {
2514         setp++;
2515         blob.in_size += strlen(ti->data) + 1;
2516         if (blob.in_size > MAXSIZE) {
2517             fprintf(stderr, "%s: sysname%s too long.\n", pn,
2518                     setp > 1 ? "s" : "");
2519             return 1;
2520         }
2521         strcpy(input, ti->data);
2522         input += strlen(ti->data);
2523         *(input++) = '\0';
2524     }
2525     memcpy(space, &setp, sizeof(afs_int32));
2526     code = pioctl(0, VIOC_AFS_SYSNAME, &blob, 1);
2527     if (code) {
2528         Die(errno, 0);
2529         return 1;
2530     }
2531     if (setp) {
2532         printf("%s: new sysname%s set.\n", pn, setp > 1 ? " list" : "");
2533         return 0;
2534     }
2535     input = space;
2536     memcpy(&setp, input, sizeof(afs_int32));
2537     input += sizeof(afs_int32);
2538     if (!setp) {
2539         fprintf(stderr, "No sysname name value was found\n");
2540         return 1;
2541     }
2542     printf("Current sysname%s is", setp > 1 ? " list" : "");
2543     for (; setp > 0; --setp) {
2544         printf(" \'%s\'", input);
2545         input += strlen(input) + 1;
2546     }
2547     printf("\n");
2548     return 0;
2549 }
2550
2551 static char *exported_types[] = { "null", "nfs", "" };
2552 static int
2553 ExportAfsCmd(struct cmd_syndesc *as, char *arock)
2554 {
2555     afs_int32 code;
2556     struct ViceIoctl blob;
2557     struct cmd_item *ti;
2558     int export = 0, type = 0, mode = 0, exp = 0, exportcall, pwsync =
2559         0, smounts = 0, clipags = 0, pagcb = 0;
2560
2561     ti = as->parms[0].items;
2562     if (strcmp(ti->data, "nfs") == 0)
2563         type = 0x71;            /* NFS */
2564     else {
2565         fprintf(stderr,
2566                 "Invalid exporter type, '%s', Only the 'nfs' exporter is currently supported\n",
2567                 ti->data);
2568         return 1;
2569     }
2570     ti = as->parms[1].items;
2571     if (ti) {
2572         if (strcmp(ti->data, "on") == 0)
2573             export = 3;
2574         else if (strcmp(ti->data, "off") == 0)
2575             export = 2;
2576         else {
2577             fprintf(stderr, "Illegal argument %s\n", ti->data);
2578             return 1;
2579         }
2580         exp = 1;
2581     }
2582     if (ti = as->parms[2].items) {      /* -noconvert */
2583         if (strcmp(ti->data, "on") == 0)
2584             mode = 2;
2585         else if (strcmp(ti->data, "off") == 0)
2586             mode = 3;
2587         else {
2588             fprintf(stderr, "Illegal argument %s\n", ti->data);
2589             return 1;
2590         }
2591     }
2592     if (ti = as->parms[3].items) {      /* -uidcheck */
2593         if (strcmp(ti->data, "on") == 0)
2594             pwsync = 3;
2595         else if (strcmp(ti->data, "off") == 0)
2596             pwsync = 2;
2597         else {
2598             fprintf(stderr, "Illegal argument %s\n", ti->data);
2599             return 1;
2600         }
2601     }
2602     if (ti = as->parms[4].items) {      /* -submounts */
2603         if (strcmp(ti->data, "on") == 0)
2604             smounts = 3;
2605         else if (strcmp(ti->data, "off") == 0)
2606             smounts = 2;
2607         else {
2608             fprintf(stderr, "Illegal argument %s\n", ti->data);
2609             return 1;
2610         }
2611     }
2612     if (ti = as->parms[5].items) {      /* -clipags */
2613         if (strcmp(ti->data, "on") == 0)
2614             clipags = 3;
2615         else if (strcmp(ti->data, "off") == 0)
2616             clipags = 2;
2617         else {
2618             fprintf(stderr, "Illegal argument %s\n", ti->data);
2619             return 1;
2620         }
2621     }
2622     if (ti = as->parms[6].items) {      /* -pagcb */
2623         if (strcmp(ti->data, "on") == 0)
2624             pagcb = 3;
2625         else if (strcmp(ti->data, "off") == 0)
2626             pagcb = 2;
2627         else {
2628             fprintf(stderr, "Illegal argument %s\n", ti->data);
2629             return 1;
2630         }
2631     }
2632     exportcall =
2633         (type << 24) | (pagcb << 10) | (clipags << 8) |
2634         (mode << 6) | (pwsync << 4) | (smounts << 2) | export;
2635     type &= ~0x70;
2636     /* make the call */
2637     blob.in = (char *)&exportcall;
2638     blob.in_size = sizeof(afs_int32);
2639     blob.out = (char *)&exportcall;
2640     blob.out_size = sizeof(afs_int32);
2641     code = pioctl(0, VIOC_EXPORTAFS, &blob, 1);
2642     if (code) {
2643         if (errno == ENODEV) {
2644             fprintf(stderr,
2645                     "Sorry, the %s-exporter type is currently not supported on this AFS client\n",
2646                     exported_types[type]);
2647         } else {
2648             Die(errno, 0);
2649         }
2650         return 1;
2651     }
2652
2653     if (exportcall & 1) {
2654         printf("'%s' translator is enabled with the following options:\n",
2655                exported_types[type]);
2656         printf("\tRunning in %s mode\n",
2657                (exportcall & 2 ? "strict unix" :
2658                 "convert owner mode bits to world/other"));
2659         printf("\tRunning in %s mode\n",
2660                (exportcall & 4 ? "strict 'passwd sync'" :
2661                 "no 'passwd sync'"));
2662         printf("\t%s\n",
2663                (exportcall & 8 ? "Allow mounts of /afs/.. subdirs" :
2664                 "Only mounts to /afs allowed"));
2665         printf("\t%s\n",
2666                (exportcall & 16 ? "Client-assigned PAG's are used" :
2667                 "Client-assigned PAG's are not used"));
2668         printf("\t%s\n",
2669                (exportcall & 32 ?
2670                 "Callbacks are made to get creds from new clients" :
2671                 "Callbacks are not made to get creds from new clients"));
2672     } else {
2673         printf("'%s' translator is disabled\n", exported_types[type]);
2674     }
2675     return 0;
2676 }
2677
2678
2679 static int
2680 GetCellCmd(struct cmd_syndesc *as, char *arock)
2681 {
2682     afs_int32 code;
2683     struct ViceIoctl blob;
2684     struct afsconf_cell info;
2685     struct cmd_item *ti;
2686     struct a {
2687         afs_int32 stat;
2688         afs_int32 junk;
2689     } args;
2690     int error = 0;
2691
2692     memset(&args, '\0', sizeof args);   /* avoid Purify UMR error */
2693     for (ti = as->parms[0].items; ti; ti = ti->next) {
2694         /* once per cell */
2695         blob.out_size = sizeof(args);
2696         blob.out = (caddr_t) & args;
2697         code = GetCellName(ti->data, &info);
2698         if (code) {
2699             error = 1;
2700             continue;
2701         }
2702         blob.in_size = 1 + strlen(info.name);
2703         blob.in = info.name;
2704         code = pioctl(0, VIOC_GETCELLSTATUS, &blob, 1);
2705         if (code) {
2706             if (errno == ENOENT)
2707                 fprintf(stderr, "%s: the cell named '%s' does not exist\n",
2708                         pn, info.name);
2709             else
2710                 Die(errno, info.name);
2711             error = 1;
2712             continue;
2713         }
2714         printf("Cell %s status: ", info.name);
2715 #ifdef notdef
2716         if (args.stat & 1)
2717             printf("primary ");
2718 #endif
2719         if (args.stat & 2)
2720             printf("no setuid allowed");
2721         else
2722             printf("setuid allowed");
2723         if (args.stat & 4)
2724             printf(", using old VLDB");
2725         printf("\n");
2726     }
2727     return error;
2728 }
2729
2730 static int
2731 SetCellCmd(struct cmd_syndesc *as, char *arock)
2732 {
2733     afs_int32 code;
2734     struct ViceIoctl blob;
2735     struct afsconf_cell info;
2736     struct cmd_item *ti;
2737     struct a {
2738         afs_int32 stat;
2739         afs_int32 junk;
2740         char cname[64];
2741     } args;
2742     int error = 0;
2743
2744     /* Check arguments. */
2745     if (as->parms[1].items && as->parms[2].items) {
2746         fprintf(stderr, "Cannot specify both -suid and -nosuid.\n");
2747         return 1;
2748     }
2749
2750     /* figure stuff to set */
2751     args.stat = 0;
2752     args.junk = 0;
2753
2754     if (!as->parms[1].items)
2755         args.stat |= 2;         /* default to -nosuid */
2756
2757     /* set stat for all listed cells */
2758     for (ti = as->parms[0].items; ti; ti = ti->next) {
2759         /* once per cell */
2760         code = GetCellName(ti->data, &info);
2761         if (code) {
2762             error = 1;
2763             continue;
2764         }
2765         strcpy(args.cname, info.name);
2766         blob.in_size = sizeof(args);
2767         blob.in = (caddr_t) & args;
2768         blob.out_size = 0;
2769         blob.out = (caddr_t) 0;
2770         code = pioctl(0, VIOC_SETCELLSTATUS, &blob, 1);
2771         if (code) {
2772             Die(errno, info.name);      /* XXX added cell name to Die() call */
2773             error = 1;
2774         }
2775     }
2776     return error;
2777 }
2778
2779 static int
2780 GetCellName(char *cellName, struct afsconf_cell *info)
2781 {
2782     struct afsconf_dir *tdir;
2783     int code;
2784
2785     tdir = afsconf_Open(AFSDIR_CLIENT_ETC_DIRPATH);
2786     if (!tdir) {
2787         fprintf(stderr,
2788                 "Could not process files in configuration directory (%s).\n",
2789                 AFSDIR_CLIENT_ETC_DIRPATH);
2790         return -1;
2791     }
2792
2793     code = afsconf_GetCellInfo(tdir, cellName, AFSCONF_VLDBSERVICE, info);
2794     if (code) {
2795         fprintf(stderr, "%s: cell %s not in %s\n", pn, cellName,
2796                 AFSDIR_CLIENT_CELLSERVDB_FILEPATH);
2797         return code;
2798     }
2799
2800     return 0;
2801 }
2802
2803
2804 static int
2805 VLDBInit(int noAuthFlag, struct afsconf_cell *info)
2806 {
2807     afs_int32 code;
2808
2809     code = ugen_ClientInit(noAuthFlag, AFSDIR_CLIENT_ETC_DIRPATH, 
2810                            info->name, 0, &uclient, 
2811                            NULL, pn, rxkad_clear,
2812                            VLDB_MAXSERVERS, AFSCONF_VLDBSERVICE, 50,
2813                            0, 0, USER_SERVICE_ID);
2814     rxInitDone = 1;
2815     return code;
2816 }
2817
2818 static struct ViceIoctl gblob;
2819 static int debug = 0;
2820 /*
2821  * here follow some routines in suport of the setserverprefs and
2822  * getserverprefs commands.  They are:
2823  * SetPrefCmd  "top-level" routine
2824  * addServer   adds a server to the list of servers to be poked into the
2825  *             kernel.  Will poke the list into the kernel if it threatens
2826  *             to get too large.
2827  * pokeServers pokes the existing list of servers and ranks into the kernel
2828  * GetPrefCmd  reads the Cache Manager's current list of server ranks
2829  */
2830
2831 /*
2832  * returns -1 if error message printed,
2833  * 0 on success,
2834  * errno value if error and no error message printed
2835  */
2836 static int
2837 pokeServers(void)
2838 {
2839     int code;
2840
2841     code = pioctl(0, VIOC_SETSPREFS, &gblob, 1);
2842     if (code && (errno == EINVAL)) {
2843         struct setspref *ssp;
2844         ssp = (struct setspref *)gblob.in;
2845         if (!(ssp->flags & DBservers)) {
2846             gblob.in = (void *)&(ssp->servers[0]);
2847             gblob.in_size -= ((char *)&(ssp->servers[0])) - (char *)ssp;
2848             code = pioctl(0, VIOC_SETSPREFS33, &gblob, 1);
2849             return code ? errno : 0;
2850         }
2851         fprintf(stderr,
2852                 "This cache manager does not support VL server preferences.\n");
2853         return -1;
2854     }
2855
2856     return code ? errno : 0;
2857 }
2858
2859 /*
2860  * returns -1 if error message printed,
2861  * 0 on success,
2862  * errno value if error and no error message printed
2863  */
2864 static int
2865 addServer(char *name, afs_int32 rank)
2866 {
2867     int t, code;
2868     struct setspref *ssp;
2869     struct spref *sp;
2870     struct hostent *thostent;
2871     afs_uint32 addr;
2872     int error = 0;
2873
2874 #ifndef MAXUSHORT
2875 #ifdef MAXSHORT
2876 #define MAXUSHORT ((unsigned short) 2*MAXSHORT+1)       /* assumes two's complement binary system */
2877 #else
2878 #define MAXUSHORT ((unsigned short) ~0)
2879 #endif
2880 #endif
2881
2882     thostent = hostutil_GetHostByName(name);
2883     if (!thostent) {
2884         fprintf(stderr, "%s: couldn't resolve name.\n", name);
2885         return -1;
2886     }
2887
2888     ssp = (struct setspref *)(gblob.in);
2889
2890     for (t = 0; thostent->h_addr_list[t]; t++) {
2891         if (gblob.in_size > MAXINSIZE - sizeof(struct spref)) {
2892             code = pokeServers();
2893             if (code)
2894                 error = code;
2895             ssp->num_servers = 0;
2896         }
2897
2898         sp = (struct spref *)(gblob.in + gblob.in_size);
2899         memcpy(&(sp->server.s_addr), thostent->h_addr_list[t],
2900                sizeof(afs_uint32));
2901         sp->rank = (rank > MAXUSHORT ? MAXUSHORT : rank);
2902         gblob.in_size += sizeof(struct spref);
2903         ssp->num_servers++;
2904
2905         if (debug)
2906             fprintf(stderr, "adding server %s, rank %d, ip addr 0x%lx\n",
2907                     name, sp->rank, sp->server.s_addr);
2908     }
2909
2910     return error;
2911 }
2912
2913
2914 static int
2915 SetPrefCmd(struct cmd_syndesc *as, char *arock)
2916 {
2917     FILE *infd;
2918     afs_int32 code;
2919     struct cmd_item *ti;
2920     char name[80];
2921     afs_int32 rank;
2922     struct setspref *ssp;
2923     int error = 0;              /* -1 means error message printed,
2924                                  * >0 means errno value for unprinted message */
2925
2926     ssp = (struct setspref *)space;
2927     ssp->flags = 0;
2928     ssp->num_servers = 0;
2929     gblob.in_size = ((char *)&(ssp->servers[0])) - (char *)ssp;
2930     gblob.in = space;
2931     gblob.out = space;
2932     gblob.out_size = MAXSIZE;
2933
2934
2935     if (geteuid()) {
2936         fprintf(stderr, "Permission denied: requires root access.\n");
2937         return 1;
2938     }
2939
2940     ti = as->parms[2].items;    /* -file */
2941     if (ti) {
2942         if (debug)
2943             fprintf(stderr, "opening file %s\n", ti->data);
2944         if (!(infd = fopen(ti->data, "r"))) {
2945             perror(ti->data);
2946             error = -1;
2947         } else {
2948             while (fscanf(infd, "%79s%ld", name, &rank) != EOF) {
2949                 code = addServer(name, (unsigned short)rank);
2950                 if (code)
2951                     error = code;
2952             }
2953         }
2954     }
2955
2956     ti = as->parms[3].items;    /* -stdin */
2957     if (ti) {
2958         while (scanf("%79s%ld", name, &rank) != EOF) {
2959             code = addServer(name, (unsigned short)rank);
2960             if (code)
2961                 error = code;
2962         }
2963     }
2964
2965     for (ti = as->parms[0].items; ti; ti = ti->next) {  /* list of servers, ranks */
2966         if (ti) {
2967             if (!ti->next) {
2968                 break;
2969             }
2970             code = addServer(ti->data, (unsigned short)atol(ti->next->data));
2971             if (code)
2972                 error = code;
2973             if (debug)
2974                 printf("set fs prefs %s %s\n", ti->data, ti->next->data);
2975             ti = ti->next;
2976         }
2977     }
2978     code = pokeServers();
2979     if (code)
2980         error = code;
2981     if (debug)
2982         printf("now working on vlservers, code=%d\n", code);
2983
2984     ssp = (struct setspref *)space;
2985     ssp->flags = DBservers;
2986     ssp->num_servers = 0;
2987     gblob.in_size = ((char *)&(ssp->servers[0])) - (char *)ssp;
2988     gblob.in = space;
2989
2990     for (ti = as->parms[1].items; ti; ti = ti->next) {  /* list of dbservers, ranks */
2991         if (ti) {
2992             if (!ti->next) {
2993                 break;
2994             }
2995             code = addServer(ti->data, (unsigned short)atol(ti->next->data));
2996             if (code)
2997                 error = code;
2998             if (debug)
2999                 printf("set vl prefs %s %s\n", ti->data, ti->next->data);
3000             ti = ti->next;
3001         }
3002     }
3003
3004     if (as->parms[1].items) {
3005         if (debug)
3006             printf("now poking vlservers\n");
3007         code = pokeServers();
3008         if (code)
3009             error = code;
3010     }
3011
3012     if (error > 0)
3013         Die(error, 0);
3014
3015     return error ? 1 : 0;
3016 }
3017
3018
3019
3020 static int
3021 GetPrefCmd(struct cmd_syndesc *as, char *arock)
3022 {
3023     afs_int32 code;
3024     struct cmd_item *ti;
3025     char *name, tbuffer[20];
3026     afs_int32 rank, addr;
3027     FILE *outfd;
3028     int resolve;
3029     int vlservers = 0;
3030     struct ViceIoctl blob;
3031     struct sprefrequest *in;
3032     struct sprefinfo *out;
3033     int i;
3034
3035     ti = as->parms[0].items;    /* -file */
3036     if (ti) {
3037         if (debug)
3038             fprintf(stderr, "opening file %s\n", ti->data);
3039         if (!(outfd = freopen(ti->data, "w", stdout))) {
3040             perror(ti->data);
3041             return 1;
3042         }
3043     }
3044
3045     ti = as->parms[1].items;    /* -numeric */
3046     resolve = !(ti);
3047     ti = as->parms[2].items;    /* -vlservers */
3048     vlservers |= (ti ? DBservers : 0);
3049     /*  ti = as->parms[3].items;   -cell */
3050
3051     in = (struct sprefrequest *)space;
3052     in->offset = 0;
3053
3054     do {
3055         blob.in_size = sizeof(struct sprefrequest);
3056         blob.in = (char *)in;
3057         blob.out = space;
3058         blob.out_size = MAXSIZE;
3059
3060         in->num_servers =
3061             (MAXSIZE - 2 * sizeof(short)) / sizeof(struct spref);
3062         in->flags = vlservers;
3063
3064         code = pioctl(0, VIOC_GETSPREFS, &blob, 1);
3065         if (code) {
3066             perror("getserverprefs pioctl");
3067             return 1;
3068         }
3069
3070         out = (struct sprefinfo *)blob.out;
3071
3072         for (i = 0; i < out->num_servers; i++) {
3073             if (resolve) {
3074                 name = hostutil_GetNameByINet(out->servers[i].server.s_addr);
3075             } else {
3076                 addr = ntohl(out->servers[i].server.s_addr);
3077                 sprintf(tbuffer, "%d.%d.%d.%d", (addr >> 24) & 0xff,
3078                         (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
3079                 name = tbuffer;
3080             }
3081             printf("%-50s %5u\n", name, out->servers[i].rank);
3082         }
3083
3084         in->offset = out->next_offset;
3085     } while (out->next_offset > 0);
3086
3087     return 0;
3088 }
3089
3090 static int
3091 StoreBehindCmd(struct cmd_syndesc *as, char *arock)
3092 {
3093     afs_int32 code = 0;
3094     struct ViceIoctl blob;
3095     struct cmd_item *ti;
3096     struct sbstruct tsb, tsb2;
3097     int verbose = 0;
3098     afs_int32 allfiles;
3099     char *t;
3100     int error = 0;
3101
3102     tsb.sb_thisfile = -1;
3103     ti = as->parms[0].items;    /* -kbytes */
3104     if (ti) {
3105         if (!as->parms[1].items) {
3106             fprintf(stderr, "%s: you must specify -files with -kbytes.\n",
3107                     pn);
3108             return 1;
3109         }
3110         tsb.sb_thisfile = strtol(ti->data, &t, 10) * 1024;
3111         if ((tsb.sb_thisfile < 0) || (t != ti->data + strlen(ti->data))) {
3112             fprintf(stderr, "%s: %s must be 0 or a positive number.\n", pn,
3113                     ti->data);
3114             return 1;
3115         }
3116     }
3117
3118     allfiles = tsb.sb_default = -1;     /* Don't set allfiles yet */
3119     ti = as->parms[2].items;    /* -allfiles */
3120     if (ti) {
3121         allfiles = strtol(ti->data, &t, 10) * 1024;
3122         if ((allfiles < 0) || (t != ti->data + strlen(ti->data))) {
3123             fprintf(stderr, "%s: %s must be 0 or a positive number.\n", pn,
3124                     ti->data);
3125             return 1;
3126         }
3127     }
3128
3129     /* -verbose or -file only or no options */
3130     if (as->parms[3].items || (as->parms[1].items && !as->parms[0].items)
3131         || (!as->parms[0].items && !as->parms[1].items
3132             && !as->parms[2].items))
3133         verbose = 1;
3134
3135     blob.in = (char *)&tsb;
3136     blob.out = (char *)&tsb2;
3137     blob.in_size = blob.out_size = sizeof(struct sbstruct);
3138     memset(&tsb2, 0, sizeof(tsb2));
3139
3140     /* once per -file */
3141     for (ti = as->parms[1].items; ti; ti = ti->next) {
3142         /* Do this solely to see if the file is there */
3143         code = pioctl(ti->data, VIOCWHEREIS, &blob, 1);
3144         if (code) {
3145             Die(errno, ti->data);
3146             error = 1;
3147             continue;
3148         }
3149
3150         code = pioctl(ti->data, VIOC_STORBEHIND, &blob, 1);
3151         if (code) {
3152             Die(errno, ti->data);
3153             error = 1;
3154             continue;
3155         }
3156
3157         if (verbose && (blob.out_size == sizeof(tsb2))) {
3158             if (tsb2.sb_thisfile == -1) {
3159                 fprintf(stdout, "Will store %s according to default.\n",
3160                         ti->data);
3161             } else {
3162                 fprintf(stdout,
3163                         "Will store up to %d kbytes of %s asynchronously.\n",
3164                         (tsb2.sb_thisfile / 1024), ti->data);
3165             }
3166         }
3167     }
3168
3169     /* If no files - make at least one pioctl call, or
3170      * set the allfiles default if we need to.
3171      */
3172     if (!as->parms[1].items || (allfiles != -1)) {
3173         tsb.sb_default = allfiles;
3174         code = pioctl(0, VIOC_STORBEHIND, &blob, 1);
3175         if (code) {
3176             Die(errno, ((allfiles == -1) ? 0 : "-allfiles"));
3177             error = 1;
3178         }
3179     }
3180
3181     /* Having no arguments also reports the default store asynchrony */
3182     if (verbose && (blob.out_size == sizeof(tsb2))) {
3183         fprintf(stdout, "Default store asynchrony is %d kbytes.\n",
3184                 (tsb2.sb_default / 1024));
3185     }
3186
3187     return error;
3188 }
3189
3190
3191 static afs_int32
3192 SetCryptCmd(struct cmd_syndesc *as, char *arock)
3193 {
3194     afs_int32 code = 0, flag;
3195     struct ViceIoctl blob;
3196     char *tp;
3197
3198     tp = as->parms[0].items->data;
3199     if (strcmp(tp, "on") == 0)
3200         flag = 1;
3201     else if (strcmp(tp, "off") == 0)
3202         flag = 0;
3203     else {
3204         fprintf(stderr, "%s: %s must be \"on\" or \"off\".\n", pn, tp);
3205         return EINVAL;
3206     }
3207
3208     blob.in = (char *)&flag;
3209     blob.in_size = sizeof(flag);
3210     blob.out_size = 0;
3211     code = pioctl(0, VIOC_SETRXKCRYPT, &blob, 1);
3212     if (code)
3213         Die(errno, NULL);
3214     return 0;
3215 }
3216
3217
3218 static afs_int32
3219 GetCryptCmd(struct cmd_syndesc *as, char *arock)
3220 {
3221     afs_int32 code = 0, flag;
3222     struct ViceIoctl blob;
3223     char *tp;
3224
3225     blob.in = NULL;
3226     blob.in_size = 0;
3227     blob.out_size = sizeof(flag);
3228     blob.out = space;
3229
3230     code = pioctl(0, VIOC_GETRXKCRYPT, &blob, 1);
3231
3232     if (code)
3233         Die(errno, NULL);
3234     else {
3235         tp = space;
3236         memcpy(&flag, tp, sizeof(afs_int32));
3237         printf("Security level is currently ");
3238         if (flag == 1)
3239             printf("crypt (data security).\n");
3240         else
3241             printf("clear.\n");
3242     }
3243     return 0;
3244 }
3245
3246 #ifdef AFS_DISCON_ENV
3247 static char *modenames[] = {
3248     "discon",
3249     "fetchonly",
3250     "partial",
3251     "nat",
3252     "full",
3253     NULL
3254 };
3255
3256 static afs_int32
3257 DisconCmd(struct cmd_syndesc *as, char *arock)
3258 {
3259     struct cmd_item *ti;
3260     char *modename;
3261     int modelen;
3262     afs_int32 mode, code;
3263     struct ViceIoctl blob;
3264
3265     blob.in = NULL;
3266     blob.in_size = 0;
3267
3268     ti = as->parms[0].items;
3269     if (ti) {
3270         modename = ti->data;
3271         modelen = strlen(modename);
3272         for (mode = 0; modenames[mode] != NULL; mode++)
3273             if (!strncasecmp(modename, modenames[mode], modelen))
3274                 break;
3275         if (modenames[mode] == NULL)
3276             printf("Unknown discon mode \"%s\"\n", modename);
3277         else {
3278             memcpy(space, &mode, sizeof mode);
3279             blob.in = space;
3280             blob.in_size = sizeof mode;
3281         }
3282     }
3283
3284     blob.out_size = sizeof(mode);
3285     blob.out = space;
3286     code = pioctl(0, VIOC_DISCON, &blob, 1);
3287     if (code)
3288         Die(errno, NULL);
3289     else {
3290         memcpy(&mode, space, sizeof mode);
3291         if (mode < sizeof modenames / sizeof (char *))
3292             printf("Discon mode is now \"%s\"\n", modenames[mode]);
3293         else
3294             printf("Unknown discon mode %d\n", mode);
3295     }
3296
3297     return 0;
3298 }
3299 #endif
3300
3301 #include "AFS_component_version_number.c"
3302
3303 int
3304 main(int argc, char **argv)
3305 {
3306     afs_int32 code;
3307     struct cmd_syndesc *ts;
3308
3309 #ifdef  AFS_AIX32_ENV
3310     /*
3311      * The following signal action for AIX is necessary so that in case of a
3312      * crash (i.e. core is generated) we can include the user's data section
3313      * in the core dump. Unfortunately, by default, only a partial core is
3314      * generated which, in many cases, isn't too useful.
3315      */
3316     struct sigaction nsa;
3317
3318     sigemptyset(&nsa.sa_mask);
3319     nsa.sa_handler = SIG_DFL;
3320     nsa.sa_flags = SA_FULLDUMP;
3321     sigaction(SIGSEGV, &nsa, NULL);
3322 #endif
3323
3324     /* try to find volume location information */
3325     ts = cmd_CreateSyntax("getclientaddrs", GetClientAddrsCmd, 0,
3326                           "get client network interface addresses");
3327     cmd_CreateAlias(ts, "gc");
3328
3329     ts = cmd_CreateSyntax("setclientaddrs", SetClientAddrsCmd, 0,
3330                           "set client network interface addresses");
3331     cmd_AddParm(ts, "-address", CMD_LIST, CMD_OPTIONAL | CMD_EXPANDS,
3332                 "client network interfaces");
3333     cmd_CreateAlias(ts, "sc");
3334
3335     ts = cmd_CreateSyntax("setserverprefs", SetPrefCmd, 0,
3336                           "set server ranks");
3337     cmd_AddParm(ts, "-servers", CMD_LIST, CMD_OPTIONAL | CMD_EXPANDS,
3338                 "fileserver names and ranks");
3339     cmd_AddParm(ts, "-vlservers", CMD_LIST, CMD_OPTIONAL | CMD_EXPANDS,
3340                 "VL server names and ranks");
3341     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL,
3342                 "input from named file");
3343     cmd_AddParm(ts, "-stdin", CMD_FLAG, CMD_OPTIONAL, "input from stdin");
3344     cmd_CreateAlias(ts, "sp");
3345
3346     ts = cmd_CreateSyntax("getserverprefs", GetPrefCmd, 0,
3347                           "get server ranks");
3348     cmd_AddParm(ts, "-file", CMD_SINGLE, CMD_OPTIONAL,
3349                 "output to named file");
3350     cmd_AddParm(ts, "-numeric", CMD_FLAG, CMD_OPTIONAL, "addresses only");
3351     cmd_AddParm(ts, "-vlservers", CMD_FLAG, CMD_OPTIONAL, "VL servers");
3352 /*    cmd_AddParm(ts, "-cell", CMD_FLAG, CMD_OPTIONAL, "cellname"); */
3353     cmd_CreateAlias(ts, "gp");
3354
3355     ts = cmd_CreateSyntax("setacl", SetACLCmd, 0, "set access control list");
3356     cmd_AddParm(ts, "-dir", CMD_LIST, 0, "directory");
3357     cmd_AddParm(ts, "-acl", CMD_LIST, 0, "access list entries");
3358     cmd_AddParm(ts, "-clear", CMD_FLAG, CMD_OPTIONAL, "clear access list");
3359     cmd_AddParm(ts, "-negative", CMD_FLAG, CMD_OPTIONAL,
3360                 "apply to negative rights");
3361     parm_setacl_id = ts->nParms;
3362     cmd_AddParm(ts, "-id", CMD_FLAG, CMD_OPTIONAL,
3363                 "initial directory acl (DFS only)");
3364     cmd_AddParm(ts, "-if", CMD_FLAG, CMD_OPTIONAL,
3365                 "initial file acl (DFS only)");
3366     cmd_CreateAlias(ts, "sa");
3367
3368     ts = cmd_CreateSyntax("listacl", ListACLCmd, 0,
3369                           "list access control list");
3370     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3371     parm_listacl_id = ts->nParms;
3372     cmd_AddParm(ts, "-id", CMD_FLAG, CMD_OPTIONAL, "initial directory acl");
3373     cmd_AddParm(ts, "-if", CMD_FLAG, CMD_OPTIONAL, "initial file acl");
3374     cmd_CreateAlias(ts, "la");
3375
3376     ts = cmd_CreateSyntax("getcalleraccess", GetCallerAccess, 0,
3377             "list callers access");
3378     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3379     cmd_CreateAlias(ts, "gca");
3380
3381     ts = cmd_CreateSyntax("cleanacl", CleanACLCmd, 0,
3382                           "clean up access control list");
3383     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3384
3385     ts = cmd_CreateSyntax("copyacl", CopyACLCmd, 0,
3386                           "copy access control list");
3387     cmd_AddParm(ts, "-fromdir", CMD_SINGLE, 0,
3388                 "source directory (or DFS file)");
3389     cmd_AddParm(ts, "-todir", CMD_LIST, 0,
3390                 "destination directory (or DFS file)");
3391     cmd_AddParm(ts, "-clear", CMD_FLAG, CMD_OPTIONAL,
3392                 "first clear dest access list");
3393     parm_copyacl_id = ts->nParms;
3394     cmd_AddParm(ts, "-id", CMD_FLAG, CMD_OPTIONAL, "initial directory acl");
3395     cmd_AddParm(ts, "-if", CMD_FLAG, CMD_OPTIONAL, "initial file acl");
3396
3397     cmd_CreateAlias(ts, "ca");
3398
3399     ts = cmd_CreateSyntax("flush", FlushCmd, 0, "flush file from cache");
3400     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3401     ts = cmd_CreateSyntax("flushmount", FlushMountCmd, 0,
3402                           "flush mount symlink from cache");
3403     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3404
3405     ts = cmd_CreateSyntax("setvol", SetVolCmd, 0, "set volume status");
3406     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3407     cmd_AddParm(ts, "-max", CMD_SINGLE, CMD_OPTIONAL,
3408                 "disk space quota in 1K units");
3409 #ifdef notdef
3410     cmd_AddParm(ts, "-min", CMD_SINGLE, CMD_OPTIONAL,
3411                 "disk space guaranteed");
3412     cmd_AddParm(ts, "-motd", CMD_SINGLE, CMD_OPTIONAL, "message of the day");
3413 #endif
3414     cmd_AddParm(ts, "-offlinemsg", CMD_SINGLE, CMD_OPTIONAL,
3415                 "offline message");
3416     cmd_CreateAlias(ts, "sv");
3417
3418     ts = cmd_CreateSyntax("messages", MessagesCmd, 0,
3419                           "control Cache Manager messages");
3420     cmd_AddParm(ts, "-show", CMD_SINGLE, CMD_OPTIONAL,
3421                 "[user|console|all|none]");
3422
3423     ts = cmd_CreateSyntax("examine", ExamineCmd, 0, "display file/volume status");
3424     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3425     cmd_CreateAlias(ts, "lv");
3426     cmd_CreateAlias(ts, "listvol");
3427
3428     ts = cmd_CreateSyntax("listquota", ListQuotaCmd, 0, "list volume quota");
3429     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3430     cmd_CreateAlias(ts, "lq");
3431
3432     ts = cmd_CreateSyntax("diskfree", DiskFreeCmd, 0,
3433                           "show server disk space usage");
3434     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3435     cmd_CreateAlias(ts, "df");
3436
3437     ts = cmd_CreateSyntax("quota", QuotaCmd, 0, "show volume quota usage");
3438     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3439
3440     ts = cmd_CreateSyntax("lsmount", ListMountCmd, 0, "list mount point");
3441     cmd_AddParm(ts, "-dir", CMD_LIST, 0, "directory");
3442
3443     ts = cmd_CreateSyntax("mkmount", MakeMountCmd, 0, "make mount point");
3444     cmd_AddParm(ts, "-dir", CMD_SINGLE, 0, "directory");
3445     cmd_AddParm(ts, "-vol", CMD_SINGLE, 0, "volume name");
3446     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
3447     cmd_AddParm(ts, "-rw", CMD_FLAG, CMD_OPTIONAL, "force r/w volume");
3448     cmd_AddParm(ts, "-fast", CMD_FLAG, CMD_OPTIONAL,
3449                 "don't check name with VLDB");
3450
3451 /*
3452
3453 defect 3069
3454
3455     cmd_AddParm(ts, "-root", CMD_FLAG, CMD_OPTIONAL, "create cellular mount point");
3456 */
3457
3458
3459     ts = cmd_CreateSyntax("rmmount", RemoveMountCmd, 0, "remove mount point");
3460     cmd_AddParm(ts, "-dir", CMD_LIST, 0, "directory");
3461
3462     ts = cmd_CreateSyntax("checkservers", CheckServersCmd, 0,
3463                           "check local cell's servers");
3464     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell to check");
3465     cmd_AddParm(ts, "-all", CMD_FLAG, CMD_OPTIONAL, "check all cells");
3466     cmd_AddParm(ts, "-fast", CMD_FLAG, CMD_OPTIONAL,
3467                 "just list, don't check");
3468     cmd_AddParm(ts, "-interval", CMD_SINGLE, CMD_OPTIONAL,
3469                 "seconds between probes");
3470
3471     ts = cmd_CreateSyntax("checkvolumes", CheckVolumesCmd, 0,
3472                           "check volumeID/name mappings");
3473     cmd_CreateAlias(ts, "checkbackups");
3474
3475
3476     ts = cmd_CreateSyntax("setcachesize", SetCacheSizeCmd, 0,
3477                           "set cache size");
3478     cmd_AddParm(ts, "-blocks", CMD_SINGLE, CMD_OPTIONAL,
3479                 "size in 1K byte blocks (0 => reset)");
3480     cmd_CreateAlias(ts, "cachesize");
3481
3482     cmd_AddParm(ts, "-reset", CMD_FLAG, CMD_OPTIONAL,
3483                 "reset size back to boot value");
3484
3485     ts = cmd_CreateSyntax("getcacheparms", GetCacheParmsCmd, 0,
3486                           "get cache usage info");
3487     cmd_AddParm(ts, "-files", CMD_FLAG, CMD_OPTIONAL, "Show cach files used as well");
3488     cmd_AddParm(ts, "-excessive", CMD_FLAG, CMD_OPTIONAL, "excessively verbose cache stats");
3489
3490     ts = cmd_CreateSyntax("listcells", ListCellsCmd, 0,
3491                           "list configured cells");
3492     cmd_AddParm(ts, "-numeric", CMD_FLAG, CMD_OPTIONAL, "addresses only");
3493
3494     ts = cmd_CreateSyntax("listaliases", ListAliasesCmd, 0,
3495                           "list configured cell aliases");
3496
3497     ts = cmd_CreateSyntax("setquota", SetQuotaCmd, 0, "set volume quota");
3498     cmd_AddParm(ts, "-path", CMD_SINGLE, CMD_OPTIONAL, "dir/file path");
3499     cmd_AddParm(ts, "-max", CMD_SINGLE, 0, "max quota in kbytes");
3500 #ifdef notdef
3501     cmd_AddParm(ts, "-min", CMD_SINGLE, CMD_OPTIONAL, "min quota in kbytes");
3502 #endif
3503     cmd_CreateAlias(ts, "sq");
3504
3505     ts = cmd_CreateSyntax("newcell", NewCellCmd, 0, "configure new cell");
3506     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "cell name");
3507     cmd_AddParm(ts, "-servers", CMD_LIST, CMD_REQUIRED, "primary servers");
3508     cmd_AddParm(ts, "-linkedcell", CMD_SINGLE, CMD_OPTIONAL,
3509                 "linked cell name");
3510
3511     ts = cmd_CreateSyntax("newalias", NewAliasCmd, 0,
3512                           "configure new cell alias");
3513     cmd_AddParm(ts, "-alias", CMD_SINGLE, 0, "alias name");
3514     cmd_AddParm(ts, "-name", CMD_SINGLE, 0, "real name of cell");
3515
3516 #ifdef FS_ENABLE_SERVER_DEBUG_PORTS
3517 /*
3518  * Turn this on only if you wish to be able to talk to a server which is listening
3519  * on alternative ports. This is not intended for general use and may not be
3520  * supported in the cache manager. It is not a way to run two servers at the
3521  * same host, since the cache manager cannot properly distinguish those two hosts.
3522  */
3523     cmd_AddParm(ts, "-fsport", CMD_SINGLE, CMD_OPTIONAL,
3524                 "cell's fileserver port");
3525     cmd_AddParm(ts, "-vlport", CMD_SINGLE, CMD_OPTIONAL,
3526                 "cell's vldb server port");
3527 #endif
3528
3529     ts = cmd_CreateSyntax("whichcell", WhichCellCmd, 0, "list file's cell");
3530     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3531
3532     ts = cmd_CreateSyntax("whereis", WhereIsCmd, 0, "list file's location");
3533     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3534
3535     ts = cmd_CreateSyntax("wscell", WSCellCmd, 0, "list workstation's cell");
3536
3537 /*
3538     ts = cmd_CreateSyntax("primarycell", PrimaryCellCmd, 0, "obsolete (listed primary cell)");
3539 */
3540
3541     /* set cache monitor host address */
3542     ts = cmd_CreateSyntax("monitor", MonitorCmd, 0, (char *)CMD_HIDDEN);
3543     cmd_AddParm(ts, "-server", CMD_SINGLE, CMD_OPTIONAL,
3544                 "host name or 'off'");
3545     cmd_CreateAlias(ts, "mariner");
3546
3547     ts = cmd_CreateSyntax("getcellstatus", GetCellCmd, 0, "get cell status");
3548     cmd_AddParm(ts, "-cell", CMD_LIST, 0, "cell name");
3549
3550     ts = cmd_CreateSyntax("setcell", SetCellCmd, 0, "set cell status");
3551     cmd_AddParm(ts, "-cell", CMD_LIST, 0, "cell name");
3552     cmd_AddParm(ts, "-suid", CMD_FLAG, CMD_OPTIONAL, "allow setuid programs");
3553     cmd_AddParm(ts, "-nosuid", CMD_FLAG, CMD_OPTIONAL,
3554                 "disallow setuid programs");
3555
3556     ts = cmd_CreateSyntax("flushvolume", FlushVolumeCmd, 0,
3557                           "flush all data in volume");
3558     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3559
3560     ts = cmd_CreateSyntax("sysname", SysNameCmd, 0,
3561                           "get/set sysname (i.e. @sys) value");
3562     cmd_AddParm(ts, "-newsys", CMD_LIST, CMD_OPTIONAL, "new sysname");
3563
3564     ts = cmd_CreateSyntax("exportafs", ExportAfsCmd, 0,
3565                           "enable/disable translators to AFS");
3566     cmd_AddParm(ts, "-type", CMD_SINGLE, 0, "exporter name");
3567     cmd_AddParm(ts, "-start", CMD_SINGLE, CMD_OPTIONAL,
3568                 "start/stop translator (on | off)");
3569     cmd_AddParm(ts, "-convert", CMD_SINGLE, CMD_OPTIONAL,
3570                 "convert from afs to unix mode (on | off)");
3571     cmd_AddParm(ts, "-uidcheck", CMD_SINGLE, CMD_OPTIONAL,
3572                 "run on strict 'uid check' mode (on | off)");
3573     cmd_AddParm(ts, "-submounts", CMD_SINGLE, CMD_OPTIONAL,
3574                 "allow nfs mounts to subdirs of /afs/.. (on  | off)");
3575     cmd_AddParm(ts, "-clipags", CMD_SINGLE, CMD_OPTIONAL,
3576                 "enable use of client-assigned PAG's (on  | off)");
3577     cmd_AddParm(ts, "-pagcb", CMD_SINGLE, CMD_OPTIONAL,
3578                 "enable callbacks to get creds from new clients (on  | off)");
3579
3580
3581     ts = cmd_CreateSyntax("storebehind", StoreBehindCmd, 0,
3582                           "store to server after file close");
3583     cmd_AddParm(ts, "-kbytes", CMD_SINGLE, CMD_OPTIONAL,
3584                 "asynchrony for specified names");
3585     cmd_AddParm(ts, "-files", CMD_LIST, CMD_OPTIONAL, "specific pathnames");
3586     cmd_AddParm(ts, "-allfiles", CMD_SINGLE, CMD_OPTIONAL,
3587                 "new default (KB)");
3588     cmd_AddParm(ts, "-verbose", CMD_FLAG, CMD_OPTIONAL, "show status");
3589     cmd_CreateAlias(ts, "sb");
3590
3591     ts = cmd_CreateSyntax("setcrypt", SetCryptCmd, 0,
3592                           "set cache manager encryption flag");
3593     cmd_AddParm(ts, "-crypt", CMD_SINGLE, 0, "on or off");
3594
3595     ts = cmd_CreateSyntax("getcrypt", GetCryptCmd, 0,
3596                           "get cache manager encryption flag");
3597
3598     ts = cmd_CreateSyntax("rxstatproc", RxStatProcCmd, 0,
3599                           "Manage per process RX statistics");
3600     cmd_AddParm(ts, "-enable", CMD_FLAG, CMD_OPTIONAL, "Enable RX stats");
3601     cmd_AddParm(ts, "-disable", CMD_FLAG, CMD_OPTIONAL, "Disable RX stats");
3602     cmd_AddParm(ts, "-clear", CMD_FLAG, CMD_OPTIONAL, "Clear RX stats");
3603
3604     ts = cmd_CreateSyntax("rxstatpeer", RxStatPeerCmd, 0,
3605                           "Manage per peer RX statistics");
3606     cmd_AddParm(ts, "-enable", CMD_FLAG, CMD_OPTIONAL, "Enable RX stats");
3607     cmd_AddParm(ts, "-disable", CMD_FLAG, CMD_OPTIONAL, "Disable RX stats");
3608     cmd_AddParm(ts, "-clear", CMD_FLAG, CMD_OPTIONAL, "Clear RX stats");
3609
3610     ts = cmd_CreateSyntax("setcbaddr", CallBackRxConnCmd, 0, "configure callback connection address");
3611     cmd_AddParm(ts, "-addr", CMD_SINGLE, CMD_OPTIONAL, "host name or address");
3612
3613     /* try to find volume location information */
3614     ts = cmd_CreateSyntax("getfid", GetFidCmd, 0,
3615                           "get fid for file(s)");
3616     cmd_AddParm(ts, "-path", CMD_LIST, CMD_OPTIONAL, "dir/file path");
3617
3618 #ifdef AFS_DISCON_ENV
3619     ts = cmd_CreateSyntax("discon", DisconCmd, 0,
3620                           "disconnection mode");
3621     cmd_AddParm(ts, "-mode", CMD_SINGLE, CMD_OPTIONAL, "nat | full");
3622 #endif
3623
3624     ts = cmd_CreateSyntax("nukenfscreds", NukeNFSCredsCmd, 0, "nuke credentials for NFS client");
3625     cmd_AddParm(ts, "-addr", CMD_SINGLE, 0, "host name or address");
3626
3627     code = cmd_Dispatch(argc, argv);
3628     if (rxInitDone)
3629         rx_Finalize();
3630
3631     return code;
3632 }
3633
3634 static void
3635 Die(int errnum, char *filename)
3636 {
3637     switch (errnum) {
3638     case EINVAL:
3639         if (filename)
3640             fprintf(stderr,
3641                     "%s: Invalid argument; it is possible that %s is not in AFS.\n",
3642                     pn, filename);
3643         else
3644             fprintf(stderr, "%s: Invalid argument.\n", pn);
3645         break;
3646     case ENOENT:
3647         if (filename)
3648             fprintf(stderr, "%s: File '%s' doesn't exist\n", pn, filename);
3649         else
3650             fprintf(stderr, "%s: no such file returned\n", pn);
3651         break;
3652     case EROFS:
3653         fprintf(stderr,
3654                 "%s: You can not change a backup or readonly volume\n", pn);
3655         break;
3656     case EACCES:
3657     case EPERM:
3658         if (filename)
3659             fprintf(stderr,
3660                     "%s: You don't have the required access rights on '%s'\n",
3661                     pn, filename);
3662         else
3663             fprintf(stderr,
3664                     "%s: You do not have the required rights to do this operation\n",
3665                     pn);
3666         break;
3667     default:
3668         if (filename)
3669             fprintf(stderr, "%s:'%s'", pn, filename);
3670         else
3671             fprintf(stderr, "%s", pn);
3672         fprintf(stderr, ": %s\n", error_message(errnum));
3673         break;
3674     }
3675 }
3676
3677 /* get clients interface addresses */
3678 static int
3679 GetClientAddrsCmd(struct cmd_syndesc *as, char *arock)
3680 {
3681     afs_int32 code;
3682     struct cmd_item *ti;
3683     char *name;
3684     struct ViceIoctl blob;
3685     struct sprefrequest *in;
3686     struct sprefinfo *out;
3687
3688     in = (struct sprefrequest *)space;
3689     in->offset = 0;
3690
3691     do {
3692         blob.in_size = sizeof(struct sprefrequest);
3693         blob.in = (char *)in;
3694         blob.out = space;
3695         blob.out_size = MAXSIZE;
3696
3697         in->num_servers =
3698             (MAXSIZE - 2 * sizeof(short)) / sizeof(struct spref);
3699         /* returns addr in network byte order */
3700         code = pioctl(0, VIOC_GETCPREFS, &blob, 1);
3701         if (code) {
3702             perror("getClientInterfaceAddr pioctl");
3703             return 1;
3704         }
3705
3706         {
3707             int i;
3708             out = (struct sprefinfo *)blob.out;
3709             for (i = 0; i < out->num_servers; i++) {
3710                 afs_int32 addr;
3711                 char tbuffer[32];
3712                 addr = ntohl(out->servers[i].server.s_addr);
3713                 sprintf(tbuffer, "%d.%d.%d.%d", (addr >> 24) & 0xff,
3714                         (addr >> 16) & 0xff, (addr >> 8) & 0xff, addr & 0xff);
3715                 printf("%-50s\n", tbuffer);
3716             }
3717             in->offset = out->next_offset;
3718         }
3719     } while (out->next_offset > 0);
3720
3721     return 0;
3722 }
3723
3724 static int
3725 SetClientAddrsCmd(struct cmd_syndesc *as, char *arock)
3726 {
3727     afs_int32 code, addr;
3728     struct cmd_item *ti;
3729     char name[80];
3730     struct ViceIoctl blob;
3731     struct setspref *ssp;
3732     int sizeUsed = 0, i, flag;
3733     afs_int32 existingAddr[1024];       /* existing addresses on this host */
3734     int existNu;
3735     int error = 0;
3736
3737     ssp = (struct setspref *)space;
3738     ssp->num_servers = 0;
3739     blob.in = space;
3740     blob.out = space;
3741     blob.out_size = MAXSIZE;
3742
3743     if (geteuid()) {
3744         fprintf(stderr, "Permission denied: requires root access.\n");
3745         return 1;
3746     }
3747
3748     /* extract all existing interface addresses */
3749     existNu = rx_getAllAddr(existingAddr, 1024);
3750     if (existNu < 0)
3751         return 1;
3752
3753     sizeUsed = sizeof(struct setspref); /* space used in ioctl buffer */
3754     for (ti = as->parms[0].items; ti; ti = ti->next) {
3755         if (sizeUsed >= sizeof(space)) {
3756             fprintf(stderr, "No more space\n");
3757             return 1;
3758         }
3759         addr = extractAddr(ti->data, 20);       /* network order */
3760         if ((addr == AFS_IPINVALID) || (addr == AFS_IPINVALIDIGNORE)) {
3761             fprintf(stderr, "Error in specifying address: %s..ignoring\n",
3762                     ti->data);
3763             error = 1;
3764             continue;
3765         }
3766         /* see if it is an address that really exists */
3767         for (flag = 0, i = 0; i < existNu; i++)
3768             if (existingAddr[i] == addr) {
3769                 flag = 1;
3770                 break;
3771             }
3772         if (!flag) {            /* this is an nonexistent address */
3773             fprintf(stderr, "Nonexistent address: 0x%08x..ignoring\n", addr);
3774             error = 1;
3775             continue;
3776         }
3777         /* copy all specified addr into ioctl buffer */
3778         (ssp->servers[ssp->num_servers]).server.s_addr = addr;
3779         printf("Adding 0x%08x\n", addr);
3780         ssp->num_servers++;
3781         sizeUsed += sizeof(struct spref);
3782     }
3783     if (ssp->num_servers < 1) {
3784         fprintf(stderr, "No addresses specified\n");
3785         return 1;
3786     }
3787     blob.in_size = sizeUsed - sizeof(struct spref);
3788
3789     code = pioctl(0, VIOC_SETCPREFS, &blob, 1); /* network order */
3790     if (code) {
3791         Die(errno, 0);
3792         error = 1;
3793     }
3794
3795     return error;
3796 }
3797
3798 static int
3799 FlushMountCmd(struct cmd_syndesc *as, char *arock)
3800 {
3801     afs_int32 code;
3802     struct ViceIoctl blob;
3803     struct cmd_item *ti;
3804     char orig_name[1024];       /*Original name, may be modified */
3805     char true_name[1024];       /*``True'' dirname (e.g., symlink target) */
3806     char parent_dir[1024];      /*Parent directory of true name */
3807     char *last_component;       /*Last component of true name */
3808     struct stat statbuff;       /*Buffer for status info */
3809     int link_chars_read;        /*Num chars read in readlink() */
3810     int thru_symlink;           /*Did we get to a mount point via a symlink? */
3811     int error = 0;
3812
3813     for (ti = as->parms[0].items; ti; ti = ti->next) {
3814         /* once per file */
3815         thru_symlink = 0;
3816         sprintf(orig_name, "%s%s", (ti->data[0] == '/') ? "" : "./",
3817                 ti->data);
3818
3819         if (lstat(orig_name, &statbuff) < 0) {
3820             /* if lstat fails, we should still try the pioctl, since it
3821              * may work (for example, lstat will fail, but pioctl will
3822              * work if the volume of offline (returning ENODEV). */
3823             statbuff.st_mode = S_IFDIR; /* lie like pros */
3824         }
3825
3826         /*
3827          * The lstat succeeded.  If the given file is a symlink, substitute
3828          * the file name with the link name.
3829          */
3830         if ((statbuff.st_mode & S_IFMT) == S_IFLNK) {
3831             thru_symlink = 1;
3832             /*
3833              * Read name of resolved file.
3834              */
3835             link_chars_read = readlink(orig_name, true_name, 1024);
3836             if (link_chars_read <= 0) {
3837                 fprintf(stderr,
3838                         "%s: Can't read target name for '%s' symbolic link!\n",
3839                         pn, orig_name);
3840                 error = 1;
3841                 continue;
3842             }
3843
3844             /*
3845              * Add a trailing null to what was read, bump the length.
3846              */
3847             true_name[link_chars_read++] = 0;
3848
3849             /*
3850              * If the symlink is an absolute pathname, we're fine.  Otherwise, we
3851              * have to create a full pathname using the original name and the
3852              * relative symlink name.  Find the rightmost slash in the original
3853              * name (we know there is one) and splice in the symlink value.
3854              */
3855             if (true_name[0] != '/') {
3856                 last_component = (char *)strrchr(orig_name, '/');
3857                 strcpy(++last_component, true_name);
3858                 strcpy(true_name, orig_name);
3859             }
3860         } else
3861             strcpy(true_name, orig_name);
3862
3863         /*
3864          * Find rightmost slash, if any.
3865          */
3866         last_component = (char *)strrchr(true_name, '/');
3867         if (last_component) {
3868             /*
3869              * Found it.  Designate everything before it as the parent directory,
3870              * everything after it as the final component.
3871              */
3872             strncpy(parent_dir, true_name, last_component - true_name);
3873             parent_dir[last_component - true_name] = 0;
3874             last_component++;   /*Skip the slash */
3875         } else {
3876             /*
3877              * No slash appears in the given file name.  Set parent_dir to the current
3878              * directory, and the last component as the given name.
3879              */
3880             strcpy(parent_dir, ".");
3881             last_component = true_name;
3882         }
3883
3884         if (strcmp(last_component, ".") == 0
3885             || strcmp(last_component, "..") == 0) {
3886             fprintf(stderr,
3887                     "%s: you may not use '.' or '..' as the last component\n",
3888                     pn);
3889             fprintf(stderr, "%s: of a name in the 'fs flushmount' command.\n",
3890                     pn);
3891             error = 1;
3892             continue;
3893         }
3894
3895         blob.in = last_component;
3896         blob.in_size = strlen(last_component) + 1;
3897         blob.out_size = 0;
3898         memset(space, 0, MAXSIZE);
3899
3900         code = pioctl(parent_dir, VIOC_AFS_FLUSHMOUNT, &blob, 1);
3901
3902         if (code != 0) {
3903             if (errno == EINVAL) {
3904                 fprintf(stderr, "'%s' is not a mount point.\n", ti->data);
3905             } else {
3906                 Die(errno, (ti->data ? ti->data : parent_dir));
3907             }
3908             error = 1;
3909         }
3910     }
3911     return error;
3912 }
3913
3914 static int
3915 RxStatProcCmd(struct cmd_syndesc *as, char *arock)
3916 {
3917     afs_int32 code;
3918     afs_int32 flags = 0;
3919     struct ViceIoctl blob;
3920     struct cmd_item *ti;
3921
3922     if (as->parms[0].items) {   /* -enable */
3923         flags |= AFSCALL_RXSTATS_ENABLE;
3924     }
3925     if (as->parms[1].items) {   /* -disable */
3926         flags |= AFSCALL_RXSTATS_DISABLE;
3927     }
3928     if (as->parms[2].items) {   /* -clear */
3929         flags |= AFSCALL_RXSTATS_CLEAR;
3930     }
3931     if (flags == 0) {
3932         fprintf(stderr, "You must specify at least one argument\n");
3933         return 1;
3934     }
3935
3936     blob.in = (char *)&flags;
3937     blob.in_size = sizeof(afs_int32);
3938     blob.out_size = 0;
3939
3940     code = pioctl(NULL, VIOC_RXSTAT_PROC, &blob, 1);
3941     if (code != 0) {
3942         Die(errno, NULL);
3943         return 1;
3944     }
3945
3946     return 0;
3947 }
3948
3949 static int
3950 RxStatPeerCmd(struct cmd_syndesc *as, char *arock)
3951 {
3952     afs_int32 code;
3953     afs_int32 flags = 0;
3954     struct ViceIoctl blob;
3955     struct cmd_item *ti;
3956
3957     if (as->parms[0].items) {   /* -enable */
3958         flags |= AFSCALL_RXSTATS_ENABLE;
3959     }
3960     if (as->parms[1].items) {   /* -disable */
3961         flags |= AFSCALL_RXSTATS_DISABLE;
3962     }
3963     if (as->parms[2].items) {   /* -clear */
3964         flags |= AFSCALL_RXSTATS_CLEAR;
3965     }
3966     if (flags == 0) {
3967         fprintf(stderr, "You must specify at least one argument\n");
3968         return 1;
3969     }
3970
3971     blob.in = (char *)&flags;
3972     blob.in_size = sizeof(afs_int32);
3973     blob.out_size = 0;
3974
3975     code = pioctl(NULL, VIOC_RXSTAT_PEER, &blob, 1);
3976     if (code != 0) {
3977         Die(errno, NULL);
3978         return 1;
3979     }
3980
3981     return 0;
3982 }
3983
3984 static int
3985 GetFidCmd(struct cmd_syndesc *as, char *arock)
3986 {
3987     struct ViceIoctl blob;
3988     struct cmd_item *ti;
3989     for (ti = as->parms[0].items; ti; ti = ti->next) {
3990       struct VenusFid vfid;
3991       
3992       blob.out_size = sizeof(struct VenusFid);
3993       blob.out = (char *) &vfid;
3994       blob.in_size = 0;
3995       
3996       if (0 == pioctl(ti->data, VIOCGETFID, &blob, 1)) {
3997         printf("File %s (%u.%u.%u) contained in volume %u\n",
3998                ti->data, vfid.Fid.Volume, vfid.Fid.Vnode, vfid.Fid.Unique,
3999                vfid.Fid.Volume);
4000       }
4001     }
4002
4003     return 0;
4004 }
4005