Add command line support for multiple audit logs
[openafs.git] / src / butc / tcmain.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12
13 #include <afs/procmgmt.h>
14
15 #include <roken.h>
16 #include <afs/opr.h>
17
18 #ifdef IGNORE_SOME_GCC_WARNINGS
19 # pragma GCC diagnostic warning "-Wimplicit-function-declaration"
20 #endif
21
22 #ifdef AFS_NT40_ENV
23 #include <WINNT/afsevent.h>
24 #endif
25
26 #include <ctype.h>
27
28 #include <rx/rx.h>
29 #include <rx/rx_globals.h>
30 #include <rx/rxkad.h>
31 #include <rx/xdr.h>
32
33 #include <afs/afsint.h>
34 #include <afs/prs_fs.h>
35 #include <afs/nfs.h>
36 #include <afs/vlserver.h>
37 #include <lwp.h>
38 #include <lock.h>
39 #include <afs/afsutil.h>
40 #include <afs/cellconfig.h>
41 #include <afs/keys.h>
42 #include <afs/volser.h>
43 #include <ubik.h>
44 #include <afs/audit.h>
45 #include <afs/com_err.h>
46 #include <afs/cmd.h>
47 #include <afs/tcdata.h>
48 #include <afs/bubasics.h>
49 #include <afs/budb_errs.h>
50 #include <afs/budb_client.h>
51 #include <afs/bucoord_prototypes.h>
52 #include <afs/butx.h>
53 #include <afs/kautils.h>
54 #include <afs/bc.h>
55
56 #include "error_macros.h"
57 #define XBSA_TCMAIN
58 #include "butc_xbsa.h"
59 #include "butc_prototypes.h"
60 #include "butc_internal.h"
61
62 #define N_SECURITY_OBJECTS 3
63 #define ERRCODE_RANGE 8         /* from error_table.h */
64
65 #define TE_PREFIX  "TE"
66 #define TL_PREFIX  "TL"
67 #define CFG_PREFIX "CFG"
68
69 extern struct ubik_client *cstruct;
70 FILE *logIO, *ErrorlogIO, *centralLogIO, *lastLogIO;
71 char lFile[AFSDIR_PATH_MAX];
72 char logFile[AFSDIR_PATH_MAX + 256];
73 char ErrorlogFile[AFSDIR_PATH_MAX + 256];
74 char lastLogFile[AFSDIR_PATH_MAX + 256];
75 char eFile[AFSDIR_PATH_MAX];
76 char tapeConfigFile[AFSDIR_PATH_MAX];
77 char pFile[AFSDIR_PATH_MAX];
78 int debugLevel = 0;
79 struct tapeConfig globalTapeConfig;
80 struct deviceSyncNode *deviceLatch;
81 char globalCellName[64];
82 char *whoami = "butc";
83
84 /* GLOBAL CONFIGURATION PARAMETERS */
85 int dump_namecheck;
86 int queryoperator;
87 int autoQuery;
88 int isafile;
89 int tapemounted;
90 char *opencallout;
91 char *closecallout;
92 char *restoretofile;
93 int forcemultiple;
94
95 int maxpass;
96 #define PASSESMIN  1
97 #define PASSESMAX  10
98 #define PASSESDFLT 2
99 afs_int32 groupId;
100 #define MINGROUPID 0x1
101 #define MAXGROUPID 0x7FFFFFFF
102 afs_int32 statusSize;
103 #define MINSTATUS  1
104 #define MAXSTATUS  0x7fffffff
105 afs_int32 BufferSize;           /* Size in B stored for data */
106 char *centralLogFile;
107 afs_int32 lastLog;              /* Log last pass info */
108 int rxBind = 0;
109 struct afsconf_dir *butc_confdir;
110 int allow_unauth = 0;
111
112 #define ADDRSPERSITE 16         /* Same global is in rx/rx_user.c */
113 afs_uint32 SHostAddrs[ADDRSPERSITE];
114
115 static afs_int32
116 SafeATOL(char *anum)
117 {
118     afs_int32 total;
119     int tc;
120
121     total = 0;
122     while ((tc = *anum)) {
123         if (tc < '0' || tc > '9')
124             return -1;
125         total *= 10;
126         total += (tc - '0');
127         anum++;
128     }
129     return total;
130 }
131
132 /* atocl
133  *      Convert a string into an afs_int32.
134  *      Returned afs_int32 is in Bytes, Kb, Mb, Gb, or Tb. Based on crunit char.
135  *      This routine only converts unsigned float values.
136  *      The returned value is a whole number.
137  * entry:
138  *      numstring - text string to be converted.
139  *      crunit - value returned in 'B', 'K', 'M', 'G', 'T'.
140  *               ' ' or NULL ==> 'B' (bytes).
141  * exit:
142  *      number - returned value in requested crunit - rounded
143  *               to nearest whole number.
144  * fn return value:
145  *       0 - conversion ok
146  *      -1 - error in conversion
147  * notes:
148  *      should deal with signed numbers. Should signal error if no digits
149  *      seen.
150  */
151 int
152 atocl(char *numstring, char crunit, afs_int32 *number)
153 {
154     float total;
155     afs_int32 runits;
156     char cunit;
157     afs_int32 units;
158     afs_int32 count;
159     char rest[256];
160
161     /* Determine which units to report in */
162     switch (crunit) {
163     case 't':
164     case 'T':
165         runits = 12;
166         break;
167
168     case 'g':
169     case 'G':
170         runits = 9;
171         break;
172
173     case 'm':
174     case 'M':
175         runits = 6;
176         break;
177
178     case 'k':
179     case 'K':
180         runits = 3;
181         break;
182
183     case 'b':
184     case 'B':
185     case ' ':
186     case 0:
187         runits = 0;
188         break;
189
190     default:
191         return (-1);
192     }
193
194     count =
195         sscanf(numstring, "%f%c%s", &total, &cunit, rest);
196     if ((count > 2) || (count <= 0))
197         return -1;
198     if (count == 1)
199         cunit = 'B';            /* bytes */
200
201     switch (cunit) {
202     case 't':
203     case 'T':
204         units = 12;
205         break;
206
207     case 'g':
208     case 'G':
209         units = 9;
210         break;
211
212     case 'm':
213     case 'M':
214         units = 6;
215         break;
216
217     case 'k':
218     case 'K':
219         units = 3;
220         break;
221
222     case 'b':
223     case 'B':
224     case ' ':
225     case 0:
226         units = 0;
227         break;
228
229     default:
230         return (-1);
231     }
232
233     /* Go to correct unit */
234     for (; units < runits; units += 3)
235         total /= 1024.0;
236     for (; units > runits; units -= 3)
237         total *= 1024.0;
238
239     total += 0.5;               /* Round up */
240     if ((total >= 2147483648.0) || (total < 0)) /* Don't go over 2G */
241         *number = 2147483647;
242     else
243         *number = total;
244
245     return (0);
246 }
247
248 static int
249 stringNowReplace(char *logFile, char *deviceName)
250 {
251     char *pos = 0;
252     char storeDevice[256];
253     int mvFlag = 0, devPrefLen;
254 #ifdef AFS_NT40_ENV
255     char devPrefix[] = "\\\\.";
256 #else
257     char devPrefix[] = "/dev";
258 #endif
259
260     devPrefLen = strlen(devPrefix);
261     strcpy(storeDevice, deviceName);
262     if (strncmp(deviceName, devPrefix, devPrefLen) == 0) {
263         deviceName += devPrefLen;
264         mvFlag++;
265     }
266     while ((pos = strchr(deviceName, devPrefix[0])))    /* look for / or \ */
267         *pos = '_';
268     strcat(logFile, deviceName);
269     /* now put back deviceName to the way it was */
270     if (mvFlag)
271         deviceName -= devPrefLen;
272
273     strcpy(deviceName, storeDevice);
274
275     return (0);
276 }
277
278
279 /* GetDeviceConfig
280  *      get the configuration information for a particular tape device
281  *      as specified by the portoffset
282  * entry:
283  *      filename - full pathname of file containing the tape device
284  *              configuration information
285  *      config - for return results
286  *      portOffset - for which configuration is required
287  * notes:
288  *      logging not available when this routine is called
289  *      caller return value checks
290  * exit:
291  *      0 => Found entry with same port, return info in config.
292  *     -1 => Error encountered trying to read the file.
293  *      1 => Desired entry does not exist or file does not exist.
294  */
295
296 #define LINESIZE        256
297 static afs_int32
298 GetDeviceConfig(char *filename, struct tapeConfig *config, afs_int32 portOffset)
299 {
300     FILE *devFile = 0;
301     char line[LINESIZE];
302     char devName[LINESIZE], tcapacity[LINESIZE], tfmsize[LINESIZE],
303         trest[LINESIZE];
304     afs_int32 aport;
305     afs_int32 capacity;
306     afs_int32 fmSize;
307     afs_int32 code = 0, count;
308
309     /* Initialize the config struct */
310     config->capacity = 0;
311     config->fileMarkSize = 0;
312     config->portOffset = portOffset;
313     strcpy(config->device, "");
314
315     devFile = fopen(filename, "r");
316     if (!devFile) {
317         if (errno == ENOENT)
318             ERROR_EXIT(1);
319         fprintf(stderr, "Error %d: Can't open %s\n", errno, filename);
320         ERROR_EXIT(-1);
321     }
322
323     while (fgets(line, LINESIZE - 1, devFile)) {
324         count =
325             sscanf(line, "%s %s %s %u%s\n", tcapacity, tfmsize, devName,
326                    &aport, trest);
327
328         if (count == 4 || count == 5) {
329             if (atocl(tcapacity, 'K', &capacity)) {
330                 fprintf(stderr,
331                         "tapeconfig: Tape capacity parse error in: %s\n",
332                         line);
333                 ERROR_EXIT(-1);
334             }
335             if (atocl(tfmsize, 'B', &fmSize)) {
336                 fprintf(stderr,
337                         "tapeconfig: File-mark size parse error in: %s\n",
338                         line);
339                 ERROR_EXIT(-1);
340             }
341         } else {
342             count = sscanf(line, "%s %u%s\n", devName, &aport, trest);
343             if (count == 2 || count == 3) {
344                 capacity = 0x7fffffff;
345                 fmSize = 0;
346             } else {
347                 fprintf(stderr, "tapeconfig: Parse error in: %s\n", line);
348                 ERROR_EXIT(-1);
349             }
350         }
351
352         if ((aport < 0) || (aport > BC_MAXPORTOFFSET)) {
353             fprintf(stderr, "tapeconfig: Port offset parse error in: %s\n",
354                     line);
355             ERROR_EXIT(-1);
356         }
357
358         if (aport != portOffset)
359             continue;
360
361         if (fmSize < 0) {
362             fprintf(stderr, "Invalid file mark size, %d, in: %s\n", fmSize,
363                     line);
364             ERROR_EXIT(-1);
365         }
366
367         config->capacity = capacity;
368         config->fileMarkSize = fmSize;
369         config->portOffset = aport;
370         strncpy(config->device, devName, 100);
371
372         ERROR_EXIT(0);
373     }
374
375     /* fprintf(stderr, "Can't find tapeconfig entry for port offset %d\n", portOffset); */
376     ERROR_EXIT(1);
377
378   error_exit:
379     if (devFile)
380         fclose(devFile);
381     return (code);
382 }
383
384 /* GetConfigParams
385  */
386 static afs_int32
387 GetConfigParams(char *filename, afs_int32 port)
388 {
389     char paramFile[AFSDIR_PATH_MAX + 257];
390     FILE *devFile = 0;
391     char line[LINESIZE], cmd[LINESIZE], value[LINESIZE];
392     afs_int32 code = 0;
393     int cnt;
394
395     /* DEFAULT SETTINGS FOR GLOBAL PARAMETERS */
396     dump_namecheck = 1;         /* check tape name on dumps */
397     queryoperator = 1;          /* can question operator */
398     autoQuery = 1;              /* prompt for first tape */
399     isafile = 0;                /* Do not dump to a file */
400     opencallout = NULL;         /* open  callout routine */
401     closecallout = NULL;        /* close callout routine */
402     tapemounted = 0;            /* tape is not mounted */
403 #ifdef xbsa
404     BufferSize = (CONF_XBSA ? XBSADFLTBUFFER : BUTM_BLOCKSIZE);
405     dumpRestAuthnLevel = rpc_c_protect_level_default;
406     xbsaObjectOwner = NULL;     /* bsaObjectOwner */
407     appObjectOwner = NULL;      /* appObjectOwner */
408     adsmServerName = NULL;      /* TSM server name - same as ADSM */
409     xbsaSecToken = NULL;        /* XBSA sercurity token */
410     xbsalGName = NULL;          /* XBSA IGName */
411 #else
412     BufferSize = BUTM_BLOCKSIZE;
413 #endif /*xbsa */
414     centralLogFile = NULL;      /* Log for all butcs */
415     centralLogIO = 0;           /* Log for all butcs */
416     statusSize = 0;             /* size before status message */
417     maxpass = PASSESDFLT;       /* dump passes */
418     lastLog = 0;                /* separate log for last pass */
419     lastLogIO = 0;              /* separate log for last pass */
420     groupId = 0;                /* Group id for multiple dumps */
421
422     /* Try opening the CFG_<port> file */
423     snprintf(paramFile, sizeof(paramFile), "%s_%d", filename, port);
424     devFile = fopen(paramFile, "r");
425     if (devFile) {
426         /* Set log names to TL_<port>, TL_<port>.lp and TE_<port> */
427         snprintf(logFile, sizeof(logFile), "%s_%d", lFile, port);
428         snprintf(lastLogFile, sizeof(lastLogFile), "%s_%d.lp", lFile, port);
429         snprintf(ErrorlogFile, sizeof(ErrorlogFile), "%s_%d", eFile, port);
430     } else if (CONF_XBSA) {
431         /* If configured as XBSA, a configuration file CFG_<port> must exist */
432         printf("Cannot open configuration file %s", paramFile);
433         ERROR_EXIT(1);
434     } else {
435         /* Try the CFG_<device> name as the device file */
436         strcpy(paramFile, filename);
437         stringNowReplace(paramFile, globalTapeConfig.device);
438         /* Set log names to TL_<device>, TL_<device> and TE_<device> */
439         strcpy(logFile, lFile);
440         stringNowReplace(logFile, globalTapeConfig.device);
441         strcpy(lastLogFile, lFile);
442         stringNowReplace(lastLogFile, globalTapeConfig.device);
443         strcat(lastLogFile, ".lp");
444         strcpy(ErrorlogFile, eFile);
445         stringNowReplace(ErrorlogFile, globalTapeConfig.device);
446
447         /* Now open the device file */
448         devFile = fopen(paramFile, "r");
449         if (!devFile)
450             ERROR_EXIT(0);      /* CFG file doesn't exist for non-XBSA and that's ok */
451     }
452
453     /* Read each line of the Configuration file */
454     while (fgets(line, LINESIZE - 1, devFile)) {
455         cnt = sscanf(line, "%s %s", cmd, value);
456         if (cnt != 2) {
457             if (cnt > 0)
458                 printf("Bad line in %s: %s\n", paramFile, line);
459             continue;
460         }
461
462         for (cnt = 0; cnt < strlen(cmd); cnt++)
463             if (islower(cmd[cnt]))
464                 cmd[cnt] = toupper(cmd[cnt]);
465
466         if (!strcmp(cmd, "NAME_CHECK")) {
467             if (CONF_XBSA) {
468                 printf
469                     ("Warning: The %s parameter is ignored with a Backup Service\n",
470                      cmd);
471                 continue;
472             }
473
474             for (cnt = 0; cnt < strlen(value); cnt++)
475                 if (islower(value[cnt]))
476                     value[cnt] = toupper(value[cnt]);
477
478             if (!strcmp(value, "NO")) {
479                 printf("Dump tape name check is disabled\n");
480                 dump_namecheck = 0;
481             } else {
482                 printf("Dump tape name check is enabled\n");
483                 dump_namecheck = 1;
484             }
485         }
486
487         else if (!strcmp(cmd, "MOUNT")) {
488             if (CONF_XBSA) {
489                 printf
490                     ("Warning: The %s parameter is ignored with a Backup Service\n",
491                      cmd);
492                 continue;
493             }
494
495             opencallout = strdup(value);
496             printf("Tape mount callout routine is %s\n", opencallout);
497         }
498
499         else if (!strcmp(cmd, "UNMOUNT")) {
500             if (CONF_XBSA) {
501                 printf
502                     ("Warning: The %s parameter is ignored with a Backup Service\n",
503                      cmd);
504                 continue;
505             }
506
507             closecallout = strdup(value);
508             printf("Tape unmount callout routine is %s\n", closecallout);
509         }
510
511         else if (!strcmp(cmd, "ASK")) {
512             for (cnt = 0; cnt < strlen(value); cnt++)
513                 if (islower(value[cnt]))
514                     value[cnt] = toupper(value[cnt]);
515
516             if (!strcmp(value, "NO")) {
517                 printf("Operator queries are disabled\n");
518                 queryoperator = 0;
519             } else {
520                 printf("Operator queries are enabled\n");
521                 queryoperator = 1;
522             }
523         }
524
525         else if (!strcmp(cmd, "FILE")) {
526             if (CONF_XBSA) {
527                 printf
528                     ("Warning: The %s parameter is ignored with a Backup Service\n",
529                      cmd);
530                 continue;
531             }
532
533             for (cnt = 0; cnt < strlen(value); cnt++)
534                 if (islower(value[cnt]))
535                     value[cnt] = toupper(value[cnt]);
536
537             if (!strcmp(value, "YES")) {
538                 printf("Will dump to a file\n");
539                 isafile = 1;
540             } else {
541                 printf("Will not dump to a file\n");
542                 isafile = 0;
543             }
544         }
545
546         else if (!strcmp(cmd, "AUTOQUERY")) {
547             if (CONF_XBSA) {
548                 printf
549                     ("Warning: The %s parameter is ignored with a Backup Service\n",
550                      cmd);
551                 continue;
552             }
553
554             for (cnt = 0; cnt < strlen(value); cnt++)
555                 if (islower(value[cnt]))
556                     value[cnt] = toupper(value[cnt]);
557
558             if (!strcmp(value, "NO")) {
559                 printf("Auto query is disabled\n");
560                 autoQuery = 0;
561             } else {
562                 printf("Auto query is enabled\n");
563                 autoQuery = 1;
564             }
565         }
566
567         else if (!strcmp(cmd, "BUFFERSIZE")) {
568             afs_int32 size;
569             afs_int32 numTapeblocks;
570
571             if (!CONF_XBSA) {
572                 if (atocl(value, 'K', &size)) {
573                     fprintf(stderr, "BUFFERSIZE parse error\n");
574                     size = 0;
575                 }
576
577                 /* A tapeblock is 16KB. Determine # of tapeblocks. Then
578                  * determine BufferSize needed for that many tapeblocks.
579                  */
580                 numTapeblocks = size / 16;
581                 if (numTapeblocks <= 0)
582                     numTapeblocks = 1;
583                 printf("BUFFERSIZE is %u KBytes\n", (numTapeblocks * 16));
584                 BufferSize = numTapeblocks * BUTM_BLOCKSIZE;
585             } else {
586 #ifdef xbsa
587                 if (atocl(value, 'B', &size)) {
588                     fprintf(stderr, "BUFFERSIZE parse error\n");
589                     size = 0;
590                 }
591                 if (size < XBSAMINBUFFER)
592                     size = XBSAMINBUFFER;
593                 if (size > XBSAMAXBUFFER)
594                     size = XBSAMAXBUFFER;
595                 printf("XBSA buffer size is %u Bytes\n", size);
596                 BufferSize = size;
597 #endif
598             }
599         }
600 #ifndef xbsa
601         /* All the xbsa spacific parameters */
602         else if (!strcmp(cmd, "TYPE") || !strcmp(cmd, "NODE")
603                  || !strcmp(cmd, "SERVER") || !strcmp(cmd, "PASSWORD")
604                  || !strcmp(cmd, "PASSFILE") || !strcmp(cmd, "MGMTCLASS")) {
605             printf("This binary does not have XBSA support\n");
606             return 1;
607         }
608 #else
609         else if (!strcmp(cmd, "TYPE")) {        /* required for XBSA */
610             if (!CONF_XBSA) {
611                 printf
612                     ("Warning: The %s parameter is ignored with a tape drive\n",
613                      cmd);
614                 continue;
615             }
616
617             for (cnt = 0; (size_t) cnt < strlen(value); cnt++)
618                 if (islower(value[cnt]))
619                     value[cnt] = toupper(value[cnt]);
620
621             if (strcmp(value, "TSM") == 0) {
622                 xbsaType = XBSA_SERVER_TYPE_ADSM;       /* Known XBSA server type */
623             } else {
624                 printf("Configuration file error, %s %s is not recognized\n",
625                        cmd, value);
626                 xbsaType = XBSA_SERVER_TYPE_UNKNOWN;
627             }
628             printf("XBSA type is %s\n",
629                    ((xbsaType ==
630                      XBSA_SERVER_TYPE_UNKNOWN) ? "Unknown" : value));
631         }
632
633         else if (!strcmp(cmd, "NODE")) {
634             if (!CONF_XBSA) {
635                 printf
636                     ("Warning: The %s parameter is ignored with a tape drive\n",
637                      cmd);
638                 continue;
639             }
640             xbsaObjectOwner = strdup(value);
641             printf("XBSA node is %s\n", xbsaObjectOwner);
642         }
643
644         else if (!strcmp(cmd, "SERVER")) {      /* required for XBSA */
645             if (!CONF_XBSA) {
646                 printf
647                     ("Warning: The %s parameter is ignored with a tape drive\n",
648                      cmd);
649                 continue;
650             }
651             adsmServerName = strdup(value);
652             printf("XBSA server is %s\n", adsmServerName);
653         }
654
655         else if (!strcmp(cmd, "PASSWORD")) {    /* This or PASSFILE required for XBSA */
656             if (!CONF_XBSA) {
657                 printf
658                     ("Warning: The %s parameter is ignored with a tape drive\n",
659                      cmd);
660                 continue;
661             }
662             if (xbsaSecToken) {
663                 printf
664                     ("Warning: The %s parameter is ignored. Already read password\n",
665                      cmd);
666                 continue;
667             }
668
669             xbsaSecToken = strdup(value);
670             printf("XBSA Password has been read\n");
671         }
672
673         else if (!strcmp(cmd, "PASSFILE")) {    /* This or PASSWORD required for XBSA */
674             FILE *pwdFile;
675             if (!CONF_XBSA) {
676                 printf
677                     ("Warning: The %s parameter is ignored with a tape drive\n",
678                      cmd);
679                 continue;
680             }
681             if (xbsaSecToken) {
682                 printf
683                     ("Warning: The %s parameter is ignored. Already read password\n",
684                      cmd);
685                 continue;
686             }
687
688             pwdFile = fopen(value, "r");
689             if (!pwdFile) {
690                 printf
691                     ("Configuration file error, cannot open password file %s\n",
692                      value);
693                 ERROR_EXIT(1);
694             }
695             xbsaSecToken = malloc(LINESIZE);
696             if (!fscanf(pwdFile, "%s", xbsaSecToken)) {
697                 printf
698                     ("Configuration file error, cannot read password file %s\n",
699                      value);
700                 ERROR_EXIT(1);
701             }
702             printf("XBSA password retrieved from password file\n");
703         }
704
705         else if (!strcmp(cmd, "MGMTCLASS")) {   /* XBSA */
706             if (!CONF_XBSA) {
707                 printf
708                     ("Warning: The %s parameter is ignored with a tape drive\n",
709                      cmd);
710                 continue;
711             }
712             xbsalGName = strdup(value);
713             printf("XBSA management class is %s\n", xbsalGName);
714         }
715 #endif
716
717         else if (!strcmp(cmd, "MAXPASS")) {
718             maxpass = SafeATOL(value);
719             if (maxpass < PASSESMIN)
720                 maxpass = PASSESMIN;
721             if (maxpass > PASSESMAX)
722                 maxpass = PASSESMAX;
723             printf("MAXPASS is %d\n", maxpass);
724         }
725
726         else if (!strcmp(cmd, "GROUPID")) {
727             groupId = SafeATOL(value);
728             if ((groupId < MINGROUPID) || (groupId > MAXGROUPID)) {
729                 printf("Configuration file error, %s %s is invalid\n", cmd,
730                        value);
731                 ERROR_EXIT(1);
732             }
733             printf("Group Id is %d\n", groupId);
734         }
735
736         else if (!strcmp(cmd, "LASTLOG")) {
737             for (cnt = 0; (size_t) cnt < strlen(value); cnt++)
738                 if (islower(value[cnt]))
739                     value[cnt] = toupper(value[cnt]);
740
741             lastLog = (strcmp(value, "YES") == 0);
742             printf("Will %sgenerate a last log\n", (lastLog ? "" : "not "));
743         }
744
745         else if (!strcmp(cmd, "CENTRALLOG")) {
746             centralLogFile = strdup(value);
747             printf("Central log file is %s\n", centralLogFile);
748         }
749
750         else if (!strcmp(cmd, "STATUS")) {
751             if (atocl(value, 'B', &statusSize)) {
752                 fprintf(stderr, "STATUS parse error\n");
753                 statusSize = 0;
754             }
755             if (statusSize < MINSTATUS)
756                 statusSize = MINSTATUS;
757             if (statusSize > MAXSTATUS)
758                 statusSize = MAXSTATUS;
759         }
760
761         else {
762             printf("Warning: Unrecognized configuration parameter: %s", line);
763         }
764     }                           /*fgets */
765
766     if (statusSize) {
767         /* Statussize is in bytes and requires that BufferSize be set first */
768         statusSize *= BufferSize;
769         if (statusSize < 0)
770             statusSize = 0x7fffffff;    /*max size */
771         printf("Status every %ld Bytes\n", afs_printable_int32_ld(statusSize));
772     }
773
774   error_exit:
775     if (devFile)
776         fclose(devFile);
777
778     /* If the butc is configured as XBSA, check for required parameters */
779 #ifdef xbsa
780     if (!code && CONF_XBSA) {
781         if (xbsaType == XBSA_SERVER_TYPE_UNKNOWN) {
782             printf
783                 ("Configuration file error, the TYPE parameter must be specified, or\n");
784             printf("an entry must exist in %s for port %d\n", tapeConfigFile,
785                    port);
786             code = 1;
787         }
788         if (!adsmServerName) {
789             printf
790                 ("Configuration file error, the SERVER parameter must be specified\n");
791             code = 1;
792         }
793         if (!xbsaSecToken) {
794             printf
795                 ("Configuration file error, the PASSWORD or PASSFILE parameter must be specified\n");
796             code = 1;
797         }
798     }
799 #endif /*xbsa */
800     return (code);
801 }
802
803 #ifdef xbsa
804 static void
805 xbsa_shutdown(int x)
806 {
807     xbsa_Finalize(&butxInfo);
808     exit(0);
809 }
810 #endif
811
812 static int
813 tc_IsLocalRealmMatch(void *rock, char *name, char *inst, char *cell)
814 {
815     struct afsconf_dir *dir = (struct afsconf_dir *)rock;
816     afs_int32 islocal = 0;      /* default to no */
817     int code;
818
819     code = afsconf_IsLocalRealmMatch(dir, &islocal, name, inst, cell);
820     if (code) {
821         TLog(0, "Failed local realm check; code=%d, name=%s, inst=%s, cell=%s\n",
822                  code, name, inst, cell);
823     }
824     return islocal;
825 }
826
827 static int
828 WorkerBee(struct cmd_syndesc *as, void *arock)
829 {
830     afs_int32 code, numClasses;
831     struct rx_securityClass *(nullObjects[1]), **secObjs, **allObjs;
832     struct rx_service *service;
833     time_t tokenExpires;
834     char cellName[64];
835     int localauth;
836     /*process arguments */
837     afs_int32 portOffset = 0;
838 #ifdef AFS_PTHREAD_ENV
839     pthread_t dbWatcherPid;
840     pthread_attr_t tattr;
841     AFS_SIGSET_DECL;
842 #else
843     PROCESS dbWatcherPid;
844 #endif
845     char hoststr[16];
846     afs_uint32 host = htonl(INADDR_ANY);
847     char *auditIface = NULL;
848
849     debugLevel = 0;
850
851     /*initialize the error tables */
852     initialize_KA_error_table();
853     initialize_RXK_error_table();
854     initialize_KTC_error_table();
855     initialize_ACFG_error_table();
856     initialize_CMD_error_table();
857     initialize_VL_error_table();
858     initialize_BUTM_error_table();
859     initialize_BUTC_error_table();
860 #ifdef xbsa
861     initialize_BUTX_error_table();
862 #endif /*xbs */
863     initialize_VOLS_error_table();
864     initialize_BUDB_error_table();
865     initialize_BUCD_error_table();
866
867     if (as->parms[0].items) {
868         portOffset = SafeATOL(as->parms[0].items->data);
869         if (portOffset == -1) {
870             fprintf(stderr, "Illegal port offset '%s'\n",
871                     as->parms[0].items->data);
872             exit(1);
873         } else if (portOffset > BC_MAXPORTOFFSET) {
874             fprintf(stderr, "%u exceeds max port offset %u\n", portOffset,
875                     BC_MAXPORTOFFSET);
876             exit(1);
877         }
878     }
879
880     xbsaType = XBSA_SERVER_TYPE_NONE;   /* default */
881     if (as->parms[3].items) {   /* -device */
882         globalTapeConfig.capacity = 0x7fffffff; /* 2T for max tape capacity */
883         globalTapeConfig.fileMarkSize = 0;
884         globalTapeConfig.portOffset = portOffset;
885         strncpy(globalTapeConfig.device, as->parms[3].items->data, 100);
886         xbsaType = XBSA_SERVER_TYPE_NONE;       /* Not XBSA */
887     } else {
888         /* Search for an entry in tapeconfig file */
889         code = GetDeviceConfig(tapeConfigFile, &globalTapeConfig, portOffset);
890         if (code == -1) {
891             fprintf(stderr, "Problem in reading config file %s\n",
892                     tapeConfigFile);
893             exit(1);
894         }
895         /* Set xbsaType. If code == 1, no entry was found in the tapeconfig file so
896          * it's an XBSA server. Don't know if its ADSM or not so its unknown.
897          */
898         xbsaType =
899             ((code == 1) ? XBSA_SERVER_TYPE_UNKNOWN : XBSA_SERVER_TYPE_NONE);
900     }
901
902     if (as->parms[6].items) {   /* -restoretofile */
903         restoretofile = strdup(as->parms[6].items->data);
904         printf("Restore to file '%s'\n", restoretofile);
905     }
906
907     /* Go and read the config file: CFG_<device> or CFG_<port>. We will also set
908      * the exact xbsaType within the call (won't be unknown) - double check.
909      */
910     code = GetConfigParams(pFile, portOffset);
911     if (code)
912         exit(code);
913 #ifdef xbsa
914     if (xbsaType == XBSA_SERVER_TYPE_UNKNOWN) {
915         printf
916             ("\nConfiguration file error, the TYPE parameter must be specified, or\n");
917         printf("an entry must exist in %s for port %d\n", tapeConfigFile,
918                portOffset);
919         exit(1);
920     }
921 #else
922     /* Not compiled for XBSA code so we can't support it */
923     if (CONF_XBSA) {
924         printf("\nNo entry found in %s for port %d\n", tapeConfigFile,
925                portOffset);
926         printf("This binary does not have XBSA support\n");
927         exit(1);
928     }
929 #endif
930
931     /* Open the log files. The pathnames were set in GetConfigParams() */
932     logIO = fopen(logFile, "a");
933     if (!logIO) {
934         fprintf(stderr, "Failed to open %s\n", logFile);
935         exit(1);
936     }
937     ErrorlogIO = fopen(ErrorlogFile, "a");
938     if (!ErrorlogIO) {
939         fprintf(stderr, "Failed to open %s\n", ErrorlogFile);
940         exit(1);
941     }
942     if (lastLog) {
943         lastLogIO = fopen(lastLogFile, "a");
944         if (!lastLogIO) {
945             fprintf(stderr, "Failed to open %s\n", lastLogFile);
946             exit(1);
947         }
948     }
949     if (centralLogFile) {
950         struct stat sbuf;
951         afs_int32 statcode;
952 #ifndef AFS_NT40_ENV
953         char *path;
954 #endif
955
956         statcode = stat(centralLogFile, &sbuf);
957         centralLogIO = fopen(centralLogFile, "a");
958         if (!centralLogIO) {
959             fprintf(stderr, "Failed to open %s; error %d\n", centralLogFile,
960                     errno);
961             exit(1);
962         }
963 #ifndef AFS_NT40_ENV
964         /* Make sure it is not in AFS, has to have been created first */
965         path = malloc(AFSDIR_PATH_MAX);
966         if (path == NULL || !realpath(centralLogFile, path)) {
967             fprintf(stderr,
968                     "Warning: can't determine real path of '%s' (%d)\n",
969                     centralLogFile, errno);
970         } else {
971             if (strncmp(path, "/afs/", 5) == 0) {
972                 fprintf(stderr, "The central log '%s' should not be in AFS\n",
973                         centralLogFile);
974                 exit(1);
975             }
976         }
977         free(path);
978 #endif
979
980         /* Write header if created it */
981         if (statcode) {
982             char *h1 =
983                 "TASK   START DATE/TIME      END DATE/TIME        ELAPSED   VOLUMESET\n";
984             char *h2 =
985                 "-----  -------------------  -------------------  --------  ---------\n";
986             /* File didn't exist before so write the header lines */
987             fwrite(h1, strlen(h1), 1, centralLogIO);
988             fwrite(h2, strlen(h2), 1, centralLogIO);
989             fflush(centralLogIO);
990         }
991     }
992
993     /* Open the configuration directory */
994     butc_confdir = afsconf_Open(AFSDIR_SERVER_ETC_DIRPATH);
995     if (butc_confdir == NULL) {
996         TLog(0, "Failed to open server configuration directory");
997         exit(1);
998     }
999
1000     if (afsconf_CountKeys(butc_confdir) == 0) {
1001         TLog(0, "WARNING: No encryption keys found! "
1002                 "All authenticated accesses will fail. "
1003                 "Run akeyconvert or asetkey to import encryption keys.\n");
1004     }
1005
1006     /* Start auditing */
1007     osi_audit_init();
1008     /* Process -audit-interface and -auditlog */
1009     if (as->parms[10].items != NULL)
1010         auditIface = as->parms[10].items->data;
1011
1012     if (as->parms[9].items != NULL) {
1013         int code;
1014         code = osi_audit_cmd_Options(auditIface, as->parms[9].items);
1015         if (code) {
1016             TLog(0, "Error processing -audit-interface or -auditlog parameters");
1017             exit(1);
1018         }
1019     }
1020
1021     osi_audit_open();
1022     osi_audit(TC_StartEvent, 0, AUD_END);
1023     osi_audit_set_user_check(butc_confdir, tc_IsLocalRealmMatch);
1024
1025     if (as->parms[1].items) {
1026         debugLevel = SafeATOL(as->parms[1].items->data);
1027         if (debugLevel == -1) {
1028             TLog(0, "Illegal debug level '%s'\n", as->parms[1].items->data);
1029             exit(1);
1030         }
1031     }
1032 #ifdef xbsa
1033     /* Setup XBSA library interface */
1034     if (CONF_XBSA) {
1035         afs_int32 rc;
1036         rc = xbsa_MountLibrary(&butxInfo, xbsaType);
1037         if (rc != XBSA_SUCCESS) {
1038             TapeLog(0, 0, rc, 0, "Unable to mount the XBSA library\n");
1039             return (1);
1040         }
1041
1042         forcemultiple = (as->parms[7].items ? 1 : 0);/*-xbsaforcemultiple */
1043         if (forcemultiple)
1044             printf("Force XBSA multiple server support\n");
1045
1046         rc = InitToServer(0 /*taskid */ , &butxInfo, adsmServerName);
1047         if (rc != XBSA_SUCCESS)
1048             return (1);
1049         (void)signal(SIGINT, xbsa_shutdown);
1050         (void)signal(SIGHUP, xbsa_shutdown);
1051     }
1052 #endif /*xbsa */
1053
1054     /* cell switch */
1055     if (as->parms[2].items)
1056         strncpy(cellName, as->parms[2].items->data, sizeof(cellName));
1057     else
1058         cellName[0] = '\0';
1059
1060     if (as->parms[4].items)
1061         autoQuery = 0;
1062
1063     localauth = (as->parms[5].items ? 1 : 0);
1064     rxBind = (as->parms[8].items ? 1 : 0);
1065     allow_unauth = (as->parms[11].items ? 1 : 0);
1066
1067     if (!allow_unauth && !localauth) {
1068         const char *errstr = "Neither -localauth nor -allow_unauthenticated was provided; refusing to start in unintended insecure configuration\n";
1069         TLog(0, "%s", (char *)errstr);
1070         exit(1);
1071     }
1072
1073     if (rxBind) {
1074         afs_int32 ccode;
1075         if (AFSDIR_SERVER_NETRESTRICT_FILEPATH ||
1076             AFSDIR_SERVER_NETINFO_FILEPATH) {
1077             char reason[1024];
1078             ccode = afsconf_ParseNetFiles(SHostAddrs, NULL, NULL,
1079                                           ADDRSPERSITE, reason,
1080                                           AFSDIR_SERVER_NETINFO_FILEPATH,
1081                                           AFSDIR_SERVER_NETRESTRICT_FILEPATH);
1082         } else
1083         {
1084             ccode = rx_getAllAddr(SHostAddrs, ADDRSPERSITE);
1085         }
1086         if (ccode == 1)
1087             host = SHostAddrs[0];
1088     }
1089
1090     TLog(0, "butc binding rx to %s:%d\n",
1091          afs_inet_ntoa_r(host, hoststr), BC_TAPEPORT + portOffset);
1092     code = rx_InitHost(host, htons(BC_TAPEPORT + portOffset));
1093     if (code) {
1094         TapeLog(0, 0, code, 0, "rx init failed on port %u\n",
1095                 BC_TAPEPORT + portOffset);
1096         exit(1);
1097     }
1098     rx_SetRxDeadTime(150);
1099
1100     /* Establish connection with the vldb server */
1101     code = vldbClientInit(0, localauth, cellName, &cstruct, &tokenExpires);
1102     if (code) {
1103         TapeLog(0, 0, code, 0, "Can't access vldb\n");
1104         return code;
1105     }
1106
1107     strcpy(globalCellName, cellName);
1108
1109     /*initialize the dumpNode list */
1110     InitNodeList(portOffset);
1111
1112     deviceLatch = malloc(sizeof(struct deviceSyncNode));
1113     Lock_Init(&(deviceLatch->lock));
1114     deviceLatch->flags = 0;
1115
1116     /* initialize database support, volume support, and logs */
1117
1118     /*
1119      * Create security objects for the Rx server functionality.  Historically
1120      * this was a single rxnull security object, since the tape controller was
1121      * run by an operator that had local access to the tape device and some
1122      * administrative privilege in the cell (to be able to perform volume-level
1123      * accesses), but on a machine that was not necessarily trusted to hold the
1124      * cell-wide key.
1125      *
1126      * Such a configuration is, of course, insecure because anyone can make
1127      * inbound RPCs and manipulate the database, including creating bogus
1128      * dumps and restoring them!  Additionally, in modern usage, butc is
1129      * frequently run with -localauth to authenticate its outbound connections
1130      * to the volservers and budb with the cell-wide key, in which case the
1131      * cell-wide key is present and could be used to authenticate incoming
1132      * connections as well.
1133      *
1134      * If -localauth is in use, create the full barrage of server security
1135      * objects, including rxkad, so that inbound connections can be verified
1136      * to only be made by authenticated clients.  Otherwise, only the rxnull
1137      * class is in use with a single server security object.  Note that butc
1138      * will refuse to start in this configuration unless the
1139      * "-allow_unauthenticated" flag is provided, indicating that the operator
1140      * has ensured that incoming connections are appropriately restricted by
1141      * firewall configuration or network topology.
1142      */
1143
1144     if (allow_unauth) {
1145         nullObjects[RX_SECIDX_NULL] = rxnull_NewServerSecurityObject();
1146         if (!nullObjects[RX_SECIDX_NULL]) {
1147             TLog(0, "rxnull_NewServerSecurityObject");
1148             exit(1);
1149         }
1150         numClasses = 1;
1151         secObjs = nullObjects;
1152     } else {
1153         /* Must be -localauth, so the cell keys are available. */
1154         afsconf_BuildServerSecurityObjects(butc_confdir, &allObjs, &numClasses);
1155         secObjs = allObjs;
1156     }
1157
1158     service =
1159         rx_NewServiceHost(host, 0, 1, "BUTC", secObjs, numClasses, TC_ExecuteRequest);
1160     if (!service) {
1161         TLog(0, "rx_NewService");
1162         exit(1);
1163     }
1164     rx_SetMaxProcs(service, 4);
1165
1166     /* Establish connection to the backup database */
1167     code = udbClientInit(0, localauth, cellName);
1168     if (code) {
1169         TapeLog(0, 0, code, 0, "Can't access backup database\n");
1170         exit(1);
1171     }
1172     /* This call is here to verify that we are authentiated.
1173      * The call does nothing and will return BUDB_NOTPERMITTED
1174      * if we don't belong.
1175      */
1176     code = bcdb_deleteDump(0, 0, 0, 0);
1177     if (code == BUDB_NOTPERMITTED) {
1178         TapeLog(0, 0, code, 0, "Can't access backup database\n");
1179         exit(1);
1180     }
1181
1182     initStatus();
1183 #ifdef AFS_PTHREAD_ENV
1184     code = pthread_attr_init(&tattr);
1185     if (code) {
1186         TapeLog(0, 0, code, 0,
1187                 "Can't pthread_attr_init database monitor task");
1188         exit(1);
1189     }
1190     code = pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED);
1191     if (code) {
1192         TapeLog(0, 0, code, 0,
1193                 "Can't pthread_attr_setdetachstate database monitor task");
1194         exit(1);
1195     }
1196     AFS_SIGSET_CLEAR();
1197     code = pthread_create(&dbWatcherPid, &tattr, dbWatcher, (void *)2);
1198     AFS_SIGSET_RESTORE();
1199 #else
1200     code =
1201         LWP_CreateProcess(dbWatcher, 20480, LWP_NORMAL_PRIORITY, (void *)2,
1202                           "dbWatcher", &dbWatcherPid);
1203 #endif
1204     if (code) {
1205         TapeLog(0, 0, code, 0, "Can't create database monitor task");
1206         exit(1);
1207     }
1208
1209     TLog(0, "Starting Tape Coordinator: Port offset %u   Debug level %u\n",
1210          portOffset, debugLevel);
1211     TLog(0, "Token expires: %s\n", cTIME(&tokenExpires));
1212
1213     rx_StartServer(1);          /* Donate this process to the server process pool */
1214     TLog(0, "Error: StartServer returned");
1215     exit(1);
1216 }
1217
1218 #ifndef AFS_NT40_ENV
1219 #include "AFS_component_version_number.c"
1220 #endif
1221
1222 int
1223 main(int argc, char **argv)
1224 {
1225     struct cmd_syndesc *ts;
1226     struct cmd_item *ti;
1227
1228 #ifdef  AFS_AIX32_ENV
1229     /*
1230      * The following signal action for AIX is necessary so that in case of a
1231      * crash (i.e. core is generated) we can include the user's data section
1232      * in the core dump. Unfortunately, by default, only a partial core is
1233      * generated which, in many cases, isn't too useful.
1234      */
1235     struct sigaction nsa;
1236
1237     sigemptyset(&nsa.sa_mask);
1238     nsa.sa_handler = SIG_DFL;
1239     nsa.sa_flags = SA_FULLDUMP;
1240     sigaction(SIGSEGV, &nsa, NULL);
1241     sigaction(SIGABRT, &nsa, NULL);
1242 #endif
1243
1244     setlinebuf(stdout);
1245
1246     ts = cmd_CreateSyntax(NULL, WorkerBee, NULL, 0, "tape coordinator");
1247     cmd_AddParm(ts, "-port", CMD_SINGLE, CMD_OPTIONAL, "port offset");
1248     cmd_AddParm(ts, "-debuglevel", CMD_SINGLE, CMD_OPTIONAL, "0 | 1 | 2");
1249     cmd_AddParm(ts, "-cell", CMD_SINGLE, CMD_OPTIONAL, "cell name");
1250     cmd_AddParm(ts, "-device", CMD_SINGLE, (CMD_OPTIONAL | CMD_HIDE),
1251                 "tape device path");
1252     cmd_AddParm(ts, "-noautoquery", CMD_FLAG, CMD_OPTIONAL,
1253                 "do not query operator for first tape");
1254     cmd_AddParm(ts, "-localauth", CMD_FLAG, CMD_OPTIONAL,
1255                 "create tickets from KeyFile");
1256     cmd_AddParm(ts, "-restoretofile", CMD_SINGLE, (CMD_OPTIONAL | CMD_HIDE),
1257                 "file to restore to");
1258     cmd_AddParm(ts, "-xbsaforcemultiple", CMD_FLAG, (CMD_OPTIONAL | CMD_HIDE),
1259                 "Force multiple XBSA server support");
1260     cmd_AddParm(ts, "-rxbind", CMD_FLAG, CMD_OPTIONAL,
1261                 "bind Rx socket");
1262     cmd_AddParm(ts, "-auditlog", CMD_LIST, CMD_OPTIONAL,
1263                 "[interface:]path[:options]");
1264     cmd_AddParm(ts, "-audit-interface", CMD_SINGLE, CMD_OPTIONAL,
1265                 "default interface");
1266     cmd_AddParm(ts, "-allow_unauthenticated", CMD_FLAG, CMD_OPTIONAL,
1267                 "allow unauthenticated inbound RPCs (requires firewalling)");
1268
1269     /* Initialize dirpaths */
1270     if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
1271 #ifdef AFS_NT40_ENV
1272         ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
1273 #endif
1274         fprintf(stderr, "Unable to obtain AFS server directory.\n");
1275         exit(2);
1276     }
1277
1278     /* setup the file paths */
1279     strcompose(eFile, AFSDIR_PATH_MAX, AFSDIR_SERVER_BACKUP_DIRPATH, "/",
1280                TE_PREFIX, (char *)NULL);
1281     strcompose(lFile, AFSDIR_PATH_MAX, AFSDIR_SERVER_BACKUP_DIRPATH, "/",
1282                TL_PREFIX, (char *)NULL);
1283     strcompose(pFile, AFSDIR_PATH_MAX, AFSDIR_SERVER_BACKUP_DIRPATH, "/",
1284                CFG_PREFIX, (char *)NULL);
1285     strcpy(tapeConfigFile, AFSDIR_SERVER_TAPECONFIG_FILEPATH);
1286
1287     /* special case "no args" case since cmd_dispatch gives help message
1288      * instead
1289      */
1290     if (argc == 1) {
1291         ts = calloc(1, sizeof(struct cmd_syndesc));
1292
1293         ti = malloc(sizeof(struct cmd_item));
1294         ti->next = 0;
1295         ti->data = "0";
1296         ts->parms[0].items = ti;
1297         ti = malloc(sizeof(struct cmd_item));
1298         ti->next = 0;
1299         ti->data = "0";
1300         ts->parms[1].items = ti;
1301         ts->parms[2].items = NULL;
1302         ts->parms[3].items = NULL;
1303         ts->parms[4].items = NULL;
1304         ts->parms[5].items = NULL;
1305         return WorkerBee(ts, NULL);
1306     } else
1307         return cmd_Dispatch(argc, argv);
1308 }