budb: Don't leak memory
[openafs.git] / src / budb / server.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  *
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afsconfig.h>
11 #include <afs/param.h>
12 #include <afs/stds.h>
13
14 #include <roken.h>
15
16 #include <fcntl.h>
17
18 #ifdef AFS_NT40_ENV
19 #include <WINNT/afsevent.h>
20 #endif
21 #include <afs/cmd.h>
22 #include <lwp.h>
23 #include <ubik.h>
24 #include <rx/xdr.h>
25 #include <rx/rx.h>
26 #include <rx/rxkad.h>
27 #include <rx/rx_globals.h>
28 #include <afs/cellconfig.h>
29 #include <afs/bubasics.h>
30 #include <afs/afsutil.h>
31 #include <afs/com_err.h>
32 #include <afs/audit.h>
33
34 #include "budb_errs.h"
35 #include "database.h"
36 #include "error_macros.h"
37 #include "budb_internal.h"
38 #include "globals.h"
39
40 struct ubik_dbase *BU_dbase;
41 struct afsconf_dir *BU_conf;    /* for getting cell info */
42
43 int argHandler(struct cmd_syndesc *, void *);
44 int truncateDatabase(void);
45 int parseServerList(struct cmd_item *);
46
47 char lcell[MAXKTCREALMLEN];
48 afs_uint32 myHost = 0;
49 int helpOption;
50
51 /* server's global configuration information. This is exported to other
52  * files/routines
53  */
54
55 buServerConfT globalConf;
56 buServerConfP globalConfPtr = &globalConf;
57 char dbDir[AFSDIR_PATH_MAX], cellConfDir[AFSDIR_PATH_MAX];
58 /* debugging control */
59 int debugging = 0;
60
61 int rxBind = 0;
62 int lwps   = 3;
63
64 #define MINLWP  3
65 #define MAXLWP 16
66
67 #define ADDRSPERSITE 16         /* Same global is in rx/rx_user.c */
68 afs_uint32 SHostAddrs[ADDRSPERSITE];
69
70 /* check whether caller is authorized to manage RX statistics */
71 int
72 BU_rxstat_userok(struct rx_call *call)
73 {
74     return afsconf_SuperUser(BU_conf, call, NULL);
75 }
76
77 int
78 convert_cell_to_ubik(struct afsconf_cell *cellinfo, afs_uint32 *myHost,
79                      afs_uint32 *serverList)
80 {
81     int i;
82     char hostname[64];
83     struct hostent *th;
84
85     /* get this host */
86     gethostname(hostname, sizeof(hostname));
87     th = gethostbyname(hostname);
88     if (!th) {
89         printf("prserver: couldn't get address of this host.\n");
90         BUDB_EXIT(1);
91     }
92     memcpy(myHost, th->h_addr, sizeof(afs_uint32));
93
94     for (i = 0; i < cellinfo->numServers; i++)
95         /* omit my host from serverList */
96         if (cellinfo->hostAddr[i].sin_addr.s_addr != *myHost)
97             *serverList++ = cellinfo->hostAddr[i].sin_addr.s_addr;
98
99     *serverList = 0;            /* terminate list */
100     return 0;
101 }
102
103 /* MyBeforeProc
104  *      The whole purpose of MyBeforeProc is to detect
105  *      if the -help option was not within the command line.
106  *      If it were, this routine would never have been called.
107  */
108 static int
109 MyBeforeProc(struct cmd_syndesc *as, void *arock)
110 {
111     helpOption = 0;
112     return 0;
113 }
114
115 /* initializeCommands
116  *      initialize all the supported commands and their arguments
117  */
118
119 void
120 initializeArgHandler(void)
121 {
122     struct cmd_syndesc *cptr;
123
124     cmd_SetBeforeProc(MyBeforeProc, NULL);
125
126     cptr = cmd_CreateSyntax(NULL, argHandler, NULL, "Backup database server");
127
128     cmd_AddParm(cptr, "-database", CMD_SINGLE, CMD_OPTIONAL,
129                 "database directory");
130
131     cmd_AddParm(cptr, "-cellservdb", CMD_SINGLE, CMD_OPTIONAL,
132                 "cell configuration directory");
133
134     cmd_AddParm(cptr, "-resetdb", CMD_FLAG, CMD_OPTIONAL,
135                 "truncate the database");
136
137     cmd_AddParm(cptr, "-noauth", CMD_FLAG, CMD_OPTIONAL,
138                 "run without authentication");
139
140     cmd_AddParm(cptr, "-smallht", CMD_FLAG, CMD_OPTIONAL,
141                 "use small hash tables");
142
143     cmd_AddParm(cptr, "-servers", CMD_LIST, CMD_OPTIONAL,
144                 "list of ubik database servers");
145
146     cmd_AddParm(cptr, "-ubikbuffers", CMD_SINGLE, CMD_OPTIONAL,
147                 "the number of ubik buffers");
148
149     cmd_AddParm(cptr, "-auditlog", CMD_SINGLE, CMD_OPTIONAL,
150                 "audit log path");
151
152     cmd_AddParm(cptr, "-p", CMD_SINGLE, CMD_OPTIONAL,
153                 "number of processes");
154
155     cmd_AddParm(cptr, "-rxbind", CMD_FLAG, CMD_OPTIONAL,
156                 "bind the Rx socket (primary interface only)");
157
158     cmd_AddParm(cptr, "-audit-interface", CMD_SINGLE, CMD_OPTIONAL,
159                 "audit interface (file or sysvmq)");
160 }
161
162 int
163 argHandler(struct cmd_syndesc *as, void *arock)
164 {
165
166     /* globalConfPtr provides the handle for the configuration information */
167
168     /* database directory */
169     if (as->parms[0].items != 0) {
170         globalConfPtr->databaseDirectory =
171             (char *)malloc(strlen(as->parms[0].items->data) + 1);
172         if (globalConfPtr->databaseDirectory == 0)
173             BUDB_EXIT(-1);
174         strcpy(globalConfPtr->databaseDirectory, as->parms[0].items->data);
175     }
176
177     /* -cellservdb, cell configuration directory */
178     if (as->parms[1].items != 0) {
179         globalConfPtr->cellConfigdir =
180             (char *)malloc(strlen(as->parms[1].items->data) + 1);
181         if (globalConfPtr->cellConfigdir == 0)
182             BUDB_EXIT(-1);
183
184         strcpy(globalConfPtr->cellConfigdir, as->parms[1].items->data);
185
186         globalConfPtr->debugFlags |= DF_RECHECKNOAUTH;
187     }
188
189     /* truncate the database */
190     if (as->parms[2].items != 0)
191         truncateDatabase();
192
193     /* run without authentication */
194     if (as->parms[3].items != 0)
195         globalConfPtr->debugFlags |= DF_NOAUTH;
196
197     /* use small hash tables */
198     if (as->parms[4].items != 0)
199         globalConfPtr->debugFlags |= DF_SMALLHT;
200
201     /* user provided list of ubik database servers */
202     if (as->parms[5].items != 0)
203         parseServerList(as->parms[5].items);
204
205     /* user provided the number of ubik buffers    */
206     if (as->parms[6].items != 0)
207         ubik_nBuffers = atoi(as->parms[6].items->data);
208     else
209         ubik_nBuffers = 0;
210
211     /* param 7 (-auditlog) handled below */
212
213     /* user provided the number of threads    */
214     if (as->parms[8].items != 0) {
215         lwps = atoi(as->parms[8].items->data);
216         if (lwps > MAXLWP) {
217             printf ("Warning: '-p %d' is too big; using %d instead\n",
218                 lwps, MAXLWP);
219             lwps = MAXLWP;
220         }
221         if (lwps < MINLWP) {
222             printf ("Warning: '-p %d' is too small; using %d instead\n",
223                 lwps, MINLWP);
224             lwps = MINLWP;
225         }
226     }
227
228     /* user provided rxbind option    */
229     if (as->parms[9].items != 0) {
230         rxBind = 1;
231     }
232
233     /* -audit-interface */
234     if (as->parms[10].items != 0) {
235         char *interface = as->parms[10].items->data;
236
237         if (osi_audit_interface(interface)) {
238             printf("Invalid audit interface '%s'\n", interface);
239             BUDB_EXIT(-1);
240         }
241     }
242
243     /* -auditlog */
244     /* needs to be after -audit-interface, so we osi_audit_interface
245      * before we osi_audit_file */
246     if (as->parms[7].items != 0) {
247         char *fileName = as->parms[7].items->data;
248
249         osi_audit_file(fileName);
250     }
251
252     return 0;
253 }
254
255 /* --- */
256
257 int
258 parseServerList(struct cmd_item *itemPtr)
259 {
260     struct cmd_item *save;
261     char **serverArgs;
262     char **ptr;
263     afs_int32 nservers = 0;
264     afs_int32 code = 0;
265
266     save = itemPtr;
267
268     /* compute number of servers in the list */
269     while (itemPtr) {
270         nservers++;
271         itemPtr = itemPtr->next;
272     }
273
274     LogDebug(3, "%d servers\n", nservers);
275
276     /* now can allocate the space for the server arguments */
277     serverArgs = (char **)malloc((nservers + 2) * sizeof(char *));
278     if (serverArgs == 0)
279         ERROR(-1);
280
281     ptr = serverArgs;
282     *ptr++ = "";
283     *ptr++ = "-servers";
284
285     /* now go through and construct the list of servers */
286     itemPtr = save;
287     while (itemPtr) {
288         *ptr++ = itemPtr->data;
289         itemPtr = itemPtr->next;
290     }
291
292     code =
293         ubik_ParseServerList(nservers + 2, serverArgs, &globalConfPtr->myHost,
294                              globalConfPtr->serverList);
295     if (code)
296         ERROR(code);
297
298     /* free space for the server args */
299     free((char *)serverArgs);
300
301   error_exit:
302     return (code);
303 }
304
305 /* truncateDatabase
306  *      truncates just the database file.
307  */
308
309 int
310 truncateDatabase(void)
311 {
312     char *path;
313     afs_int32 code = 0;
314     int fd;
315
316     path = malloc(strlen(globalConfPtr->databaseDirectory) +
317                   strlen(globalConfPtr->databaseName) +
318                   strlen(globalConfPtr->databaseExtension) + 1);
319     if (path == NULL)
320         ERROR(-1);
321
322     /* construct the database name */
323     strcpy(path, globalConfPtr->databaseDirectory);
324     strcat(path, globalConfPtr->databaseName);
325     strcat(path, globalConfPtr->databaseExtension);
326
327     fd = open(path, O_RDWR, 0755);
328     if (!fd) {
329         code = errno;
330     } else {
331         if (ftruncate(fd, 0) != 0) {
332             code = errno;
333         } else
334             close(fd);
335     }
336
337     free(path);
338
339   error_exit:
340     return (code);
341 }
342
343
344 /* --- */
345
346 #include "AFS_component_version_number.c"
347
348 int
349 main(int argc, char **argv)
350 {
351     char *whoami = argv[0];
352     char *dbNamePtr = 0;
353     struct afsconf_cell cellinfo;
354     time_t currentTime;
355     afs_int32 code = 0;
356     afs_uint32 host = ntohl(INADDR_ANY);
357
358     char  clones[MAXHOSTSPERCELL];
359
360     struct rx_service *tservice;
361     struct rx_securityClass **securityClasses;
362     afs_int32 numClasses;
363
364     extern int rx_stackSize;
365
366 #ifdef AFS_NT40_ENV
367     /* initialize winsock */
368     if (afs_winsockInit() < 0) {
369         ReportErrorEventAlt(AFSEVT_SVR_WINSOCK_INIT_FAILED, 0, argv[0], 0);
370         fprintf(stderr, "%s: Couldn't initialize winsock.\n", whoami);
371         exit(1);
372     }
373 #endif
374
375 #ifdef  AFS_AIX32_ENV
376     /*
377      * The following signal action for AIX is necessary so that in case of a
378      * crash (i.e. core is generated) we can include the user's data section
379      * in the core dump. Unfortunately, by default, only a partial core is
380      * generated which, in many cases, isn't too useful.
381      */
382     struct sigaction nsa;
383
384     sigemptyset(&nsa.sa_mask);
385     nsa.sa_handler = SIG_DFL;
386     nsa.sa_flags = SA_FULLDUMP;
387     sigaction(SIGSEGV, &nsa, NULL);
388     sigaction(SIGABRT, &nsa, NULL);
389 #endif
390     osi_audit_init();
391     osi_audit(BUDB_StartEvent, 0, AUD_END);
392
393     initialize_BUDB_error_table();
394     initializeArgHandler();
395
396     /* Initialize dirpaths */
397     if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
398 #ifdef AFS_NT40_ENV
399         ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
400 #endif
401         afs_com_err(whoami, errno, "; Unable to obtain AFS server directory.");
402         exit(2);
403     }
404
405     memset(globalConfPtr, 0, sizeof(*globalConfPtr));
406
407     /* set default configuration values */
408     strcpy(dbDir, AFSDIR_SERVER_DB_DIRPATH);
409     strcat(dbDir, "/");
410     globalConfPtr->databaseDirectory = dbDir;
411     globalConfPtr->databaseName = DEFAULT_DBPREFIX;
412     strcpy(cellConfDir, AFSDIR_SERVER_ETC_DIRPATH);
413     globalConfPtr->cellConfigdir = cellConfDir;
414
415     /* open the log file */
416 /*
417     globalConfPtr->log = fopen(DEFAULT_LOGNAME,"a");
418     if ( globalConfPtr->log == NULL )
419     {
420         printf("Can't open log file %s - aborting\n", DEFAULT_LOGNAME);
421         BUDB_EXIT(-1);
422     }
423 */
424
425     srandom(1);
426
427 #ifdef AFS_PTHREAD_ENV
428     SetLogThreadNumProgram( rx_GetThreadNum );
429 #endif
430
431     /* process the user supplied args */
432     helpOption = 1;
433     code = cmd_Dispatch(argc, argv);
434     if (code)
435         ERROR(code);
436
437     /* exit if there was a help option */
438     if (helpOption)
439         BUDB_EXIT(0);
440
441     /* open the log file */
442     globalConfPtr->log = fopen(AFSDIR_SERVER_BUDBLOG_FILEPATH, "a");
443     if (globalConfPtr->log == NULL) {
444         printf("Can't open log file %s - aborting\n",
445                AFSDIR_SERVER_BUDBLOG_FILEPATH);
446         BUDB_EXIT(-1);
447     }
448
449     /* keep log closed so can remove it */
450
451     fclose(globalConfPtr->log);
452
453     /* open the cell's configuration directory */
454     LogDebug(4, "opening %s\n", globalConfPtr->cellConfigdir);
455
456     BU_conf = afsconf_Open(globalConfPtr->cellConfigdir);
457     if (BU_conf == 0) {
458         LogError(code, "Failed getting cell info\n");
459         afs_com_err(whoami, code, "Failed getting cell info");
460         ERROR(BUDB_NOCELLS);
461     }
462
463     code = afsconf_GetLocalCell(BU_conf, lcell, sizeof(lcell));
464     if (code) {
465         LogError(0, "** Can't determine local cell name!\n");
466         ERROR(code);
467     }
468
469     if (globalConfPtr->myHost == 0) {
470         /* if user hasn't supplied a list of servers, extract server
471          * list from the cell's database
472          */
473
474         LogDebug(1, "Using server list from %s cell database.\n", lcell);
475
476         code = afsconf_GetExtendedCellInfo (BU_conf, lcell, 0, &cellinfo,
477                                             clones);
478         code =
479             convert_cell_to_ubik(&cellinfo, &globalConfPtr->myHost,
480                                  globalConfPtr->serverList);
481         if (code)
482             ERROR(code);
483     }
484
485     /* initialize ubik */
486     ubik_SetClientSecurityProcs(afsconf_ClientAuth, afsconf_UpToDate, BU_conf);
487     ubik_SetServerSecurityProcs(afsconf_BuildServerSecurityObjects,
488                                 afsconf_CheckAuth, BU_conf);
489
490     if (ubik_nBuffers == 0)
491         ubik_nBuffers = 400;
492
493     LogError(0, "Will allocate %d ubik buffers\n", ubik_nBuffers);
494
495     dbNamePtr =
496         (char *)malloc(strlen(globalConfPtr->databaseDirectory) +
497                        strlen(globalConfPtr->databaseName) + 1);
498     if (dbNamePtr == 0)
499         ERROR(-1);
500
501     /* construct the database name */
502     strcpy(dbNamePtr, globalConfPtr->databaseDirectory);
503     strcat(dbNamePtr, globalConfPtr->databaseName);     /* name prefix */
504
505     rx_SetRxDeadTime(60);       /* 60 seconds inactive before timeout */
506
507     if (rxBind) {
508         afs_int32 ccode;
509         if (AFSDIR_SERVER_NETRESTRICT_FILEPATH ||
510             AFSDIR_SERVER_NETINFO_FILEPATH) {
511             char reason[1024];
512             ccode = parseNetFiles(SHostAddrs, NULL, NULL,
513                                            ADDRSPERSITE, reason,
514                                            AFSDIR_SERVER_NETINFO_FILEPATH,
515                                            AFSDIR_SERVER_NETRESTRICT_FILEPATH);
516         } else
517         {
518             ccode = rx_getAllAddr(SHostAddrs, ADDRSPERSITE);
519         }
520         if (ccode == 1) {
521             host = SHostAddrs[0];
522             rx_InitHost(host, htons(AFSCONF_BUDBPORT));
523         }
524     }
525
526     code = ubik_ServerInitByInfo (globalConfPtr->myHost,
527                                   htons(AFSCONF_BUDBPORT),
528                                   &cellinfo,
529                                   clones,
530                                   dbNamePtr,           /* name prefix */
531                                   &BU_dbase);
532
533     if (code) {
534         LogError(code, "Ubik init failed\n");
535         afs_com_err(whoami, code, "Ubik init failed");
536         ERROR(code);
537     }
538
539     afsconf_BuildServerSecurityObjects(BU_conf, &securityClasses, &numClasses);
540
541     /* Disable jumbograms */
542     rx_SetNoJumbo();
543
544     tservice =
545         rx_NewServiceHost(host, 0, BUDB_SERVICE, "BackupDatabase",
546                           securityClasses, numClasses, BUDB_ExecuteRequest);
547
548     if (tservice == (struct rx_service *)0) {
549         LogError(0, "Could not create backup database rx service\n");
550         printf("Could not create backup database rx service\n");
551         BUDB_EXIT(3);
552     }
553     rx_SetMinProcs(tservice, 1);
554     rx_SetMaxProcs(tservice, lwps);
555     rx_SetStackSize(tservice, 10000);
556
557     /* allow super users to manage RX statistics */
558     rx_SetRxStatUserOk(BU_rxstat_userok);
559
560     /* misc. initialization */
561
562     /* database dump synchronization */
563     memset(dumpSyncPtr, 0, sizeof(*dumpSyncPtr));
564     Lock_Init(&dumpSyncPtr->ds_lock);
565
566     rx_StartServer(0);          /* start handling requests */
567
568     code = InitProcs();
569     if (code)
570         ERROR(code);
571
572
573     currentTime = time(0);
574     LogError(0, "Ready to process requests at %s\n", ctime(&currentTime));
575
576     rx_ServerProc(NULL);                /* donate this LWP */
577
578   error_exit:
579     osi_audit(BUDB_FinishEvent, code, AUD_END);
580     return (code);
581 }
582
583 void
584 consistencyCheckDb(void)
585 {
586     /* do consistency checks on structure sizes */
587     if ((sizeof(struct htBlock) > BLOCKSIZE)
588         || (sizeof(struct vfBlock) > BLOCKSIZE)
589         || (sizeof(struct viBlock) > BLOCKSIZE)
590         || (sizeof(struct dBlock) > BLOCKSIZE)
591         || (sizeof(struct tBlock) > BLOCKSIZE)
592         ) {
593         fprintf(stderr, "Block layout error!\n");
594         BUDB_EXIT(99);
595     }
596 }
597
598 void
599 LogDebug(int level, char *fmt, ... )
600 {
601     va_list ap;
602
603     va_start(ap, fmt);
604
605     if (debugging >= level) {
606         /* log normally closed so can remove it */
607         globalConfPtr->log = fopen(AFSDIR_SERVER_BUDBLOG_FILEPATH, "a");
608         if (globalConfPtr->log != NULL) {
609             vfprintf(globalConfPtr->log, fmt, ap);
610             fflush(globalConfPtr->log);
611             fclose(globalConfPtr->log);
612         }
613     }
614     va_end(ap);
615 }
616
617 static char *
618 TimeStamp(time_t t)
619 {
620     struct tm *lt;
621     static char timestamp[20];
622
623     lt = localtime(&t);
624     strftime(timestamp, 20, "%m/%d/%Y %H:%M:%S", lt);
625     return timestamp;
626 }
627
628 void
629 Log(char *fmt, ...)
630 {
631     va_list ap;
632     time_t now;
633
634     va_start(ap, fmt);
635     globalConfPtr->log = fopen(AFSDIR_SERVER_BUDBLOG_FILEPATH, "a");
636     if (globalConfPtr->log != NULL) {
637         now = time(0);
638         fprintf(globalConfPtr->log, "%s ", TimeStamp(now));
639
640         vfprintf(globalConfPtr->log, fmt, ap);
641         fflush(globalConfPtr->log);
642         fclose(globalConfPtr->log);
643     }
644     va_end(ap);
645 }
646
647 void
648 LogError(long code, char *fmt, ... )
649 {
650     va_list ap;
651     time_t now;
652
653     va_start(ap, fmt);
654     globalConfPtr->log = fopen(AFSDIR_SERVER_BUDBLOG_FILEPATH, "a");
655
656     if (globalConfPtr->log != NULL) {
657         now = time(0);
658         fprintf(globalConfPtr->log, "%s ", TimeStamp(now));
659
660         if (code)
661             fprintf(globalConfPtr->log, "%s: %s\n", afs_error_table_name(code),
662                     afs_error_message(code));
663         vfprintf(globalConfPtr->log, fmt, ap );
664         fflush(globalConfPtr->log);
665         fclose(globalConfPtr->log);
666     }
667 }
668
669
670 /*  ----------------
671  * debug
672  * ----------------
673  */
674
675 void
676 LogNetDump(struct dump *dumpPtr)
677 {
678     struct dump hostDump;
679     extern buServerConfP globalConfPtr;
680
681     dump_ntoh(dumpPtr, &hostDump);
682
683     globalConfPtr->log = fopen(AFSDIR_SERVER_BUDBLOG_FILEPATH, "a");
684     if (globalConfPtr->log != NULL) {
685         printDump(globalConfPtr->log, &hostDump);
686         fclose(globalConfPtr->log);
687     }
688 }
689