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