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