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