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