Initial IBM OpenAFS 1.0 tree
[openafs.git] / src / venus / fstrace.c
1 /*
2  * Copyright (C) 1991 Transarc Corporation
3  * All Rights Reserved
4  */
5 #include <afs/param.h>
6 #include <stdio.h>
7 #include <sys/types.h>
8 #if !defined(AFS_SUN3_ENV) && !defined(sys_vax_ul43)
9 #include <time.h>
10 /*#ifdef        AFS_AIX_ENV*/
11 #include <sys/time.h>
12 /*#endif*/
13 #include <errno.h>
14 #undef abs
15 #include <stdlib.h>
16 #include <afs/stds.h>
17 #include <afs/cmd.h>
18 #include <afs/afs_args.h>
19 #include <afs/icl.h>
20 #include <afs/afsutil.h>
21
22 #if defined(AFS_ALPHA_ENV) || defined(AFS_SGI61_ENV)
23 /* For SGI 6.2, this is changed to 1 if it's a 32 bit kernel. */
24 int afs_icl_sizeofLong = 2;
25 #else
26 int afs_icl_sizeofLong = 1;
27 #endif
28
29 #ifdef AFS_SGI61_ENV
30 int afs_64bit_kernel = 1;       /* Default for 6.2+, and always for 6.1 */
31 extern int afs_icl_sizeofLong;  /* Used in ICL_SIZEHACK() */
32 #ifdef AFS_SGI62_ENV
33 #include <unistd.h>
34
35 /* If _SC_KERN_POINTERS not in sysconf, then we can assume a 32 bit abi. */
36 void set_kernel_sizeof_long(void)
37 {
38     int retval;
39     
40     
41     retval = sysconf(_SC_KERN_POINTERS);
42     if (retval == 64) {
43         afs_64bit_kernel = 1;
44         afs_icl_sizeofLong = 2;
45     }
46     else {
47         afs_64bit_kernel = 0;
48         afs_icl_sizeofLong = 1;
49     }
50 }
51
52 #endif /* AFS_SGI62_ENV */
53 #endif /* AFS_SGI61_ENV */
54
55 #define BUFFER_MULTIPLIER     1024
56
57 /* make it big enough to snapshot everything at once, since
58  * decoding takes so long.
59  */
60 #define IBSIZE          100000  /* default size */
61
62 struct logInfo {
63     struct logInfo *nextp;
64     char *name;
65 } *allInfo = 0;
66
67 char dumpFileName[256] = "";
68 RegisterIclDumpFileName(name)
69 char *name;
70 {
71     (void) sprintf(dumpFileName, "icl.%.250s", name);
72 }
73
74 /* define globals to use for bulk info */
75 afs_icl_bulkSetinfo_t *setInfo = (afs_icl_bulkSetinfo_t *)0;
76 afs_icl_bulkLoginfo_t *logInfo = (afs_icl_bulkLoginfo_t *)0;
77
78 struct afs_icl_set *icl_allSets = 0;
79
80
81 char *name;
82 /* given a type and an address, get the size of the thing
83  * in words.
84  */
85 static icl_GetSize(type, addr)
86   afs_int32 type;
87   char *addr;
88 {
89     int rsize;
90     int tsize;
91
92     rsize = 0;
93     ICL_SIZEHACK(type, addr);
94     return rsize;
95 }
96
97 /* Check types in printf string "bufferp", making sure that each
98  * is compatible with the corresponding parameter type described
99  * by typesp.  Also watch for prematurely running out of parameters
100  * before the string is gone.
101  */
102 #ifdef AFS_SGI61_ENV
103 static int CheckTypes(bufferp, typesp, typeCount, outMsgBuffer)
104   char *bufferp;
105   int *typesp;
106   int typeCount;
107   char *outMsgBuffer;
108 {
109     register char tc;
110     int inPercent;
111     int tix;
112
113     inPercent = 0;
114     tix = 0;
115     for(tc = *bufferp;; outMsgBuffer++, tc = *(++bufferp)) {
116         *outMsgBuffer = tc;
117         if (tc == 0) {
118             /* hit end of string.  We win as long as we aren't
119              * in a '%'.
120              */
121             if (inPercent) return 0;
122             else
123                 return 1;
124         }
125         if (tc == '%') {
126             inPercent = 1 - inPercent;
127             continue;
128         }
129         if (inPercent) {
130             if (tc >= '0' && tc <= '9') {
131                 /* skip digits in % string */
132                 outMsgBuffer--;
133                 continue;
134             }
135             if (tc == 'l') {
136                 /* 'l' is a type modifier. */
137                 outMsgBuffer--;
138                 continue; 
139             }
140             /* otherwise, we've finally gotten to the type-describing
141              * character.  Make sure there's a type descriptor, and then
142              * check the type descriptor.
143              */
144             inPercent = 0;
145             if (tix > typeCount)
146                 return 0;       /* no more type descriptors left */
147             if (tc == 's' ) {
148                 if (typesp[tix] != 1)   /* not a string descriptor */
149                     return 0;
150                 outMsgBuffer--;
151                 *outMsgBuffer = (char)1;
152             }
153             if (tc == 'u' || tc == 'x' || tc == 'd'
154                 || tc == 'o') {
155                 if (typesp[tix] != 0)
156                     return 0;   /* not an integer descriptor */
157                 outMsgBuffer--;
158                 switch (tc) {
159                 case 'd':
160                     *outMsgBuffer = (char)2;
161                     break;
162                 case 'u':
163                     *outMsgBuffer = (char)3;
164                     break;
165                 case 'o':
166                     *outMsgBuffer = (char)4;
167                     break;
168                 case 'x':
169                 default:
170                     *outMsgBuffer = (char)5;
171                     break;
172                 }
173             }
174             /* otherwise we're fine, so eat this descriptor */
175             tix++;
176         }
177     }
178     /* not reached */
179 }
180 #else /* AFS_SGI61_ENV */
181 static CheckTypes(bufferp, typesp, typeCount)
182   char *bufferp;
183   int *typesp;
184   int typeCount;
185 {
186     register char tc;
187     int inPercent;
188     int tix;
189
190     inPercent = 0;
191     tix = 0;
192     for(tc = *bufferp;; tc = *(++bufferp)) {
193         if (tc == 0) {
194             /* hit end of string.  We win as long as we aren't
195              * in a '%'.
196              */
197             if (inPercent) return 0;
198             else return 1;
199         }
200         if (tc == '%') {
201             inPercent = 1 - inPercent;
202             continue;
203         }
204         if (inPercent) {
205             if (tc >= '0' && tc <= '9') continue; /* skip digits in % string */
206             /* otherwise, we've finally gotten to the type-describing
207              * character.  Make sure there's a type descriptor, and then
208              * check the type descriptor.
209              */
210             inPercent = 0;
211             if (tix > typeCount)
212                 return 0;       /* no more type descriptors left */
213             if (tc == 's' && typesp[tix] != 1)  /* not a string descriptor */
214                 return 0;
215             if ((tc == 'u' || tc == 'l' || tc == 'x' || tc == 'd')
216                 && (typesp[tix] != 0))
217                 return 0;       /* not an integer descriptor */
218             /* otherwise we're fine, so eat this descriptor */
219             tix++;
220         }
221     }
222     /* not reached */
223 }
224 #endif /* AFS_SGI61_ENV */
225
226 /* display a single record.
227  * alp points at the first word in the array to be interpreted
228  * rsize gives the # of words in the array
229  */
230 #if defined(AFS_SGI61_ENV) && !defined(AFS_SGI62_ENV)
231 #define uint64_t long long
232 #endif
233 static DisplayRecord(outFilep, alp, rsize)
234   FILE *outFilep;
235   register afs_int32 *alp;
236   afs_int32 rsize;
237 {
238     char msgBuffer[1024];
239 #ifdef AFS_SGI61_ENV
240     char outMsgBuffer[1024];
241     uint64_t tempParam;
242     uint64_t printfParms[ICL_MAXEXPANSION * /* max parms */ 4];
243     char *printfStrings[ICL_MAXEXPANSION  * /* max parms */ 4];
244 #else /* AFS_SGI61_ENV */
245     long printfParms[ICL_MAXEXPANSION * /* max parms */ 4];
246 #endif /* AFS_SGI61_ENV */
247     int printfTypes[ICL_MAXEXPANSION * 4];
248     int i;
249     afs_int32 done = 0;
250     afs_int32 temp;
251     int j;
252     int type;
253     int pix;                    /* index in alp */
254     int pfpix;                  /* index in printfParms */
255     int pftix;                  /* index in printfTypes */
256     int status;
257     int printed;                /* did we print the string yet? */
258     afs_int32 *tlp;
259
260     /* decode parameters */
261     temp = alp[0];      /* type encoded in low-order 24 bits, t0 high */
262     pix = 4;
263     pfpix = 0;
264     pftix = 0;
265     /* init things */
266
267     for(i=0; i<4*ICL_MAXEXPANSION; i++) 
268         printfParms[i] = 0;
269     /* decode each parameter, getting addrs for afs_hyper_t and strings */
270     for(i=0; !done && i<4; i++) {
271         type = (temp >> (18 - i*6)) & 0x3f;
272         switch (type) {
273         case ICL_TYPE_NONE:
274             done = 1;
275             break;
276         case ICL_TYPE_LONG:
277         case ICL_TYPE_POINTER:
278             printfTypes[pftix++] = 0;
279 #ifdef AFS_SGI61_ENV
280             printfParms[pfpix] = alp[pix];
281             printfParms[pfpix] &= 0xffffffff;
282             if (afs_64bit_kernel) {
283                 printfParms[pfpix] <<= 32;
284                 printfParms[pfpix] |= alp[pix+1];
285             }
286 #else /* AFS_SGI61_ENV */
287 #ifdef AFS_ALPHA_ENV
288             printfParms[pfpix] = alp[pix+1];
289             printfParms[pfpix] |= (alp[pix] <<= 32);
290 #else /* AFS_ALPHA_ENV */
291             printfParms[pfpix] = alp[pix];
292 #endif /* AFS_ALPHA_ENV */
293 #endif /* AFS_SGI61_ENV */
294             pfpix++;
295             break;
296         case ICL_TYPE_INT32:
297             printfTypes[pftix++] = 0;
298             printfParms[pfpix++] = alp[pix];
299             break;
300         case ICL_TYPE_HYPER:
301             printfTypes[pftix++] = 0;
302             printfParms[pfpix++] = alp[pix];
303             printfTypes[pftix++] = 0;
304             printfParms[pfpix++] = alp[pix+1];
305             break;
306         case ICL_TYPE_FID:
307             printfTypes[pftix++] = 0;
308             printfParms[pfpix++] = alp[pix];
309             printfTypes[pftix++] = 0;
310             printfParms[pfpix++] = alp[pix+1];
311             printfTypes[pftix++] = 0;
312             printfParms[pfpix++] = alp[pix+2];
313             printfTypes[pftix++] = 0;
314             printfParms[pfpix++] = alp[pix+3];
315             break;
316         case ICL_TYPE_STRING:
317             printfTypes[pftix++] = 1;
318 #ifdef AFS_SGI64_ENV
319             printfStrings[pfpix++] = (char*) &alp[pix];
320 #else /* AFS_SGI64_ENV */
321 #ifdef AFS_SGI61_ENV
322             printfStrings[pfpix++] = (char *) &alp[pix];
323 #else /* AFS_SGI61_ENV */
324             printfParms[pfpix++] = (long) &alp[pix];
325 #endif /* AFS_SGI61_ENV */
326 #endif /* AFS_SGI64_ENV */
327             break;
328         case ICL_TYPE_UNIXDATE:
329             printfParms[pfpix++] = (long) ctime((time_t *)&alp[pix]);
330             break;
331         default:
332             printf("DisplayRecord: Bad type %d in decode switch.\n", type);
333             done = 1;
334             break;
335         }
336         if (done)
337             break;
338
339         pix += icl_GetSize(type, (char *) &alp[pix]);
340     }
341
342     /* next, try to decode the opcode into a printf string */
343     dce1_error_inq_text(alp[1], msgBuffer, &status);
344
345     /* if we got a string back, and it is compatible with the
346      * parms we've got, then print it.
347      */
348     printed = 0;
349     if (status == 0) {
350 #ifdef AFS_SGI61_ENV
351         if (CheckTypes(msgBuffer, printfTypes, pftix, outMsgBuffer)) {
352             /* we have a string to use, but it ends "(dfs / zcm)",
353              * so we remove the extra gunk.
354              */
355             j = strlen(outMsgBuffer);
356             if (j > 12) {
357                 outMsgBuffer[j-11] = 0;
358                 j -= 11;
359             }
360             pfpix = 0;
361             fprintf(outFilep, "time %d.%06d, pid %u: ", alp[3]/1000000,
362                     alp[3] % 1000000, alp[2]);
363             for (i=0; i<j; i++) {
364                 if ((int)outMsgBuffer[i] > 5)
365                     fputc(outMsgBuffer[i], outFilep);
366                 else {
367                     switch (outMsgBuffer[i]) {
368                     case 0: /* done */
369                         break;
370                     case 1: /* string */
371                         fprintf(outFilep, "%s", printfStrings[pfpix++]);
372                         break;
373                     case 2: /* signed integer */
374                         fprintf(outFilep, "%lld", printfParms[pfpix++]);
375                         break;
376                     case 3: /* unsigned integer */
377                         fprintf(outFilep, "%llu", printfParms[pfpix++]);
378                         break;
379                     case 4: /* octal integer */
380                         fprintf(outFilep, "%llo", printfParms[pfpix++]);
381                         break;
382                     case 5: /* hex integer */
383                         fprintf(outFilep, "%llx", printfParms[pfpix++]);
384                         break;
385                     default:
386                         fprintf(outFilep,
387                                 "fstrace: Bad char %d in outMsgBuffer for parm %d\n",
388                                outMsgBuffer[i], pfpix);
389                         fprintf(outFilep, "fstrace: msgBuffer='%s'\n",
390                                 msgBuffer);
391                         break;
392                     }
393                 }
394             }
395             fprintf(outFilep, "\n");
396             printed = 1;
397         }
398 #else /* AFS_SGI61_ENV */
399         if (CheckTypes(msgBuffer, printfTypes, pftix)) {
400             /* we have a string to use, but it ends "(dfs / zcm)",
401              * so we remove the extra gunk.
402              */
403             j = strlen(msgBuffer);
404             if (j > 12) msgBuffer[j-11] = 0;
405             fprintf(outFilep, "time %d.%06d, pid %u: ", alp[3]/1000000,
406                     alp[3] % 1000000, alp[2]);
407             fprintf(outFilep, msgBuffer, printfParms[0], printfParms[1],
408                     printfParms[2], printfParms[3], printfParms[4],
409                     printfParms[5], printfParms[6], printfParms[7],
410                     printfParms[8], printfParms[9], printfParms[10],
411                     printfParms[11], printfParms[12], printfParms[13],
412                     printfParms[14], printfParms[15]);
413             fprintf(outFilep, "\n");
414             printed = 1;
415         }
416 #endif /* AFS_SGI61_ENV */
417         else {
418             fprintf(outFilep, "Type mismatch, using raw print.\n");
419             fprintf(outFilep, "%s", msgBuffer);
420         }
421     }
422     if (!printed) {
423         if (alp[1] == ICL_INFO_TIMESTAMP) {
424             fprintf(outFilep, "time %d.%06d, pid %u: %s\n",
425                 alp[3]/1000000, alp[3] % 1000000, alp[2],
426                 ctime((time_t *)&alp[4]));
427         } else {
428             fprintf(outFilep, "raw op %d, time %d.%06d, pid %u\n",
429                     alp[1], alp[3]/1000000, alp[3] % 1000000, alp[2]);
430             /* now decode each parameter and print it */
431             pix = 4;
432             done = 0;
433             for(i=0; !done && i<4; i++) {
434                 type = (temp >> (18 - i*6)) & 0x3f;
435                 switch (type) {
436                 case ICL_TYPE_NONE:
437                     done = 1;
438                     break;
439                 case ICL_TYPE_INT32:
440                     fprintf(outFilep, "p%d:%d ", i, alp[pix]);
441                     break;
442                 case ICL_TYPE_LONG:
443 #ifdef AFS_SGI61_ENV
444                     tempParam = alp[pix];
445                     tempParam <<= 32;
446                     tempParam |= alp[pix+1];
447                     fprintf(outFilep, "p%d:%lld ", i, tempParam);
448 #else /* AFS_SGI61_ENV */
449                     fprintf(outFilep, "p%d:%d ", i, alp[pix]);
450 #endif /* AFS_SGI61_ENV */
451                     break;
452                 case ICL_TYPE_POINTER:
453 #ifdef AFS_SGI61_ENV
454                     tempParam = alp[pix];
455                     tempParam <<= 32;
456                     tempParam |= alp[pix+1];
457                     fprintf(outFilep, "p%d:0x%llx ", i, tempParam);
458 #else /* AFS_SGI61_ENV */
459                     fprintf(outFilep, "p%d:0x%x ", i, alp[pix]);
460 #endif /* AFS_SGI61_ENV */
461                     break;
462                 case ICL_TYPE_HYPER:
463                     fprintf(outFilep, "p%d:%x.%x ", i, alp[pix], alp[pix+1]);
464                     break;
465                 case ICL_TYPE_FID:
466                     fprintf(outFilep, "p%d:%d.%d.%d.%d ",  i, alp[pix],
467                             alp[pix+1], alp[pix+2], alp[pix+3]);
468                     break;
469                 case ICL_TYPE_STRING:
470                     fprintf(outFilep, "p%d:%s ", i, (char *) &alp[pix]);
471                     break;
472                 case ICL_TYPE_UNIXDATE:
473                     fprintf(outFilep, "p%d:%s ", i, ctime((time_t *)&alp[pix]));
474                     break;
475                 default:
476                     printf("DisplayRecord: Bad type %d in raw print switch.\n",
477                            type);
478                     done = 1;
479                     break;
480                 }
481                 if (done)
482                     break;
483
484                 pix += icl_GetSize(type, (char *) &alp[pix]);
485             }
486         }
487         fprintf(outFilep, "\n");        /* done with line */
488     }
489 }
490
491
492
493 #include <locale.h>
494 #ifdef  AFS_OSF_ENV
495 #include <limits.h>
496 #endif
497 #include <nl_types.h>
498
499 #if     defined(AFS_OSF_ENV) && !defined(AFS_OSF20_ENV)
500 #include <fcntl.h>
501 static nl_catd  catopen1();
502 nl_catd NLcatopen();
503 static nl_catd  _do1_open();
504 static nl_catd cat_already_open();
505 static int  make_sets();
506 static FILE *open1catfile();
507 static void add_open_cat();
508 static void cat_hard_close();
509 extern char *strchr();
510
511 static int  catpid[NL_MAXOPEN];
512 static CATD *catsopen[NL_MAXOPEN];
513 #define PATH_FORMAT     "/usr/lib/nls/msg/%L/%N:/etc/nls/msg/%L/%N"
514 #define DEFAULT_LANG    "C"
515 #define TOO_MANY_HOLES(num_holes, num_non_holes) \
516     (((num_holes) > 100) && ((num_holes) > (num_non_holes)))
517
518 char *rmalloc(n) 
519 int n; 
520
521         /*----  n: the number of bytes to be malloc'ed  ----*/
522 {
523         char *t;
524
525         t = (char *) malloc(n);
526         if (!t)
527             printf("Failed to get mem\n");
528         return(t);
529 }
530 #ifdef  notdef
531 #endif
532 nl_catd catopen1 (cat, dummy)
533 char *cat; 
534 int dummy;
535         /*---- char *cat:  the name of the cat to be opened ----*/
536         /*---- int dummy:  dummy variable  ----*/
537
538 {
539         int errno_save;
540         nl_catd _do_open();         /*---- routine that actually opens 
541                                            the catalog ---- */
542         CATD *catd;
543
544         errno_save = errno;
545
546 /*
547         if (catd = cat_already_open(cat)) {
548                 catd->_count = catd->_count + 1;
549                 return(catd);
550         }
551 */
552         catd = (CATD *)rmalloc (sizeof(CATD));
553         if ( catd == NULL )
554                 return(CATD_ERR);
555         catd->_name = (char *)rmalloc(strlen(cat) + 1);
556         if ( catd->_name == NULL )
557                 return(CATD_ERR);
558         strcpy(catd->_name,cat);
559         catd->_fd = FALSE;
560         catd->_magic = CAT_MAGIC;
561         catd->_mem = FALSE;
562 #ifndef AFS_OSF20_ENV
563         catd->_pid = getpid();
564 #endif
565         catd->_count = 1;
566         if (_do1_open(catd) != CATD_ERR)  
567                 return(catd);
568         else {
569                 free(catd->_name);
570                 free(catd); 
571                 return(CATD_ERR);
572         }
573 }
574
575
576
577 nl_catd _do1_open(catd)
578 nl_catd catd;
579
580         /*---- pointer to the partially set up cat descriptor ----*/
581
582 {
583         int make_sets();        /*---- routine to unpack the sets into 
584                                                 fast acccess mode ----*/
585         void add_open_cat();    /*---- routine to keep a list of 
586                                                opened cats ----*/
587         /*long*/ int magic;
588         int i;                  /*---- Misc counter(s) used for loop */
589         struct _catset cs;
590         int errno_save;
591         int num_holes;
592
593         errno_save = errno;
594
595         catd->_fd = open1catfile( catd->_name );
596         if ( !catd->_fd ) {
597                 return( CATD_ERR );
598         }
599         fread((void *)&magic,(size_t)4,(size_t)1,catd->_fd);
600         if (magic != CAT_MAGIC){
601 printf("Magic was %x instead of %x -> %x\n", magic, CAT_MAGIC, CATD_ERR);
602 /*
603                 fclose(catd->_fd);
604                 catd->_fd = NULL;
605                 return( CATD_ERR );
606 */
607         }
608 /*      if ((catd->_mem = shmat((int)fileno(catd->_fd), (char *)0, SHM_MAP | SHM_RDONLY))
609            == (char * )ERR ) {   */
610
611         if (1) {      /* disable the shmat, share memory segemnt */
612
613 /*______________________________________________________________________
614         If the file can not be mapped then simulate mapping for the index
615         table so that make_sets cat set things up. (rmalloc an area big
616         enough for the index table and read the whole thing in)
617   ______________________________________________________________________*/
618
619                 /* reset the file pointer to the beginning of catalog */
620                 fseek(catd->_fd,(long)0,0);
621
622                 /* malloc the header, if fails return error */
623                 catd->_hd = (struct _header *) rmalloc(sizeof(struct _header));
624                 if ( catd->_hd == NULL )
625                         return(CATD_ERR);
626
627                 /* read in the whole header */
628                 fread((void *)catd->_hd,(size_t)sizeof(struct _header),(size_t)1,catd->_fd);
629
630                 /* cs is a dummpy to hold a set temperorily. The purpose of */
631                 /* this for loop is to fread the whole catalog so that the  */
632                 /* file pointer will be moved to the end of the catalog.    */
633                 for (i = 0 ; i < catd->_hd->_n_sets ; i++) {
634                         fread((void *)&cs,(size_t)4,(size_t)1,catd->_fd);
635                         fseek(catd->_fd, (long)(cs._n_msgs * sizeof(struct _msgptr)),1);
636                 }
637
638                 /* after the for loop, ftell returns the byte offset of the */
639                 /* end of the catalog relative to the begining of the file. */
640                 /* i.e. i contains the byte offset of the whole catalog.    */
641                 i = ftell(catd->_fd);
642
643                 /* malloc _mem as a temp pointer to hold the entire catalog. */
644                 catd->_mem = (char *)rmalloc(i);
645                 if ( catd->_mem == NULL )
646                         return(CATD_ERR);
647
648                 /* reset the file pointer to the begining. */
649                 fseek(catd->_fd,(long)0,0);
650
651                 /* read in the whole catalog into _mem */
652                 fread((void *)catd->_mem,(size_t)i,(size_t)1,catd->_fd);
653
654                 /*
655                  * If there aren't many holes in the set numbers,
656                  * fully expand the compacted set array from the
657                  * catalog.  Then in catgets(), we'll be able to use
658                  * the set number to index directly into the expanded
659                  * array.
660                  *
661                  * If there are a lot of holes, leave the set array
662                  * compacted.  In catgets(), we'll search through it
663                  * for the requested set.
664                  */
665
666                 num_holes = catd->_hd->_setmax - catd->_hd->_n_sets;
667                 if (!TOO_MANY_HOLES(num_holes, catd->_hd->_n_sets)) {
668                         catd->_sets_expanded = TRUE;
669                         catd->_n_sets = catd->_hd->_setmax;
670                 }
671                 else {
672                         catd->_sets_expanded = FALSE;
673                         catd->_n_sets = catd->_hd->_n_sets - 1;
674                 }
675
676                 /* malloc one extra set more than the max. set index */
677                 catd->_set = (struct _catset *) rmalloc((catd->_n_sets+1)* 
678                               sizeof (struct _catset));
679                 if ( catd->_set == NULL )
680                         return(CATD_ERR);
681
682                 /* save the max. set number in catd->_setmax */
683                 catd->_setmax = catd->_hd->_setmax;
684                 /* call make_set to malloc memory for every message */
685                 if(make_sets(catd) == -1)
686                         return (CATD_ERR);
687                 free(catd->_mem);
688                 catd->_mem = FALSE;
689                 add_open_cat(catd);
690                 return(catd);
691         }
692         else {
693
694 /*______________________________________________________________________
695         Normal mapping has occurred, set a few things up and call make_sets
696   ______________________________________________________________________*/
697
698                 catd->_hd =( struct _header * )( catd->_mem );
699                 catd->_setmax = catd->_hd->_setmax;
700                 catd->_set = (struct _catset *) rmalloc((catd->_hd->_setmax+1)*
701                              sizeof (struct _catset));
702                 if ( catd->_set == NULL )
703                         return(CATD_ERR);
704                 if(make_sets(catd) == -1)
705                         return (CATD_ERR);
706                 add_open_cat(catd);
707                 return(catd);
708         }
709 }
710
711
712 static void add_open_cat(catd)
713 nl_catd catd;
714                 /*---- catd to be added to the list of catalogs ----*/
715
716 {
717         int i = 0;      /*---- Misc counter(s) used for loops ----*/
718         while (i < NL_MAXOPEN && catsopen[i]) {
719                 if (!strcmp(catd->_name,catsopen[i]->_name) 
720 #ifndef AFS_OSF20_ENV
721                    && getpid()==catsopen[i]->_pid)
722 #else
723
724                     )
725 #endif
726                         return; /*---- The catalog is already here ----*/
727                 i++;
728         }
729
730         if (i < NL_MAXOPEN) {
731                 catsopen[i] = catd;
732                 catpid[i] = getpid();
733         }
734 }
735
736
737 /*
738  * 
739  * NAME: make_sets
740  *
741  * FUNCTION: Expands the compacted version of the catalog index table into
742  *      the fast access memory version.
743  *
744  * EXECUTION ENVIRONMENT:
745  *
746  *      Make_set executes under a process.      
747  *
748  * RETURNS: int
749  */
750
751
752 static int make_sets(catd)
753 nl_catd catd;
754 {
755         struct _catset *cset;
756         char           *base = catd->_mem;
757         int            n_sets = catd->_hd->_n_sets;
758         int     i;      /*---- Misc counter(s) used for loops ----*/
759         int     j;      /*---- Misc counter(s) used for loops ----*/
760         int     msgmax; /*---- The maximum number of _messages in a set ----*/
761         char    *cmpct_set_ptr; /*---- pointer into the index table ----*/
762         struct _catset  cs;     /*---- used to look at the sets in the table -*/
763         int     num_holes;
764
765         cmpct_set_ptr = base + sizeof(struct _header);
766
767         for (i = 0 ; i < n_sets ; i++) {
768             /* loop through each compacted set */
769
770                 cs = *(struct _catset *)cmpct_set_ptr;  
771                 /* set the _catset ptr to the base of the current 
772                    compacted set.        */
773
774                 cs._mp = (struct _msgptr *)(cmpct_set_ptr +
775                           2 * sizeof(unsigned short));
776                           /* set the ms array ptr to the base of
777                              compacted array of _msgptr's     */
778
779                 cset = (catd->_sets_expanded) ?
780                         &catd->_set[cs._setno] : &catd->_set[i];
781
782                 /*
783                  * If there aren't many holes in the message numbers,
784                  * fully expand the compacted message array from the
785                  * catalog.  Then in catgets(), we'll be able to use
786                  * the message number to index directly into the
787                  * expanded array.
788                  *
789                  * If there are many holes, leave the message array
790                  * compacted.  In catgets(), we'll search through it
791                  * for the requested message.
792                  */
793
794                 msgmax = cs._mp[cs._n_msgs - 1]._msgno;
795                 num_holes = msgmax - cs._n_msgs;
796                 if (!TOO_MANY_HOLES(num_holes, cs._n_msgs)) {
797                         cset->_msgs_expanded = TRUE;
798                         cset->_n_msgs = msgmax;
799                 }
800                 else {
801                         cset->_msgs_expanded = FALSE;
802                         cset->_n_msgs = cs._n_msgs - 1;
803                 }
804
805                 cset->_mp = (struct _msgptr *) rmalloc((1 + cset->_n_msgs) *
806                                                        sizeof(struct _msgptr));
807                 if (cset->_mp == NULL)
808                         return (-1);
809
810                 cset->_msgtxt = (char **) rmalloc((1 + cset->_n_msgs) *
811                                                   sizeof(char *));
812                 if (cset->_msgtxt == NULL)
813                         return (-1);
814
815                 if (cset->_msgs_expanded) {
816                         for (j = 0 ; j < cs._n_msgs ; j++) {
817                                 cset->_mp[cs._mp[j]._msgno] = cs._mp[j];
818                         }
819                 }
820                 else {
821                         for (j = 0 ; j < cs._n_msgs ; j++) {
822                                 cset->_mp[j] = cs._mp[j];
823                         }
824                 }
825
826                 cset->_setno = cs._setno;
827                 /* Superfluous but should have the correct data. Increment 
828                    the base of the set pointer.          */
829
830                 cmpct_set_ptr += 2 * sizeof(unsigned short) + cs._n_msgs *
831                                  sizeof(struct _msgptr);
832         }
833         return(0);
834 }
835
836
837
838 /*
839  * 
840  * NAME: opencatfile
841  *
842  * FUNCTION: Opens a catalog file, looking in the language path first (if 
843  *      there is no slash) and returns a pointer to the file stream.
844  *                                                                    
845  * EXECUTION ENVIRONMENT:
846  *
847  *      Opencatfile executes under a process.   
848  *
849  * RETURNS:  Returns a pointer to the file stream, and a NULL pointer on
850  *      failure.
851  */
852
853 static FILE *open1catfile(file)
854 char *file;
855 {
856         extern char *getenv();
857         char    fl[PATH_MAX];   /*---- place to hold full path ----*/
858         char    *nlspath;       /*---- pointer to the nlspath val ----*/
859         FILE    *fp;            /*---- file pointer ----*/
860         char    cpth[PATH_MAX]; /*---- current value of nlspath ----*/
861         char    *p,*np;
862         char    *fulllang;      /* %L language value */
863         char    lang[PATH_MAX]; /* %l language value */
864         char    *territory;     /* %t language value */
865         char    *codeset;       /* %c language value */
866         char    *ptr;           /* for decompose of $LANG */
867         char *str;
868         char *optr;
869         int nchars;
870         int lenstr;
871         char outptr[PATH_MAX];
872         int valid;
873
874         if (strchr(file,'/')) {
875                 if ((fp = fopen(file,"r")))
876                    {  fcntl(fileno(fp),F_SETFD,1);
877                                 /* set the close-on-exec flag for
878                                     child process                */
879                       return(fp);
880                    }
881         }
882         else {
883                 if (!(nlspath = getenv("NLSPATH")))
884                         nlspath = PATH_FORMAT; 
885                 if (!(fulllang = getenv("LANG"))) 
886                         fulllang = DEFAULT_LANG;
887                 if (fulllang == DEFAULT_LANG)                         
888                         nlspath = PATH_FORMAT;  /* if fullang is C, use the 
889                                                    the default nlspath:    */
890
891                 /*
892                 ** LANG is a composite of three fields:
893                 ** language_territory.codeset
894                 ** and we're going to break it into those
895                 ** three fields.
896                 */
897
898                 strcpy(lang, fulllang);
899
900                 territory = "";
901                 codeset = "";
902
903                 ptr = strchr( lang, '_' );
904                 if (ptr != NULL) {
905                         territory = ptr+1;
906                         *ptr = '\0';
907                         ptr = strchr(territory, '.');
908                         if (ptr != NULL) {
909                                 codeset = ptr+1;
910                                 *ptr = '\0';
911                         }
912                 } else {
913                         ptr = strchr( lang, '.' );
914                         if (ptr != NULL) {
915                                 codeset = ptr+1;
916                                 *ptr = '\0';
917                         }
918                 }
919
920                 np = nlspath;
921                 while (*np) {
922                         p = cpth;
923                         while (*np && *np != ':')
924                                 *p++ = *np++;
925                         *p = '\0';
926                         if (*np)        /*----  iff on a colon then advance --*/
927                                 np++;
928                         valid = 0;
929                         if (strlen(cpth)) {
930                                 ptr = cpth;
931                                 optr = outptr;
932
933                                 nchars = 0;
934                                 while (*ptr != '\0') {
935                                         while ((*ptr != '\0') && (*ptr != '%') 
936                                                       && (nchars < PATH_MAX)) {
937                                                 *(optr++) = *(ptr++);
938                                                 nchars++;
939                                         }
940                                         if (*ptr == '%') {
941                                                 switch (*(++ptr)) {
942                                                         case '%':
943                                                                 str = "%";
944                                                                 break;
945                                                         case 'L':
946                                                                 str = fulllang;
947                                                                 break;
948                                                         case 'N':
949                                                                 valid = 1;
950                                                                 str = file;
951                                                                 break;
952                                                         case 'l':
953                                                                 str = lang;
954                                                                 break;
955                                                         case 't':
956                                                                 str = territory;
957                                                                 break;
958                                                         case 'c':
959                                                                 str = codeset;
960                                                                 break;
961                                                         default:
962                                                                 str = "";
963                                                                 break;
964                                                 }
965                                                 lenstr = strlen(str);
966                                                 nchars += lenstr;
967                                                 if (nchars < PATH_MAX) {
968                                                         strcpy(optr, str);
969                                                         optr += lenstr;
970                                                 } else {        
971                                                         break;
972                                                 } 
973                                                 ptr++;
974                                         } else {
975                                                 if (nchars >= PATH_MAX) {
976                                                         break;
977                                                 }
978                                         }
979                                 }
980                                 *optr = '\0';
981                                 strcpy(cpth, outptr);
982                         }
983                         else {          /*----  iff leading | trailing | 
984                                                 adjacent colons ... --*/
985                                 valid = 1;
986                                 strcpy(cpth,file);
987                         }
988                         if (valid == 1 && (fp = fopen(cpth,"r")))
989                             {  fcntl(fileno(fp),F_SETFD,1);
990                                         /* set the close-on-exec flag for
991                                            child process                */
992                                return(fp);
993                             }
994                 }
995                 if (fp = fopen(file,"r"))
996                    {  fcntl(fileno(fp),F_SETFD,1);
997                                  /* set the close-on-exec flag for
998                                     child process                */
999                        return(fp);
1000                     }
1001         }
1002         return (NULL);
1003 }
1004
1005  
1006
1007  
1008
1009 /*
1010  * 
1011  * NAME: cat_already_open
1012  *
1013  * FUNCTION: Checkes to see if a specific cat has already been opened.
1014  *                                                                    
1015  * EXECUTION ENVIRONMENT:
1016  *
1017  *      Cat_already_open executes under a process.
1018  *
1019  * RETURNS: Returns a pointer to the existing CATD if one exists, and 
1020  *      a NULL pointer if no CATD exists.
1021  */
1022
1023 static nl_catd cat_already_open(cat)
1024 char *cat;
1025                         /*---- name of the catalog to be opened ----*/
1026
1027 {
1028         int i;                  /*---- Misc counter(s) used for loops ----*/
1029         
1030         for (i = 0 ; i < NL_MAXOPEN && catsopen[i] ; i++)  {
1031 #ifndef AFS_OSF20_ENV
1032                 if (!strcmp(cat,catsopen[i]->_name) && getpid()==catsopen[i]->_pid) {
1033 #else
1034                 if (!strcmp(cat,catsopen[i]->_name)) {
1035 #endif
1036                         return(catsopen[i]);
1037                 }
1038         }
1039         return(0);
1040 }
1041
1042
1043 int catclose1(catd)             /*---- the catd to be closed ----*/
1044 nl_catd catd;           /*---- the catd to be closed ----*/
1045
1046 {
1047         int i;
1048
1049
1050         if (catd == CATD_ERR)
1051                 return(-1);
1052         for (i=0; i< NL_MAXOPEN && catsopen[i]; i++) {
1053 #ifndef AFS_OSF20_ENV
1054                 if (catd == catsopen[i] && getpid()==catsopen[i]->_pid)
1055 #else
1056                 if (catd == catsopen[i])
1057 #endif
1058                         break;
1059         }
1060         if (i == NL_MAXOPEN || catsopen[i] == NULL)
1061                 return (-1);
1062         if (catd->_fd == (FILE *)NULL)  
1063                                 /*----  return if this is an extra open or
1064                                         a bad catalog discriptor         ----*/
1065                 return(-1);
1066         if (cat_already_open(catd->_name)) {
1067                 if (catd->_count == 1) {
1068                         cat_hard_close(catd);
1069                         return (0);     /*--- the last legal clsoe ---*/
1070                 }
1071                 else if (catd->_count > 1) {
1072                         catd->_count = catd->_count - 1;
1073                         return(0);      /*--- a legal close ---*/
1074                 }
1075                 else    
1076                         return (-1);    /*--- an extra illegal close ---*/
1077         }
1078         else {
1079                 return(-1);
1080         }
1081 }
1082
1083 static void cat_hard_close(catd)
1084 nl_catd catd;
1085                 /*---- the catd to be closed ----*/
1086
1087 {
1088         int i;                  /*---- Misc counter(s) used for loops ----*/
1089         int j;                  /*----  Misc counter ----*/
1090
1091         if (catd == CATD_ERR)
1092                 return;
1093         
1094 /*______________________________________________________________________
1095         remove any entry for the catalog in the catsopen array
1096   ______________________________________________________________________*/
1097
1098         for (i = 0 ; i < NL_MAXOPEN && catsopen[i] ; i++) {
1099                 if (catd == catsopen[i]) {
1100                         for (; i < NL_MAXOPEN-1; i++) {
1101                             catsopen[i] = catsopen[i+1];
1102                             catpid[i] = catpid[i+1];
1103                         }
1104                         catsopen[i] = NULL;
1105                         catpid[i] = 0;
1106                 }
1107         }
1108
1109 /*______________________________________________________________________
1110         close the cat and free up the memory
1111   ______________________________________________________________________*/
1112         if (catd->_mem == FALSE)
1113         {
1114         for (i = 0 ; i <= catd->_n_sets ; i++) {
1115                 if (catd->_set[i]._mp) 
1116                         free(catd->_set[i]._mp);   
1117                         /*---- free the _message pointer arrays ----*/
1118
1119                 if (catd->_set[i]._msgtxt) {
1120                         for (j = 0 ; j <= catd->_set[i]._n_msgs ; j++) {
1121                                 if (catd->_set[i]._msgtxt[j]) {
1122 /*                                      free(catd->_set[i]._msgtxt[j]);*/
1123                                     }
1124                         }
1125                         if (catd->_set[i]._msgtxt)
1126                             free(catd->_set[i]._msgtxt);
1127                 }
1128         }
1129         }
1130
1131         if (catd->_fd)
1132                 fclose(catd->_fd);      /*---- close the ctatlog ----*/
1133         if (catd->_set) 
1134                 free(catd->_set);       /*---- free the sets ----*/
1135         if (catd->_name)
1136                 free(catd->_name);      /*---- free the name ----*/
1137         if (catd->_hd)
1138                 free(catd->_hd);        /*---- free the header ----*/
1139         if (catd)
1140                 free(catd);             /*---- free the catd ----*/
1141 }
1142
1143 static char *_do1_read_msg(nl_catd catd,int setno,int msgno) 
1144
1145         /*---- catd: the catd of the catalog to be read from ----*/
1146         /*---- setno: the set number of the message ----*/
1147         /*---- msgno: the msgno of the message ----*/
1148
1149 {
1150         nl_catd catd1;          /*--- catd for different process ----*/
1151         char *_read1_msg();
1152
1153 #ifndef AFS_OSF20_ENV
1154         if (getpid() == catd->_pid)
1155 #else
1156         if (1)
1157 #endif
1158                 return (_read1_msg (catd, setno, msgno));
1159         else {
1160                 /*
1161                  * Since our pid is different from the one in
1162                  * catd, catd must have come from a catopen()
1163                  * in our parent.  We need a catd of our own.
1164                  * The first time through here, the call to
1165                  * catopen() creates a new catd and we try to
1166                  * open its message catalog.  After that, the
1167                  * catopen() just retrieves the catd.
1168                  */
1169                 if (((catd1 = catopen1(catd->_name, 0)) != CATD_ERR) &&
1170                     ((catd1->_fd == NL_FILE_CLOSED &&
1171                       _do1_open(catd1) != CATD_ERR) ||
1172                      (catd1->_fd != NL_FILE_UNUSED)))
1173                         return (_read1_msg(catd1,setno,msgno));
1174                 else    
1175                         return(NULL);
1176         }
1177 }
1178         
1179
1180 struct _catset *_cat1_get_catset();
1181 static struct _msgptr *_cat1_get_msgptr();
1182 static char *_read1_msg(nl_catd catd,int setno,int msgno)
1183
1184
1185         struct _catset *set;    /*--- ptr to set's _catset structure ---*/
1186         struct _msgptr *msg;    /*--- ptr to msg's _msgptr structure ---*/
1187         char **msgtxt;          /*--- temporary pointer to the message text
1188                                       for speed.  ----*/
1189
1190         set = _cat1_get_catset(catd, setno);
1191         if (set) {
1192                 msg = _cat1_get_msgptr(set, msgno);
1193                 if (msg) {
1194                         msgtxt = &set->_msgtxt[msg - set->_mp];
1195                         if (1/*!*msgtxt*/) {
1196                                 *msgtxt = (char *) malloc(msg->_msglen + 1);
1197                                 if (!*msgtxt)
1198                                         return(NULL);
1199
1200                                 fseek(catd->_fd, (long) msg->_offset, 0);
1201                                 if (fread((void *) *msgtxt,
1202                                           (size_t) (msg->_msglen + 1),
1203                                           (size_t) 1, catd->_fd) != 1)
1204                                         return(NULL);
1205                         }
1206
1207                         return(*msgtxt);
1208                 }
1209         }
1210         return(NULL);
1211 }
1212
1213 /*
1214  * NAME: compare_sets
1215  *                                                                    
1216  * FUNCTION: Compare function used by bsearch() in _cat_get_catset().
1217  *                                                                    
1218  * ARGUMENTS:
1219  *      key             - pointer to set number we're searching for
1220  *      element         - pointer to current _catset structure
1221  *
1222  * RETURNS: Returns -1, 0, or 1, depending on whether the set number
1223  *          is less than, equal to, or greater than the set number of
1224  *          the _catset structure.
1225  *
1226  */  
1227
1228 static int
1229 compare_sets(const void *key, const void *element)
1230 {
1231         int *setno = (int *) key;
1232         struct _catset *set = (struct _catset *) element;
1233
1234         if (*setno < set->_setno)
1235                 return -1;
1236         if (*setno > set->_setno)
1237                 return  1;
1238
1239         return 0;
1240 }
1241
1242
1243 /*
1244  * NAME: _cat_get_catset
1245  *                                                                    
1246  * FUNCTION: Find a set in the catd->_set array.  Assumes that the
1247  *           sets in the array are sorted by increasing set number.
1248  *                                                                    
1249  * ARGUMENTS:
1250  *      catd            - catalog descripter obtained from catopen()
1251  *      setno           - message catalogue set number
1252  *
1253  * RETURNS: Returns a pointer to the set on success.
1254  *      On any error, returns NULL.
1255  *
1256  */  
1257
1258 struct _catset *_cat1_get_catset(nl_catd catd, int setno)
1259 {
1260         struct _catset *set;
1261
1262         if ((catd == (nl_catd) NULL) || (catd == CATD_ERR))
1263                 return (struct _catset *) NULL;
1264
1265         if (catd->_sets_expanded) {
1266                 if ((setno < 0) || (setno > catd->_n_sets))
1267                         return (struct _catset *) NULL;
1268
1269                 set = &catd->_set[setno];
1270
1271                 /*
1272                  * Catch empty elements in the array.  They aren't
1273                  * real sets.
1274                  */
1275
1276                 if (set->_mp == (struct _msgptr *) NULL)
1277                         return (struct _catset *) NULL;
1278         }
1279         else {
1280                 set = (struct _catset *) bsearch((void *) &setno,
1281                                                  catd->_set, catd->_n_sets + 1,
1282                                                  sizeof(struct _catset),
1283                                                  compare_sets);
1284
1285                 /*
1286                  * Since the sets are compacted, there aren't any
1287                  * empty elements in the array to check for.
1288                  */
1289         }
1290
1291         return set;
1292 }
1293
1294
1295 /*
1296  * NAME: compare_msgs
1297  *                                                                    
1298  * FUNCTION: Compare function used by bsearch() in _cat_get_msgptr().
1299  *                                                                    
1300  * ARGUMENTS:
1301  *      key             - pointer to message number we're searching for
1302  *      element         - pointer to current _msgptr structure
1303  *
1304  * RETURNS: Returns -1, 0, or 1, depending on whether the message
1305  *          number is less than, equal to, or greater than the message
1306  *          number of the _msgptr structure.
1307  *
1308  */  
1309
1310 static int
1311 compare_msgs(const void *key, const void *element)
1312 {
1313         int *msgno = (int *) key;
1314         struct _msgptr *msg = (struct _msgptr *) element;
1315
1316         if (*msgno < msg->_msgno)
1317                 return -1;
1318         if (*msgno > msg->_msgno)
1319                 return  1;
1320
1321         return 0;
1322 }
1323
1324 /*
1325  * NAME: _cat1_get_msgptr
1326  *                                                                    
1327  * FUNCTION: Find a message in a set's set->_mp array.  Assumes that
1328  *           the messages in the array are sorted by increasing
1329  *           message number.
1330  *                                                                    
1331  * ARGUMENTS:
1332  *      set             - ptr to _catset structure
1333  *      msgno           - message catalogue message number
1334  *
1335  * RETURNS: Returns a pointer to the message on success.
1336  *      On any error, returns NULL.
1337  *
1338  */  
1339 static struct _msgptr *_cat1_get_msgptr(struct _catset *set, int msgno)
1340 {
1341         struct _msgptr *msg;
1342  
1343         if (set == (struct _catset *) NULL)
1344                 return (struct _msgptr *) NULL;
1345  
1346         if (set->_mp == (struct _msgptr *) NULL) /* empty set */
1347                 return (struct _msgptr *) NULL;
1348  
1349         if (set->_msgs_expanded) {
1350                 if ((msgno < 0) || (msgno > set->_n_msgs))
1351                         return (struct _msgptr *) NULL;
1352  
1353                 msg = &set->_mp[msgno];
1354  
1355                 /*
1356                  * Catch empty elements in the array.  They aren't
1357                  * real messages.
1358                  */
1359  
1360                 if (!msg->_offset)
1361                         return (struct _msgptr *) NULL;
1362         }
1363         else {
1364                 msg = (struct _msgptr *) bsearch((void *) &msgno,
1365                                                  set->_mp, set->_n_msgs + 1,
1366                                                  sizeof(struct _msgptr),
1367                                                  compare_msgs);
1368  
1369                 /*
1370                  * Since the messages are compacted, there aren't any
1371                  * empty elements in the array to check for.
1372                  */
1373         }
1374  
1375         return msg;
1376 }
1377
1378 char *catgets1(nl_catd catd,int setno,int msgno,char *def)
1379         /*---- catd: the catd to get the message from ----*/
1380         /*---- setno: the set number of the message ----*/
1381         /*---- msgno: the message number of the message ----*/
1382         /*---- def: the default string to be returned ----*/
1383  
1384 {
1385         int errno_save;
1386         char *_do_read_msg();
1387         char *m;
1388         errno_save = errno;
1389
1390         if (catd == NULL || catd == CATD_ERR ||
1391             catd->_magic != CAT_MAGIC || catd->_fd == NL_FILE_UNUSED) {
1392                 return(def);
1393             }
1394         if (catd->_fd == NL_FILE_CLOSED) {
1395                 catd = _do1_open(catd);
1396                 if (catd == CATD_ERR) 
1397                         return(def);
1398         }
1399  
1400         if (catd->_mem) {       /*----  for mapped files ----*/
1401                 if (setno <= catd->_hd->_setmax) {
1402                         if (msgno < catd->_set[setno]._n_msgs) {
1403                                 if (catd->_set[setno]._mp[msgno]._offset) {
1404                                         return(catd->_mem + 
1405                                         catd->_set[setno]._mp[msgno]._offset);
1406                                 }
1407                         }
1408                 }
1409                 return(def);
1410         }
1411         else {  /*---- for unmapped files ----*/
1412                 m = _do1_read_msg(catd,setno,msgno);
1413                 if(m == NULL)
1414                         return(def);
1415                 else
1416                         return(m);
1417         }
1418 }
1419
1420 #endif
1421
1422 #define FACILITY_CODE_MASK          0xF0000000
1423 #define FACILITY_CODE_SHIFT         28
1424
1425 #define COMPONENT_CODE_MASK         0x0FFFF000
1426 #define COMPONENT_CODE_SHIFT        12
1427
1428 #define STATUS_CODE_MASK            0x00000FFF
1429 #define STATUS_CODE_SHIFT           0
1430
1431 #define NO_MESSAGE                  "THIS IS NOT A MESSAGE"
1432
1433 /*
1434  * The system-dependant location for the catalog files is defined in sysconf.h
1435  * RPC_DEFAULT_NLSPATH should be defined in sysconf.h. Otherwise we use
1436  * /usr/afs/etc/C/%s.cat 
1437  */
1438
1439 #ifndef RPC_NLS_FORMAT
1440 #define RPC_NLS_FORMAT "%s.cat"
1441 #endif
1442
1443 dce1_error_inq_text (status_to_convert, error_text, status)
1444 afs_uint32           status_to_convert;
1445 unsigned char           *error_text;
1446 int                     *status;
1447
1448 {
1449     unsigned short  facility_code;
1450     unsigned short  component_code;
1451     unsigned short  status_code;
1452     unsigned short  i, failed=0;
1453     nl_catd     catd;
1454     char        component_name[4];
1455     char        *facility_name;
1456     char        filename_prefix[7];
1457     char        nls_filename[11];
1458     char        alt_filename[80];
1459     char        *message;
1460     int J;
1461     static char *facility_names[] = {
1462         "xxx",
1463         "afs"
1464     };
1465
1466     /*
1467      * set up output status for future error returns
1468      */
1469     if (status != NULL)
1470     {
1471         *status = -1;
1472     }
1473     /*
1474      * check for ok input status
1475      */
1476     if (status_to_convert == 0)
1477     {
1478         if (status != NULL)
1479         {
1480             *status = 0;
1481         }
1482         strcpy ((char *)error_text, "successful completion");
1483         return;
1484     }
1485
1486     /*
1487      * extract the component, facility and status codes
1488      */
1489     facility_code = (status_to_convert & FACILITY_CODE_MASK)  >> FACILITY_CODE_SHIFT;
1490     component_code = (status_to_convert & COMPONENT_CODE_MASK) >> COMPONENT_CODE_SHIFT;
1491     status_code = (status_to_convert & STATUS_CODE_MASK)  >> STATUS_CODE_SHIFT;
1492
1493     /*
1494      * see if this is a recognized facility
1495      */
1496     if (facility_code == 0 || facility_code > sizeof (facility_names) / sizeof (char *))
1497     {
1498         sprintf ((char *) error_text, "status %08x (unknown facility)", status_to_convert);
1499         return; 
1500     }
1501     facility_name = facility_names[facility_code - 1];
1502     /*
1503      * Convert component name from RAD-50 component code.  (Mapping is:
1504      * 0 => 'a', ..., 25 => 'z', 26 => '{', 27 => '0', ..., 36 => '9'.)
1505      */
1506     component_name[3] = 0;
1507     component_name[2] = component_code % 40;
1508     component_code /= 40;
1509     component_name[1] = component_code % 40;
1510     component_name[0] = component_code / 40;
1511     for (i = 0; i < 3; i++)
1512     {
1513         component_name[i] += (component_name[i] <= 26) ? 'a' : ('0' - 27);
1514     }
1515     sprintf (filename_prefix, "%3s%3s", facility_name, component_name);
1516     sprintf (nls_filename, RPC_NLS_FORMAT, filename_prefix);
1517
1518     /*
1519      * Open the message file
1520      */
1521 #if     defined(AFS_OSF_ENV)
1522 #if     defined(AFS_OSF20_ENV)
1523     catd = (nl_catd) catopen (nls_filename, 0);
1524 #else
1525     catd = (nl_catd) catopen1 (nls_filename, 0);
1526 #endif
1527 #else
1528     J = (int) catopen (nls_filename, 0);
1529     catd = (nl_catd)J;
1530 #endif
1531     if (catd == (nl_catd) -1)
1532     {
1533         /*
1534          * If we did not succeed in opening message file using NLSPATH,
1535          * try to open the message file in a well-known default area
1536          */
1537 tryagain:         
1538 #ifndef RPC_DEFAULT_NLSPATH
1539         sprintf(alt_filename, "%s/C/%s.cat", AFSDIR_CLIENT_ETC_DIRPATH, filename_prefix);
1540 #else
1541         sprintf (alt_filename, RPC_DEFAULT_NLSPATH, filename_prefix);
1542 #endif
1543
1544 #if     defined(AFS_OSF_ENV)
1545 #if     defined(AFS_OSF20_ENV)
1546         catd = (nl_catd) catopen (alt_filename, 0);
1547 #else
1548         catd = (nl_catd) catopen1 (alt_filename, 0);
1549 #endif
1550 #else
1551         J = (int) catopen (alt_filename, 0);
1552         catd = (nl_catd)J;
1553 #endif
1554         if (catd == (nl_catd) -1)
1555         {
1556             sprintf ((char *) error_text, "status %08x (%s / %s)", 
1557                         status_to_convert, facility_name, component_name);
1558             return;
1559         }
1560     }    
1561     /*
1562      * try to get the specified message from the file
1563      */
1564 #if     defined(AFS_OSF_ENV) && !defined(AFS_OSF20_ENV)
1565     message = (char *) catgets1 (catd, 1, status_code, NO_MESSAGE);
1566 #else
1567     message = (char *) catgets (catd, 1, status_code, NO_MESSAGE);
1568 #endif
1569     /*
1570      * if everything went well, return the resulting message
1571      */
1572     if (strcmp (message, NO_MESSAGE) != 0)
1573     {
1574         sprintf ((char *) error_text, "%s (%s / %s)", 
1575                     message, facility_name, component_name);
1576         if (status != NULL)
1577         {
1578             *status = 0;
1579         }
1580     }
1581     else
1582     {
1583         if (!failed) {
1584             failed = 1;
1585             goto tryagain;
1586         }
1587         sprintf ((char *) error_text, "status %08x (%s / %s)", 
1588                     status_to_convert, facility_name, component_name);
1589     }
1590 #if     defined(AFS_OSF_ENV) && !defined(AFS_OSF20_ENV)
1591     catclose1 (catd);
1592 #else
1593     catclose (catd);
1594 #endif
1595
1596 }        
1597
1598
1599 icl_DumpKernel(outFilep, setname)
1600   FILE *outFilep;
1601   char *setname;
1602 {
1603     afs_int32 bufferSize = 0;
1604     afs_int32 *bufferp;
1605     afs_int32 i;
1606     afs_int32 code, retVal = 0;
1607     char tname[64];
1608     afs_int32 nwords;
1609     afs_int32 ix;
1610     afs_int32 rlength;
1611     afs_int32 dummy, dummy2;
1612     struct logInfo *lip;
1613
1614     /* first, enumerate the logs we're interested in */
1615     if (setname)
1616     {
1617         int found = 0;
1618         /* dump logs for a particular set */
1619         for(i=0; i < ICL_LOGSPERSET; i++) {
1620             code = afs_syscall(AFSCALL_ICL, ICL_OP_ENUMLOGSBYSET,
1621                                (long) setname, i, (long) tname, sizeof(tname));
1622             if (code) {
1623                 if (errno == EBADF) {
1624                     code = 0;
1625                     continue;   /* missing slot, nothing to worry about */
1626                 }
1627                 break;
1628             }
1629             code = afs_syscall(AFSCALL_ICL, ICL_OP_GETLOGINFO,
1630                                (long) tname, (long) &dummy, (long) &dummy2, 0);
1631             if (code)
1632                 break;
1633             found++;
1634             if (dummy > bufferSize)     /* find biggest log */
1635                 bufferSize = dummy;
1636             lip = (struct logInfo *) malloc(sizeof(struct logInfo));
1637             bzero((char *)lip, sizeof(*lip));
1638             lip->nextp = allInfo;
1639             allInfo = lip;
1640             lip->name = (char *) malloc(strlen(tname)+1);
1641             strcpy(lip->name, tname);
1642         }
1643         i = found;
1644     }
1645     else
1646     {
1647         /* dump all logs */
1648         for(i=0;i<1000;i++) {
1649             code = afs_syscall(AFSCALL_ICL, ICL_OP_ENUMLOGS,
1650                                i, (long) tname, sizeof(tname), (long) &dummy);
1651             if (code) break;
1652             if (dummy > bufferSize)     /* find biggest log */
1653                 bufferSize = dummy;
1654             lip = (struct logInfo *) malloc(sizeof(struct logInfo));
1655             bzero((char *)lip, sizeof(*lip));
1656             lip->nextp = allInfo;
1657             allInfo = lip;
1658             lip->name = (char *) malloc(strlen(tname)+1);
1659             strcpy(lip->name, tname);
1660         }
1661     }
1662
1663     if (bufferSize == 0) return -1;
1664     bufferp = (afs_int32 *) malloc(sizeof(afs_int32) * bufferSize);
1665     if (!bufferp) return -1;
1666
1667     fprintf(outFilep, "Found %d logs.\n", i);
1668
1669     /* now print out the contents of each log */
1670     for(lip = allInfo; lip; lip=lip->nextp) {
1671         fprintf(outFilep, "\nContents of log %s:\n", lip->name);
1672         /* read out everything first; gets a more consistent
1673          * snapshot.
1674          */
1675         nwords = 0;             /* total words copied out */
1676         for(i=0;;) {
1677             /* display all the entries in the log */
1678             if (bufferSize - nwords <= 0) break;        /* filled whole buffer */
1679             code = afs_syscall(AFSCALL_ICL, ICL_OP_COPYOUT,
1680                                (long) lip->name, (long) (bufferp+nwords),
1681                                bufferSize - nwords, (long) &i);
1682             if (code < 0) {
1683                 /* otherwise we've got an error */
1684                 fprintf(outFilep, "Returned error %d dumping log.\n", errno);
1685                 break;
1686             }
1687             /* otherwise, we have flags in the high order byte, and
1688              * a length (in words) in the remainder.
1689              */
1690             if ((code >> 24) & ICL_COPYOUTF_MISSEDSOME)
1691                 fprintf(outFilep, "Log wrapped; data missing.\n");
1692             code &= 0xffffff;
1693             if (code == 0) {
1694                 /* we're done */
1695                 break;
1696             }
1697             nwords += code;
1698             i += code;
1699         }       /* for loop over all cookies */
1700
1701         /* otherwise we should display all of the log entries here.
1702          * Note that a record may end in the middle, in which case
1703          * we should start over with the cookie value of the start
1704          * of that record.
1705          */
1706         for(ix = 0; ix<nwords;) {
1707             /* start of a record */
1708             rlength = (bufferp[ix] >> 24) & 0xff;
1709             if (rlength <= 0) {
1710                 fprintf(outFilep, "Internal error: 0 length record\n");
1711                 retVal = -1;
1712                 goto done;
1713             }
1714             /* ensure that entire record fits */
1715             if (ix+rlength > nwords) {
1716                 /* doesn't fit, adjust cookie and break */
1717                 break;
1718             }
1719             /* print the record */
1720             DisplayRecord(outFilep, &bufferp[ix], rlength);
1721             ix += rlength;
1722 #ifdef notdef
1723             /* obsolete: read entire buffer first */
1724             i += rlength;       /* update cookie value, too */
1725 #endif
1726         }       /* for loop displaying buffer */
1727     }           /* for loop over all logs */
1728
1729   done:
1730     free(bufferp);
1731     return(retVal);
1732 }
1733
1734 /* clear out log 'name' */
1735 icl_ClearLog(name)
1736   char *name;
1737 {
1738     afs_int32 code;
1739
1740     code = afs_syscall(AFSCALL_ICL, ICL_OP_CLRLOG, (long)name, 0, 0, 0);
1741     return code;
1742 }
1743
1744 /* clear out set 'name' */
1745 icl_ClearSet(name)
1746   char *name;
1747 {
1748     afs_int32 code;
1749
1750     code = afs_syscall(AFSCALL_ICL, ICL_OP_CLRSET, (long) name, 0, 0, 0);
1751     return code;
1752 }
1753
1754 /* clear out all logs */
1755 icl_ClearAll()
1756 {
1757     afs_int32 code;
1758
1759     code = afs_syscall(AFSCALL_ICL, ICL_OP_CLRALL, 0, 0, 0, 0);
1760     return code;
1761 }
1762
1763 /* list out all available sets to outFileP */
1764 int icl_ListSets(outFileP)
1765   FILE *outFileP;
1766 {
1767     int i;
1768     afs_int32 code = 0;
1769     afs_int32 states;
1770     char tname[64];
1771
1772     for(i=0;i<1000;i++) {
1773         code = afs_syscall(AFSCALL_ICL, ICL_OP_ENUMSETS,
1774                        i, (long) tname, sizeof(tname), (long) &states);
1775         if (code) break;
1776         (void) fprintf(outFileP, "%s %s%s%s\n", tname,
1777                        (states & ICL_SETF_ACTIVE) ? "active" : "inactive",
1778                        (states & ICL_SETF_FREED) ? " (dormant)" : "",
1779                        (states & ICL_SETF_PERSISTENT) ? " persistent" : "");
1780     }
1781
1782     return 0;
1783 }
1784
1785 /* list out all available logs to outFileP */
1786 int icl_ListLogs(outFileP, int32flg)
1787   FILE *outFileP;
1788   int int32flg;
1789 {
1790     int i;
1791     int allocated;
1792     afs_int32 code = 0;
1793     afs_int32 logSize;
1794     char tname[64];
1795
1796     for(i=0;i<1000;i++) {
1797         code = afs_syscall(AFSCALL_ICL, ICL_OP_ENUMLOGS,
1798                        i, (long) tname, sizeof(tname), (long) &logSize);
1799         if (code) break;
1800         if (int32flg)
1801         {
1802             /* get more information on the log */
1803             code = afs_syscall(AFSCALL_ICL, ICL_OP_GETLOGINFO, (long) tname, 
1804                                (long) &logSize, (long) &allocated, 0);
1805             if (code)
1806                 break;
1807             (void) fprintf(outFileP, "%s : %d kbytes (%s)\n", tname, logSize/1024,
1808                            allocated ? "allocated" : "unallocated");
1809         }
1810         else
1811             (void) fprintf(outFileP, "%s\n", tname);
1812     }
1813
1814     return 0;
1815 }
1816
1817 /* list out all available logs to outFileP */
1818 int icl_ListLogsBySet(outFileP, setname, int32flg)
1819   FILE *outFileP;
1820   char *setname;
1821   int int32flg;
1822 {
1823     int i;
1824     afs_int32 code = 0;
1825     afs_int32 logSize;
1826     int allocated;
1827     char tname[64];
1828
1829     for(i=0; i < ICL_LOGSPERSET; i++) {
1830         code = afs_syscall(AFSCALL_ICL, ICL_OP_ENUMLOGSBYSET,
1831                        (long) setname, i, (long) tname, sizeof(tname));
1832         if (code) {
1833             if (errno == EBADF) {
1834                 code = 0;
1835                 continue;       /* missing */
1836             }
1837             break;
1838         }
1839         if (int32flg)
1840         {
1841             /* get more information on the log */
1842             code = afs_syscall(AFSCALL_ICL, ICL_OP_GETLOGINFO, (long) tname, 
1843                                (long) &logSize, (long) &allocated, 0);
1844             if (code)
1845                 break;
1846             (void) fprintf(outFileP, "%s : %d kbytes (%s)\n", tname, logSize/1024,
1847                            allocated ? "allocated" : "unallocated");
1848         }
1849         else
1850             (void) fprintf(outFileP, "%s\n", tname);
1851     }
1852
1853     return code;
1854 }
1855
1856 /* activate/deactivate/free specified set */
1857 int icl_ChangeSetState(name, op)
1858   char *name;
1859   afs_int32 op;
1860 {
1861     afs_int32 code;
1862
1863     code = afs_syscall(AFSCALL_ICL, ICL_OP_SETSTAT, (long) name, op, 0, 0);
1864     return code;
1865 }
1866
1867 /* activate/deactivate/free all sets */
1868 int icl_ChangeAllSetState(op)
1869   afs_int32 op;
1870 {
1871     afs_int32 code;
1872
1873     code = afs_syscall(AFSCALL_ICL, ICL_OP_SETSTATALL, op, 0, 0, 0);
1874     return code;
1875 }
1876
1877 /* set size if log */
1878 int icl_ChangeLogSize(name, logSize)
1879   char *name;
1880   afs_int32 logSize;
1881 {
1882     afs_int32 code;
1883
1884     code = afs_syscall(AFSCALL_ICL, ICL_OP_SETLOGSIZE, (long)name, logSize, 0, 0);
1885     return code;
1886 }
1887
1888 /* get logsize of specified log */
1889 int icl_GetLogsize(logname, logSizeP, allocatedP)
1890   char *logname;
1891   afs_int32 *logSizeP;
1892   int *allocatedP;
1893 {
1894     afs_int32 code;
1895     code = afs_syscall(AFSCALL_ICL, ICL_OP_GETLOGINFO, (long) logname,
1896                        (long) logSizeP, (long) allocatedP, 0);
1897     return code;
1898 }
1899
1900 /* get state of specified set */
1901 int icl_GetSetState(setname, stateP)
1902   char *setname;
1903   afs_int32 *stateP;
1904 {
1905     afs_int32 code;
1906     code = afs_syscall(AFSCALL_ICL, ICL_OP_GETSETINFO, (long) setname,
1907                        (long) stateP, 0, 0);
1908     return code;
1909 }
1910
1911 icl_TailKernel(outFilep, logname, waitTime)
1912   FILE *outFilep;
1913   char *logname;
1914   afs_int32 waitTime;
1915 {
1916     afs_int32 bufferSize = 0;
1917     afs_int32 newBufferSize;
1918     afs_int32 *bufferp;
1919     afs_int32 i;
1920     afs_int32 code, retVal = 0;
1921     afs_int32 nwords;
1922     afs_int32 ix;
1923     afs_int32 rlength;
1924     int allocated;
1925     struct logInfo *lip;
1926
1927     /* get information about the specified log */
1928     code = afs_syscall(AFSCALL_ICL, ICL_OP_GETLOGINFO,
1929                        (long) logname, (long) &bufferSize, (long) &allocated, 0);
1930     if (code)
1931     {
1932         if (errno == ENOENT)
1933             (void) fprintf(stderr, "'%s' not found\n", logname);
1934         else
1935             (void) fprintf(stderr, "cannot get information on log '%s' (errno = %d)\n",
1936                            logname, errno);
1937         return -1;
1938     }
1939
1940     if (!allocated)
1941     {
1942         (void) fprintf(stderr, "'%s' not allocated\n", logname);
1943         return 0;
1944     }
1945
1946     if (bufferSize == 0) return -1;
1947     bufferp = (afs_int32 *) malloc(sizeof(afs_int32) * bufferSize);
1948     if (!bufferp) 
1949     {
1950         (void) fprintf(stderr, "cannot allocate %d words for buffer\n", 
1951                        bufferSize);
1952         return -1;
1953     }
1954
1955     /* start "infinite" loop */
1956     for(;;)
1957     {
1958         /* read out all that's currently there */
1959         nwords = 0;             /* total words copied out */
1960         i = 0;  /* initialize cookie */
1961         for(;;) {
1962             /* display all the entries in the log */
1963             if (bufferSize - nwords <= 0) 
1964                 break;  /* filled whole buffer, clear when done */
1965             code = afs_syscall(AFSCALL_ICL, ICL_OP_COPYOUTCLR,
1966                                (long) logname, (long) (bufferp+nwords),
1967                                bufferSize - nwords, (long) &i);
1968             if (code < 0) {
1969                 /* otherwise we've got an error */
1970                 fprintf(stderr, "returned error %d dumping log.\n", errno);
1971                 retVal = -1;
1972                 goto tail_done;
1973             }
1974             /* otherwise, we have flags in the high order byte, and
1975              * a length (in words) in the remainder.
1976              */
1977             code &= 0xffffff;
1978             if (code == 0) {
1979                 /* we're done */
1980                 break;
1981             }
1982             nwords += code;
1983             i += code;
1984         }       /* for loop over all cookies */
1985
1986         /* otherwise we should display all of the log entries here.
1987          * Note that a record may end in the middle, in which case
1988          * we should start over with the cookie value of the start
1989          * of that record.
1990          */
1991         for(ix = 0; ix<nwords;) {
1992             /* start of a record */
1993             rlength = (bufferp[ix] >> 24) & 0xff;
1994             /* ensure that entire record fits */
1995             if (ix+rlength > nwords) {
1996                 /* doesn't fit, adjust cookie and break */
1997                 if (rlength <= 0) {
1998                     fprintf(stderr, "BOGUS: 0 length record\n");
1999                     retVal = -1;
2000                     goto tail_done;
2001                 }
2002                 break;
2003             }
2004             /* print the record */
2005             DisplayRecord(outFilep, &bufferp[ix], rlength);
2006             ix += rlength;
2007         }       /* for loop displaying buffer */
2008
2009         if (waitTime)
2010             sleep(waitTime);
2011
2012         /* see if things have changed */
2013         code = afs_syscall(AFSCALL_ICL, ICL_OP_GETLOGINFO,
2014                            (long) logname, (long) &newBufferSize,
2015                            (long) &allocated, 0);
2016         if (code)
2017         {
2018             if (errno == ENOENT)
2019                 (void) fprintf(stderr, "'%s' not found\n", logname);
2020             else
2021                 (void) fprintf(stderr, "cannot get information on log '%s' (errno = %d)\n",
2022                                logname, errno);
2023             retVal = -1;
2024             goto tail_done;
2025         }
2026         
2027         if (!allocated)
2028         {
2029             (void) fprintf(stderr, "'%s' no int32er allocated\n", logname);
2030             retVal = -1;
2031             goto tail_done;
2032         }
2033         
2034         if (bufferSize == 0) 
2035         {
2036             (void) fprintf(stderr, "buffer size has become 0\n");
2037             retVal = -1;
2038             goto tail_done;
2039         }
2040         if (bufferSize != newBufferSize)
2041         {
2042             /* have to reallocate a buffer */
2043             bufferSize = newBufferSize;
2044             free(bufferp);
2045             bufferp = (afs_int32 *) malloc(sizeof(afs_int32) * bufferSize);
2046             if (!bufferp) 
2047             {
2048                 (void) fprintf(stderr, "cannot allocate %d words for buffer\n", 
2049                                bufferSize);
2050                 retVal = -1;
2051                 goto tail_done;
2052             }
2053         }
2054     }           /* infinite loop */
2055
2056   tail_done:
2057     free(bufferp);
2058     return(retVal);
2059 }
2060
2061 #if !defined(AFS_SGI_ENV)
2062 afs_syscall(call, parm0, parm1, parm2, parm3, parm4, parm5, parm6) 
2063 long call, parm0, parm1, parm2, parm3, parm4, parm5, parm6;
2064 {
2065     int code;
2066 #ifdef AFS_LINUX20_ENV
2067     int eparm[4];
2068     eparm[0] = parm3;
2069     eparm[1] = parm4;
2070     eparm[2] = parm5;
2071     eparm[3] = parm6;
2072     /* Linux can only handle 5 arguments in the actual syscall. */
2073     if (call == AFSCALL_ICL) {
2074         code = syscall(AFS_SYSCALL, call, parm0, parm1, parm2, eparm);
2075     }
2076     else {
2077         code = syscall(AFS_SYSCALL, call, parm0, parm1, parm2, parm3);
2078     }
2079 #else
2080 #if !defined(AFS_SGI_ENV) && !defined(AFS_AIX32_ENV)
2081     code =  syscall(AFS_SYSCALL, call, parm0, parm1, parm2, parm3, parm4);
2082 #else
2083 #if defined(AFS_SGI_ENV)
2084     code =  syscall(AFS_ICL, call, parm0, parm1, parm2, parm3, parm4);  /* XXX */
2085 #else
2086     code = syscall(AFSCALL_ICL, parm0, parm1, parm2, parm3, parm4);
2087 #endif
2088 #endif
2089     return code;
2090 #endif /* AFS_LINUX20_ENV */
2091 }
2092 #endif
2093
2094
2095 int icl_inited = 0;
2096
2097 /* init function, called once, under icl_lock */
2098 icl_Init()
2099 {
2100     icl_inited = 1;
2101
2102 #ifndef KERNEL
2103     /* setup signal handler, in user space */
2104 #endif /* KERNEL */
2105
2106     return 0;
2107 }
2108
2109 icl_CreateSet(name, baseLogp, fatalLogp, outSetpp)
2110   char *name;
2111   struct afs_icl_log *baseLogp;
2112   struct afs_icl_log *fatalLogp;
2113   struct afs_icl_set **outSetpp;
2114 {
2115     return icl_CreateSetWithFlags(name, baseLogp, fatalLogp, /*flags*/0, outSetpp);
2116 }
2117
2118 /* create a set, given pointers to base and fatal logs, if any.
2119  * Logs are unlocked, but referenced, and *outSetpp is returned
2120  * referenced.  Function bumps reference count on logs, since it
2121  * addds references from the new icl_set.  When the set is destroyed,
2122  * those references will be released.
2123  */
2124 icl_CreateSetWithFlags(name, baseLogp, fatalLogp, flags, outSetpp)
2125   char *name;
2126   struct afs_icl_log *baseLogp;
2127   struct afs_icl_log *fatalLogp;
2128   afs_uint32  flags;
2129   struct afs_icl_set **outSetpp;
2130 {
2131     register struct afs_icl_set *setp;
2132     register int i;
2133     afs_int32 states = ICL_DEFAULT_SET_STATES;
2134
2135     if (!icl_inited) icl_Init();
2136
2137     for (setp = icl_allSets; setp; setp = setp->nextp) {
2138         if (strcmp(setp->name, name) == 0) {
2139             setp->refCount++;
2140             *outSetpp = setp;
2141             if (flags & ICL_CRSET_FLAG_PERSISTENT)
2142             {
2143                 setp->states |= ICL_SETF_PERSISTENT;
2144             }
2145             return 0;
2146         }
2147     }
2148     
2149     /* determine initial state */
2150     if (flags & ICL_CRSET_FLAG_DEFAULT_ON)
2151         states = ICL_SETF_ACTIVE;
2152     else if (flags & ICL_CRSET_FLAG_DEFAULT_OFF)
2153         states = ICL_SETF_FREED;
2154     if (flags & ICL_CRSET_FLAG_PERSISTENT)
2155         states |= ICL_SETF_PERSISTENT;
2156
2157     setp = (struct afs_icl_set *) osi_Alloc(sizeof(struct afs_icl_set));
2158     bzero((caddr_t)setp, sizeof(*setp));
2159     setp->refCount = 1;
2160     if (states & ICL_SETF_FREED)
2161         states &= ~ICL_SETF_ACTIVE;     /* if freed, can't be active */
2162     setp->states = states;
2163
2164     setp->name = (char *)osi_Alloc(strlen(name)+1);
2165     strcpy(setp->name, name);
2166     setp->nevents = ICL_DEFAULTEVENTS;
2167     setp->eventFlags = (char *)osi_Alloc(ICL_DEFAULTEVENTS);
2168     for(i=0; i<ICL_DEFAULTEVENTS; i++)
2169         setp->eventFlags[i] = 0xff;     /* default to enabled */
2170
2171     /* update this global info under the icl_lock */
2172     setp->nextp = icl_allSets;
2173     icl_allSets = setp;
2174
2175     /* set's basic lock is still held, so we can finish init */
2176     if (baseLogp) {
2177         setp->logs[0] = baseLogp;
2178         icl_LogHold(baseLogp);
2179         if (!(setp->states & ICL_SETF_FREED))
2180             icl_LogUse(baseLogp);       /* log is actually being used */
2181     }
2182     if (fatalLogp) {
2183         setp->logs[1] = fatalLogp;
2184         icl_LogHold(fatalLogp);
2185         if (!(setp->states & ICL_SETF_FREED))
2186             icl_LogUse(fatalLogp);      /* log is actually being used */
2187     }
2188
2189     *outSetpp = setp;
2190     return 0;
2191 }
2192
2193 /* function to change event enabling information for a particular set */
2194 icl_SetEnable(setp, eventID, setValue)
2195   struct afs_icl_set *setp;
2196   afs_int32 eventID;
2197   int setValue;
2198 {
2199     char *tp;
2200
2201     if (!ICL_EVENTOK(setp, eventID)) {
2202         return -1;
2203     }
2204     tp = &setp->eventFlags[ICL_EVENTBYTE(eventID)];
2205     if (setValue)
2206         *tp |= ICL_EVENTMASK(eventID);
2207     else
2208         *tp &= ~(ICL_EVENTMASK(eventID));
2209     return 0;
2210 }
2211
2212 /* return indication of whether a particular event ID is enabled
2213  * for tracing.  If *getValuep is set to 0, the event is disabled,
2214  * otherwise it is enabled.  All events start out enabled by default.
2215  */
2216 icl_GetEnable(setp, eventID, getValuep)
2217   struct afs_icl_set *setp;
2218   afs_int32 eventID;
2219   int *getValuep;
2220 {
2221     if (!ICL_EVENTOK(setp, eventID)) {
2222         return -1;
2223     }
2224     if (setp->eventFlags[ICL_EVENTBYTE(eventID)] & ICL_EVENTMASK(eventID))
2225         *getValuep = 1;
2226     else
2227         *getValuep = 0;
2228     return 0;
2229 }
2230
2231 /* hold and release event sets */
2232 icl_SetHold(setp)
2233   register struct afs_icl_set *setp;
2234 {
2235     setp->refCount++;
2236     return 0;
2237 }
2238
2239 /* free a set.  Called with icl_lock locked */
2240 icl_ZapSet(setp)
2241   register struct afs_icl_set *setp;
2242 {
2243     register struct afs_icl_set **lpp, *tp;
2244     int i;
2245     register struct afs_icl_log *tlp;
2246
2247     for(lpp = &icl_allSets, tp = *lpp; tp; lpp = &tp->nextp, tp = *lpp) {
2248         if (tp == setp) {
2249             /* found the dude we want to remove */
2250             *lpp = setp->nextp;
2251             osi_Free(setp->name, 1+strlen(setp->name));
2252             osi_Free(setp->eventFlags, ICL_EVENTBYTES(setp->nevents));
2253             for(i=0; i < ICL_LOGSPERSET; i++) {
2254                 if (tlp = setp->logs[i])
2255                     icl_LogReleNL(tlp);
2256             }
2257             osi_Free(setp, sizeof(struct afs_icl_set));
2258             break;      /* won't find it twice */
2259         }
2260     }
2261     return 0;
2262 }
2263
2264 /* do the release, watching for deleted entries */
2265 icl_SetRele(setp)
2266   register struct afs_icl_set *setp;
2267 {
2268     if (--setp->refCount == 0 && (setp->states & ICL_SETF_DELETED)) {
2269         icl_ZapSet(setp);       /* destroys setp's lock! */
2270     }
2271     return 0;
2272 }
2273
2274 /* free a set entry, dropping its reference count */
2275 icl_SetFree(setp)
2276   register struct afs_icl_set *setp;
2277 {
2278     setp->states |= ICL_SETF_DELETED;
2279     icl_SetRele(setp);
2280     return 0;
2281 }
2282
2283 /* find a set by name, returning it held */
2284 struct afs_icl_set *icl_FindSet(name)
2285   char *name;
2286 {
2287     register struct afs_icl_set *tp;
2288
2289     for(tp = icl_allSets; tp; tp=tp->nextp) {
2290         if (strcmp(tp->name, name) == 0) {
2291             /* this is the dude we want */
2292             tp->refCount++;
2293             break;
2294         }
2295     }
2296     return tp;
2297 }
2298
2299 /* zero out all the logs in the set */
2300 icl_ZeroSet(setp)
2301    struct afs_icl_set *setp;
2302 {
2303     register int i;
2304     int code = 0;
2305     int tcode;
2306     struct afs_icl_log *logp;
2307     
2308     for(i = 0; i < ICL_LOGSPERSET; i++) {
2309         logp = setp->logs[i];
2310         if (logp) {
2311             icl_LogHold(logp);
2312             tcode = icl_ZeroLog(logp);
2313             if (tcode != 0) code = tcode;       /* save the last bad one */
2314             icl_LogRele(logp);
2315         }
2316     }
2317     return code;
2318 }
2319
2320 icl_EnumerateSets(aproc, arock)
2321   int (*aproc)();
2322   char *arock;
2323 {
2324     register struct afs_icl_set *tp, *np;
2325     register afs_int32 code;
2326
2327     code = 0;
2328     for(tp = icl_allSets; tp; tp=np) {
2329         tp->refCount++; /* hold this guy */
2330         code = (*aproc)(tp->name, arock, tp);
2331         np = tp->nextp; /* tp may disappear next, but not np */
2332         if (--tp->refCount == 0 && (tp->states & ICL_SETF_DELETED))
2333             icl_ZapSet(tp);
2334         if (code) break;
2335     }
2336     return code;
2337 }
2338
2339 icl_AddLogToSet(setp, newlogp)
2340   struct afs_icl_set *setp;
2341   struct afs_icl_log *newlogp;
2342 {
2343     register int i;
2344     int code = -1;
2345     struct afs_icl_log *logp;
2346     
2347     for(i = 0; i < ICL_LOGSPERSET; i++) {
2348         if (!setp->logs[i]) {
2349             setp->logs[i] = newlogp;
2350             code = i;
2351             icl_LogHold(newlogp);
2352             if (!(setp->states & ICL_SETF_FREED)) {
2353                 /* bump up the number of sets using the log */
2354                 icl_LogUse(newlogp);
2355             }
2356             break;
2357         }
2358     }
2359     return code;
2360 }
2361
2362 icl_SetSetStat(setp, op)
2363   struct afs_icl_set *setp;
2364   int op;
2365 {
2366     int i;
2367     afs_int32 code;
2368     struct afs_icl_log *logp;
2369
2370     switch(op) {
2371     case ICL_OP_SS_ACTIVATE:    /* activate a log */
2372         /*
2373          * If we are not already active, see if we have released
2374          * our demand that the log be allocated (FREED set).  If
2375          * we have, reassert our desire.
2376          */
2377         if (!(setp->states & ICL_SETF_ACTIVE)) {
2378             if (setp->states & ICL_SETF_FREED) {
2379                 /* have to reassert desire for logs */
2380                 for(i = 0; i < ICL_LOGSPERSET; i++) {
2381                     logp = setp->logs[i];
2382                     if (logp) {
2383                         icl_LogHold(logp);
2384                         icl_LogUse(logp);
2385                         icl_LogRele(logp);
2386                     }
2387                 }
2388                 setp->states &= ~ICL_SETF_FREED;
2389             }
2390             setp->states |= ICL_SETF_ACTIVE;
2391         }
2392         code = 0;
2393         break;
2394
2395     case ICL_OP_SS_DEACTIVATE:  /* deactivate a log */
2396         /* this doesn't require anything beyond clearing the ACTIVE flag */
2397         setp->states &= ~ICL_SETF_ACTIVE;
2398         code = 0;
2399         break;
2400
2401     case ICL_OP_SS_FREE:        /* deassert design for log */
2402         /* 
2403          * if we are already in this state, do nothing; otherwise
2404          * deassert desire for log
2405          */
2406         if (setp->states & ICL_SETF_ACTIVE)
2407             code = EINVAL;
2408         else {
2409             if (!(setp->states & ICL_SETF_FREED)) {
2410                 for(i = 0; i < ICL_LOGSPERSET; i++) {
2411                     logp = setp->logs[i];
2412                     if (logp) {
2413                         icl_LogHold(logp);
2414                         icl_LogFreeUse(logp);
2415                         icl_LogRele(logp);
2416                     }
2417                 }
2418                 setp->states |= ICL_SETF_FREED;
2419             }
2420             code = 0;
2421         }
2422         break;
2423
2424     default:
2425         code = EINVAL;
2426     }
2427
2428     return code;
2429 }
2430
2431 struct afs_icl_log *afs_icl_allLogs = 0;
2432
2433 /* hold and release logs */
2434 icl_LogHold(logp)
2435   register struct afs_icl_log *logp;
2436 {
2437     logp->refCount++;
2438     return 0;
2439 }
2440
2441 /* hold and release logs, called with lock already held */
2442 icl_LogHoldNL(logp)
2443   register struct afs_icl_log *logp;
2444 {
2445     logp->refCount++;
2446     return 0;
2447 }
2448
2449 /* keep track of how many sets believe the log itself is allocated */
2450 icl_LogUse(logp)
2451   register struct afs_icl_log *logp;
2452 {
2453     if (logp->setCount == 0) {
2454         /* this is the first set actually using the log -- allocate it */
2455         if (logp->logSize == 0) {
2456             /* we weren't passed in a hint and it wasn't set */
2457             logp->logSize = ICL_DEFAULT_LOGSIZE;
2458         }
2459         logp->datap = (afs_int32 *) osi_Alloc(sizeof(afs_int32) * logp->logSize);
2460     }
2461     logp->setCount++;
2462     return 0;
2463 }
2464
2465 /* decrement the number of real users of the log, free if possible */
2466 icl_LogFreeUse(logp)
2467   register struct afs_icl_log *logp;
2468 {
2469     if (--logp->setCount == 0) {
2470         /* no more users -- free it (but keep log structure around)*/
2471         osi_Free(logp->datap, sizeof(afs_int32) * logp->logSize);
2472         logp->firstUsed = logp->firstFree = 0;
2473         logp->logElements = 0;
2474         logp->datap = (afs_int32 *)0;
2475     }
2476     return 0;
2477 }
2478
2479 /* set the size of the log to 'logSize' */
2480 icl_LogSetSize(logp, logSize)
2481   register struct afs_icl_log *logp;
2482   afs_int32 logSize;
2483 {  
2484     if (!logp->datap) {
2485         /* nothing to worry about since it's not allocated */
2486         logp->logSize = logSize;
2487     }
2488     else {
2489         /* reset log */
2490         logp->firstUsed = logp->firstFree = 0;
2491         logp->logElements = 0;
2492
2493         /* free and allocate a new one */
2494         osi_Free(logp->datap, sizeof(afs_int32) * logp->logSize);
2495         logp->datap = (afs_int32 *) osi_Alloc(sizeof(afs_int32) * logSize);
2496         logp->logSize = logSize;
2497     }
2498
2499     return 0;
2500 }
2501
2502 /* free a log.  Called with icl_lock locked. */
2503 icl_ZapLog(logp)
2504   register struct afs_icl_log *logp;
2505 {
2506     register struct afs_icl_log **lpp, *tp;
2507
2508     for(lpp = &afs_icl_allLogs, tp = *lpp; tp; lpp = &tp->nextp, tp = *lpp) {
2509         if (tp == logp) {
2510             /* found the dude we want to remove */
2511             *lpp = logp->nextp;
2512             osi_Free(logp->name, 1+strlen(logp->name));
2513             osi_Free(logp->datap, logp->logSize * sizeof(afs_int32));
2514             osi_Free(logp, sizeof(struct icl_log));
2515             break;      /* won't find it twice */
2516         }
2517     }
2518     return 0;
2519 }
2520
2521 /* do the release, watching for deleted entries */
2522 icl_LogRele(logp)
2523   register struct afs_icl_log *logp;
2524 {
2525     if (--logp->refCount == 0 && (logp->states & ICL_LOGF_DELETED)) {
2526         icl_ZapLog(logp);       /* destroys logp's lock! */
2527     }
2528     return 0;
2529 }
2530
2531 /* do the release, watching for deleted entries, log already held */
2532 icl_LogReleNL(logp)
2533   register struct afs_icl_log *logp;
2534 {
2535     if (--logp->refCount == 0 && (logp->states & ICL_LOGF_DELETED)) {
2536         icl_ZapLog(logp);       /* destroys logp's lock! */
2537     }
2538     return 0;
2539 }
2540
2541 /* zero out the log */
2542 icl_ZeroLog(logp)
2543   register struct afs_icl_log *logp;
2544 {
2545     logp->firstUsed = logp->firstFree = 0;
2546     logp->logElements = 0;
2547     return 0;
2548 }
2549
2550 /* free a log entry, and drop its reference count */
2551 icl_LogFree(logp)
2552   register struct afs_icl_log *logp;
2553 {
2554     logp->states |= ICL_LOGF_DELETED;
2555     icl_LogRele(logp);
2556     return 0;
2557 }
2558
2559
2560 icl_EnumerateLogs(aproc, arock)
2561   int (*aproc)();
2562   char *arock;
2563 {
2564     register struct afs_icl_log *tp;
2565     register afs_int32 code;
2566
2567     code = 0;
2568     for(tp = afs_icl_allLogs; tp; tp=tp->nextp) {
2569         tp->refCount++; /* hold this guy */
2570         code = (*aproc)(tp->name, arock, tp);
2571         if (--tp->refCount == 0)
2572             icl_ZapLog(tp);
2573         if (code) break;
2574     }
2575     return code;
2576 }
2577
2578
2579 afs_icl_bulkSetinfo_t *GetBulkSetInfo()
2580 {
2581     unsigned int infoSize;
2582
2583     infoSize = sizeof(afs_icl_bulkSetinfo_t) + 
2584         (ICL_RPC_MAX_SETS-1) * sizeof(afs_icl_setinfo_t);
2585     if (!setInfo) 
2586     {
2587         setInfo = (afs_icl_bulkSetinfo_t *)malloc(infoSize);
2588         if (!setInfo)
2589         {
2590             (void) fprintf(stderr, "Could not allocate the memory for bulk set info structure\n");
2591             exit (1);
2592         }
2593     }
2594     bzero((char *)setInfo, infoSize);
2595
2596     return setInfo;
2597 }
2598
2599 afs_icl_bulkLoginfo_t *GetBulkLogInfo()
2600 {   unsigned int infoSize;
2601
2602     infoSize = sizeof(afs_icl_bulkLoginfo_t) + 
2603         (ICL_RPC_MAX_LOGS-1) * sizeof(afs_icl_loginfo_t);
2604     if (!logInfo)
2605     {
2606         logInfo = (afs_icl_bulkLoginfo_t *)malloc(infoSize);
2607         if (!logInfo)
2608         {
2609             (void) fprintf(stderr, "Could not allocate the memory for bulk log info structure\n");
2610             exit (1);
2611         }
2612     }
2613
2614     bzero((char *)logInfo, infoSize);
2615     return logInfo;
2616 }
2617
2618
2619 static DoDump(as, arock)
2620   register struct cmd_syndesc *as;
2621   char *arock;
2622 {
2623     afs_int32 code = 0;
2624     afs_int32 tcode;
2625     afs_int32 waitTime = 10 /* seconds */;
2626     int error = 0;
2627     char *logname;
2628     char *filename;
2629     FILE *outfp = stdout;
2630     time_t startTime;
2631     struct cmd_item *   itemp;
2632
2633     if (geteuid() != 0) {
2634         printf("fstrace must be run as root\n");
2635         exit(1);
2636     }
2637
2638     if (as->parms[3].items)  {
2639         if (!as->parms[1].items)
2640         {
2641             (void) fprintf(stderr, "-sleep can only be used with -follow\n");
2642             return 1;
2643         }
2644         waitTime = strtol(as->parms[3].items->data, 
2645                           (char **)0, 0);
2646     }
2647
2648     if (as->parms[2].items)
2649     {
2650         /* try to open the specified output file */
2651         if ((outfp = fopen(as->parms[2].items->data, "w")) == NULL)
2652         {
2653             (void) fprintf(stderr, "Cannot open file '%s' for writing\n", 
2654                            as->parms[2].items->data);
2655             return 1;
2656         }
2657     }
2658 #ifdef AFS_SGI64_ENV
2659     startTime = time((time_t *) 0);
2660 #else
2661     startTime = time(0);
2662 #endif
2663     (void) fprintf(outfp, "AFS Trace Dump -\n\n   Date: %s\n",
2664                    ctime(&startTime));
2665
2666     if (as->parms[0].items)
2667     {
2668         for (itemp = as->parms[0].items; itemp; itemp = itemp->next)
2669         {
2670             tcode = icl_DumpKernel(outfp, itemp->data);
2671             if (tcode)
2672             {
2673                 (void) fprintf(stderr, "Unable to dump set %s (errno = %d)\n",
2674                                itemp->data, errno);
2675                 code = tcode;
2676             }
2677         }
2678     } else if (as->parms[1].items)  {
2679         logname = as->parms[1].items->data;
2680         code = icl_TailKernel(outfp, logname, waitTime);
2681         if (code) {
2682             (void) fprintf(stderr, "Error tailing kernel log '%s' (errno = %d)\n",
2683                            logname, errno);
2684         }
2685     } else
2686         code = icl_DumpKernel(outfp, NULL);
2687
2688     (void) fprintf(outfp, "\nAFS Trace Dump - %s\n", 
2689                    code ? "FAILED" : "Completed");
2690
2691     if (outfp != stdout)
2692         (void) fclose(outfp);
2693
2694     return code;
2695 }
2696
2697 static void SetUpDump()
2698 {
2699     struct cmd_syndesc *dumpSyntax;
2700     
2701     dumpSyntax = cmd_CreateSyntax("dump", DoDump, (char *)NULL, "dump AFS trace logs");
2702     (void)cmd_AddParm(dumpSyntax, "-set", CMD_LIST, CMD_OPTIONAL, "set_name");
2703     (void)cmd_AddParm(dumpSyntax, "-follow", CMD_SINGLE, CMD_OPTIONAL, "log_name");
2704     (void)cmd_AddParm(dumpSyntax, "-file", CMD_SINGLE, CMD_OPTIONAL, "output_filename");
2705     (void)cmd_AddParm(dumpSyntax, "-sleep", CMD_SINGLE,  CMD_OPTIONAL, "seconds_between_reads");
2706 }
2707
2708 static DoShowLog(as, arock)
2709   register struct cmd_syndesc *as;
2710   char *arock;
2711 {
2712     afs_int32 retVal = 0;
2713     afs_int32 code = 0;
2714     afs_int32 logSize;
2715     int allocated;
2716     int int32flg=0;
2717     struct cmd_item *   itemp;
2718
2719     if (geteuid() != 0) {
2720         printf("fstrace must be run as root\n");
2721         exit(1);
2722     }
2723     if (as->parms[2].items) int32flg = 1;
2724
2725     if (as->parms[0].items)  {
2726         /* enumerate logs for the specified sets */
2727         for (itemp = as->parms[0].items; itemp; itemp = itemp->next) {
2728             (void) fprintf(stdout, "Logs for set '%s':\n", itemp->data);
2729             code = icl_ListLogsBySet(stdout, itemp->data, int32flg);
2730             if (code) {
2731                 (void) fprintf(stderr, "Error in enumerating set %s (errno = %d)\n",
2732                                itemp->data, errno);
2733                 retVal = code;
2734             }
2735         }
2736     }
2737     else if (as->parms[1].items)  {
2738         /* print out log information */
2739         for (itemp = as->parms[1].items; itemp; itemp = itemp->next) {
2740             code = icl_GetLogsize(itemp->data, &logSize, &allocated);
2741             if (!code)
2742                 (void) fprintf(stdout, "%s : %d kbytes (%s)\n", itemp->data,
2743                                logSize/1024, allocated ? "allocated" : "unallocated");
2744             else {
2745                 (void) fprintf(stderr, "Could not find log '%s' (errno = %d)\n", 
2746                                itemp->data, errno);
2747                 retVal = code;
2748             }
2749         }
2750     }
2751     else {
2752         /* show all logs */
2753         (void) fprintf(stdout, "Available logs:\n");
2754         code = icl_ListLogs(stdout, int32flg);
2755         if (code) {
2756             (void) fprintf(stderr, "Error in listing logs (errno = %d)\n", errno);
2757             retVal = code;
2758         }
2759     }
2760
2761     return retVal;
2762 }
2763
2764 static void SetUpShowLog()
2765 {
2766     struct cmd_syndesc *showSyntax;
2767     
2768     showSyntax = cmd_CreateSyntax("lslog", DoShowLog, (char *)NULL,"list available logs");
2769     (void)cmd_AddParm(showSyntax, "-set", CMD_LIST, CMD_OPTIONAL,"set_name");
2770     (void)cmd_AddParm(showSyntax, "-log", CMD_LIST, CMD_OPTIONAL,"log_name");
2771     (void)cmd_AddParm(showSyntax, "-long", CMD_FLAG, CMD_OPTIONAL,"");
2772 }
2773
2774 static DoShowSet(as, arock)
2775   register struct cmd_syndesc *as;
2776   char *arock;
2777 {
2778     afs_int32 retVal = 0;
2779     afs_int32 code = 0;
2780     afs_int32 state;
2781     struct cmd_item *   itemp;
2782
2783     if (geteuid() != 0) {
2784         printf("fstrace must be run as root\n");
2785         exit(1);
2786     }
2787     if (as->parms[0].items)  {
2788         /* print information on the specified sets */
2789         for (itemp = as->parms[0].items; itemp; itemp = itemp->next) {
2790             code = icl_GetSetState(itemp->data, &state);
2791             if (code) {
2792                 (void) fprintf(stderr, "Error getting status on set %s (errno = %d)\n",
2793                                itemp->data, errno);
2794                 retVal = code;
2795             }
2796             else
2797                 (void) fprintf(stdout, "Set %s: %s%s%s\n", itemp->data,
2798                                (state & ICL_SETF_ACTIVE) ? "active" : "inactive",
2799                                (state & ICL_SETF_FREED) ? " (dormant)" : "",
2800                                (state & ICL_SETF_PERSISTENT) ? " persistent" : "");
2801         }
2802     }
2803     else {
2804         /* show all sets */
2805         (void) fprintf(stdout, "Available sets:\n");
2806         code = icl_ListSets(stdout);
2807         if (code) {
2808             (void) fprintf(stderr, "Error in listing sets (errno = %d)\n", errno);
2809             retVal = code;
2810         }
2811     }
2812
2813     return retVal;
2814 }
2815
2816 static void SetUpShowSet()
2817 {
2818     struct cmd_syndesc *showSyntax;
2819     
2820     showSyntax = cmd_CreateSyntax("lsset", DoShowSet, (char *)NULL, "list available event sets");
2821     (void)cmd_AddParm(showSyntax, "-set", CMD_LIST, CMD_OPTIONAL, "set_name");
2822 }
2823
2824 static DoClear(as, arock)
2825   register struct cmd_syndesc *as;
2826   char *arock;
2827 {
2828     afs_int32 retVal = 0;
2829     afs_int32 code = 0;
2830     struct cmd_item *   itemp;
2831
2832     if (geteuid() != 0) {
2833         printf("fstrace must be run as root\n");
2834         exit(1);
2835     }
2836     if (as->parms[0].items)  {
2837         /* clear logs for the specified sets */
2838         for (itemp = as->parms[0].items; itemp; itemp = itemp->next) {
2839             code = icl_ClearSet(itemp->data);
2840             if (code) {
2841                 (void) fprintf(stderr, "Error in clearing set %s (errno = %d)\n",
2842                                itemp->data, errno);
2843                 retVal = code;
2844             }
2845         }
2846     } else if (as->parms[1].items)  {
2847         /* clear specified log */
2848         for (itemp = as->parms[0].items; itemp; itemp = itemp->next) {
2849             code = icl_ClearLog(itemp->data);
2850             if (code) {
2851                 (void) fprintf(stderr, "Error in clearing log %s (errno = %d)\n",
2852                                itemp->data, errno);
2853                 retVal = code;
2854             }
2855         }
2856     }
2857     else {
2858         /* clear all logs */
2859         code = icl_ClearAll();
2860         if (code) {
2861             (void) fprintf(stderr, "Error in clearing logs (errno = %d)\n", errno);
2862             retVal = code;
2863         }
2864     }
2865
2866     return retVal;
2867 }
2868
2869 static void SetUpClear()
2870 {
2871     struct cmd_syndesc *clearSyntax;
2872     
2873     clearSyntax = cmd_CreateSyntax("clear", DoClear, (char *)NULL, "clear logs by logname or by event set");
2874     (void)cmd_AddParm(clearSyntax, "-set", CMD_LIST, CMD_OPTIONAL,"set_name");
2875     (void)cmd_AddParm(clearSyntax, "-log", CMD_LIST, CMD_OPTIONAL, "log_name");
2876 }
2877
2878 static DoSet(as, arock)
2879   register struct cmd_syndesc *as;
2880   char *arock;
2881 {
2882     afs_int32 retVal = 0;
2883     afs_int32 code = 0;
2884     int op;
2885     int doFree = 0;
2886     char *operation;
2887     struct cmd_item *   itemp;
2888
2889     if (geteuid() != 0) {
2890         printf("fstrace must be run as root\n");
2891         exit(1);
2892     }
2893     if (as->parms[1].items) {
2894         op = ICL_OP_SS_ACTIVATE;
2895         operation = "active";
2896     } else if (as->parms[2].items) {
2897         op = ICL_OP_SS_DEACTIVATE;
2898         operation = "inactive";
2899     } else if (as->parms[3].items) {
2900         op = ICL_OP_SS_DEACTIVATE;
2901         operation = "inactive";
2902         doFree = 1;
2903     } else {
2904         /* assume active" */
2905         op = ICL_OP_SS_ACTIVATE;
2906         operation = "active";
2907     }
2908
2909     if (as->parms[0].items)  {
2910         /* activate specified sets */
2911         for (itemp = as->parms[0].items; itemp; itemp = itemp->next) {
2912             code = icl_ChangeSetState(itemp->data, op);
2913             if (code) {
2914                 (void) fprintf(stderr, "cannot set state of %s to %s (errno = %d)\n",
2915                                itemp->data, operation, errno);
2916                 retVal = code;
2917             }
2918             else if (doFree) {
2919                 /* try to make it dormant as well */
2920                 code = icl_ChangeSetState(itemp->data, ICL_OP_SS_FREE);
2921                 if (code) {
2922                     (void) fprintf(stderr, "cannot set state of %s to dormant (errno = %d)\n",
2923                                    itemp->data, errno);
2924                     retVal = code;
2925                 }
2926             }
2927         }
2928     }
2929     else {
2930         /* show all sets */
2931         code = icl_ChangeAllSetState(op);
2932         if (code) {
2933             (void) fprintf(stderr, "cannot set the state of all sets to %s (errno = %d)\n", 
2934                            operation, errno);
2935             retVal = code;
2936         }
2937         else if (doFree) {
2938             /* try to make it dormant as well */
2939             code = icl_ChangeAllSetState(ICL_OP_SS_FREE);
2940             if (code) {
2941                 (void) fprintf(stderr, "cannot set the state of all sets to dormant (errno = %d)\n", errno);
2942                 retVal = code;
2943             }
2944         }
2945     }
2946
2947     return retVal;
2948 }
2949
2950 static void SetUpSet()
2951 {
2952     struct cmd_syndesc *setSyntax;
2953     
2954     setSyntax = cmd_CreateSyntax("setset", DoSet, (char *)NULL,"set state of event sets");
2955     (void)cmd_AddParm(setSyntax, "-set", CMD_LIST, CMD_OPTIONAL,"set_name");
2956     (void)cmd_AddParm(setSyntax, "-active", CMD_FLAG, CMD_OPTIONAL,"");
2957     (void)cmd_AddParm(setSyntax, "-inactive", CMD_FLAG, CMD_OPTIONAL, "");
2958     (void)cmd_AddParm(setSyntax, "-dormant", CMD_FLAG, CMD_OPTIONAL,"");
2959 }
2960
2961 static DoResize(as, arock)
2962   register struct cmd_syndesc *as;
2963   char *arock;
2964 {
2965     afs_int32 retVal = 0;
2966     afs_int32 code = 0;
2967     afs_int32 bufferSize;
2968     struct cmd_item *   itemp;
2969
2970     if (geteuid() != 0) {
2971         printf("fstrace must be run as root\n");
2972         exit(1);
2973     }
2974     /* get buffer size */
2975     bufferSize = atoi(as->parms[1].items->data);
2976     bufferSize *= BUFFER_MULTIPLIER;
2977     if (bufferSize == 0)
2978         bufferSize = ICL_DEFAULT_LOGSIZE;
2979
2980     /* set the size of the specified logs */
2981     if (itemp = as->parms[0].items) {
2982         for (; itemp; itemp = itemp->next) {
2983             code = icl_ChangeLogSize(itemp->data, bufferSize);
2984             if (code) {
2985                 (void) fprintf(stderr, "Error in changing log %s buffer size (errno = %d)\n",
2986                                itemp->data, errno);
2987                 retVal = code;
2988             }
2989         }
2990     } else {
2991         /* Use the only current support log, "cmfx" */
2992         code = icl_ChangeLogSize("cmfx", bufferSize);
2993         if (code) {
2994             (void) fprintf(stderr, "Error in changing log cmfx buffer size (errno = %d)\n", errno);
2995             retVal = code;
2996         }
2997     }
2998
2999     return retVal;
3000 }
3001
3002 static void SetUpResize()
3003 {
3004     struct cmd_syndesc *setsizeSyntax;
3005     
3006     setsizeSyntax = cmd_CreateSyntax("setlog", DoResize, (char *)NULL, "set the size of a log");
3007     (void)cmd_AddParm(setsizeSyntax, "-log", CMD_LIST, CMD_OPTIONAL,"log_name");
3008     (void)cmd_AddParm(setsizeSyntax, "-buffersize", CMD_SINGLE,  CMD_REQUIRED, "1-kilobyte_units");
3009 }
3010
3011 #include "AFS_component_version_number.c"
3012
3013 main(argc, argv)
3014   IN int argc;
3015   IN char *argv[];
3016 {
3017     setlocale(LC_ALL, "");
3018 #ifdef AFS_SGI62_ENV
3019     set_kernel_sizeof_long();
3020 #endif
3021
3022     /* set up user interface then dispatch */
3023     SetUpDump();
3024     SetUpShowLog();
3025     SetUpShowSet();
3026     SetUpClear();
3027     SetUpSet();
3028     SetUpResize();
3029
3030     return(cmd_Dispatch(argc, argv));
3031 }
3032 #else
3033 #include "AFS_component_version_number.c"
3034
3035 main() {
3036   printf("fstrace is NOT supported for this OS\n");
3037 }
3038 #endif
3039
3040