Standardize License information
[openafs.git] / src / bozo / bosoprocs.c
1 /*
2  * Copyright 2000, International Business Machines Corporation and others.
3  * All Rights Reserved.
4  * 
5  * This software has been released under the terms of the IBM Public
6  * License.  For details, see the LICENSE file in the top-level source
7  * directory or online at http://www.openafs.org/dl/license10.html
8  */
9
10 #include <afs/param.h>
11 #include <afs/stds.h>
12 #include <sys/types.h>
13 #ifdef AFS_NT40_ENV
14 #include <io.h>
15 #include <fcntl.h>
16 #include <sys/utime.h>
17 #else
18 #include <sys/file.h>
19 #include <netinet/in.h>
20 #endif /* AFS_NT40_ENV */
21 #include <rx/xdr.h>
22 #include <rx/rx.h>
23 #include <rx/rxkad.h>
24 #include <errno.h>
25 #include <afs/cellconfig.h>
26 #include <afs/keys.h>
27 #include <sys/stat.h>
28 #include <des.h>
29 #include <dirent.h>
30 #include <stdio.h>
31 #include <afs/afsutil.h>
32 #include <afs/fileutil.h>
33 #include <afs/ktime.h>
34 #include <afs/audit.h>
35
36 #include "bnode.h"
37 #include "bosint.h"
38
39
40 extern struct ktime bozo_nextRestartKT, bozo_nextDayKT;
41
42 extern struct afsconf_dir *bozo_confdir;
43 extern struct rx_securityClass *bozo_rxsc[2];
44 extern int bozo_newKTs;
45 extern int DoLogging;
46
47 BOZO_GetRestartTime(acall, atype, aktime)
48 struct rx_call *acall;
49 afs_int32 atype;
50 struct ktime *aktime; {
51     register afs_int32 code;
52
53     code = 0;           /* assume success */
54     switch (atype) {
55       case 1:
56         bcopy(&bozo_nextRestartKT, aktime, sizeof(struct ktime));
57         break;
58
59       case 2:
60         bcopy(&bozo_nextDayKT, aktime, sizeof(struct ktime));
61         break;
62
63       default:
64         code = BZDOM;
65         break;
66     }
67
68     return code;
69 }
70
71 BOZO_SetRestartTime(acall, atype, aktime)
72 struct rx_call *acall;
73 afs_int32 atype;
74 struct ktime *aktime; {
75     register afs_int32 code;
76     char caller[MAXKTCNAMELEN];
77
78     /* check for proper permissions */
79     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
80       code = BZACCESS;
81       goto fail;
82     }
83     if (DoLogging) bozo_Log("%s is executing SetRestartTime\n", caller);
84
85     code = 0;           /* assume success */
86     switch (atype) {
87       case 1:
88         bcopy(aktime, &bozo_nextRestartKT, sizeof(struct ktime));
89         break;
90
91       case 2:
92         bcopy(aktime, &bozo_nextDayKT, sizeof(struct ktime));
93         break;
94
95       default:
96         code = BZDOM;
97         break;
98     }
99
100     if (code == 0) {
101         /* try to update the bozo init file */
102         code = WriteBozoFile(0);
103         bozo_newKTs = 1;
104     }
105
106 fail:
107     osi_auditU (acall, BOS_SetRestartEvent, code, AUD_END);
108     return code;
109 }
110
111 BOZO_Exec(acall, acmd)
112 struct rx_call *acall;
113 char *acmd; {
114
115     char caller[MAXKTCNAMELEN];
116     int code;
117
118     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
119       code = BZACCESS;
120       goto fail;
121     }
122     if (DoLogging) bozo_Log("%s is executing the shell command '%s'\n", caller, acmd);
123
124     /* should copy output to acall, but don't yet cause its hard */
125     /*  hard... NOT!  Nnow _at least_ return the exit status */
126     code = system(acmd);
127     osi_auditU (acall, BOS_ExecEvent, code, AUD_STR, acmd, AUD_END);
128
129 fail:
130     return code;
131 }
132
133 BOZO_GetDates(acall, aname, atime, abakTime, aoldTime)
134 struct rx_call *acall;
135 char *aname;
136 afs_int32 *atime, *abakTime, *aoldTime; {
137     register afs_int32 code;
138     struct stat tstat;
139     char *strp;
140     char tbuffer[AFSDIR_PATH_MAX];
141
142     *atime = *abakTime = *aoldTime = 0;
143
144     /* construct local path from canonical (wire-format) path */
145     if (ConstructLocalBinPath(aname, &strp)) {
146         return 0;
147     }
148     strcpy(tbuffer, strp);
149     free(strp);
150
151     strp = tbuffer + strlen(tbuffer);
152
153     if (!stat(tbuffer, &tstat)) {
154         *atime = tstat.st_mtime;
155     }
156
157     strcpy(strp, ".BAK");
158     if (!stat(tbuffer, &tstat)) {
159         *abakTime = tstat.st_mtime;
160     }
161
162     strcpy(strp, ".OLD");
163     if (!stat(tbuffer, &tstat)) {
164         *aoldTime = tstat.st_mtime;
165     }
166     return 0;
167 }
168
169 BOZO_UnInstall(acall, aname)
170 struct rx_call *acall;
171 register char *aname; {
172     char *filepath;
173     char fpOld[AFSDIR_PATH_MAX], fpBak[AFSDIR_PATH_MAX];
174     afs_int32 code;
175     char caller[MAXKTCNAMELEN];
176     struct stat tstat;
177
178     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
179         code = BZACCESS;
180         osi_auditU (acall, BOS_UnInstallEvent, code, AUD_STR, aname, AUD_END);
181         return code;
182     }
183
184     /* construct local path from canonical (wire-format) path */
185     if (ConstructLocalBinPath(aname, &filepath)) {
186         return BZNOENT;
187     }
188
189     if (DoLogging) bozo_Log("%s is executing UnInstall '%s'\n", caller, filepath);
190
191     strcpy(fpBak, filepath);
192     strcat(fpBak, ".BAK");
193     strcpy(fpOld, filepath);
194     strcat(fpOld, ".OLD");
195
196     code = renamefile(fpBak, filepath);
197     if (code) {
198         /* can't find .BAK, try .OLD */
199         code = renamefile(fpOld, filepath);
200         if (code && errno == ENOENT)  /* If doesn't exist don't fail */
201             code = 0;
202     } else {
203         /* now rename .OLD to .BAK */
204         if (stat(fpOld, &tstat) == 0)
205             code = renamefile(fpOld, fpBak);
206     }
207     if (code)
208         code = errno;
209
210     osi_auditU (acall, BOS_UnInstallEvent, code, AUD_STR, filepath, AUD_END);
211     free(filepath);
212
213     return code;
214 }
215
216 #define BOZO_OLDTIME        (7*24*3600)     /* 7 days old */
217 static void SaveOldFiles(char *aname)
218 {
219     register afs_int32 code;
220     char bbuffer[AFSDIR_PATH_MAX], obuffer[AFSDIR_PATH_MAX];
221     struct stat tstat;
222     register afs_int32 now;
223     afs_int32 oldTime, bakTime;
224     
225     strcpy(bbuffer, aname);
226     strcat(bbuffer, ".BAK");
227     strcpy(obuffer, aname);
228     strcat(obuffer, ".OLD");
229     now = FT_ApproxTime();
230
231     code = stat(aname, &tstat);
232     if (code < 0) return;       /* can't stat file */
233     
234     code = stat(obuffer, &tstat);    /* discover old file's time */
235     if (code) oldTime = 0;
236     else oldTime = tstat.st_mtime;
237
238     code = stat(bbuffer, &tstat);    /* discover back file's time */
239     if (code) bakTime = 0;
240     else bakTime = tstat.st_mtime;
241
242     if (bakTime && (oldTime == 0 || bakTime < now - BOZO_OLDTIME)) {
243         /* no .OLD file, or .BAK is at least a week old */
244         code = renamefile(bbuffer, obuffer);
245     }
246     
247     /* finally rename to .BAK extension */
248     renamefile(aname, bbuffer);
249 }
250
251 BOZO_Install(acall, aname, asize, mode, amtime)
252 struct rx_call *acall;
253 char *aname;
254 afs_int32 asize;
255 afs_int32 amtime;
256 afs_int32 mode; {
257     afs_int32 code;
258     int fd;
259     afs_int32 len;
260     afs_int32 total;
261 #ifdef AFS_NT40_ENV
262     struct _utimbuf utbuf;
263 #else
264     struct timeval tvb[2];
265 #endif
266     char filepath[AFSDIR_PATH_MAX], tbuffer[AFSDIR_PATH_MAX], *fpp;
267     char caller[MAXKTCNAMELEN];
268
269     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) return BZACCESS;
270
271     /* construct local path from canonical (wire-format) path */
272     if (ConstructLocalBinPath(aname, &fpp)) {
273         return BZNOENT;
274     }
275     strcpy(filepath, fpp);
276     free(fpp);
277
278     if (DoLogging) bozo_Log("%s is executing Install '%s'\n", caller, filepath);
279
280     /* open file */
281     fpp = filepath + strlen(filepath);
282     strcpy(fpp, ".NEW");  /* append ".NEW" to end of filepath */
283     fd = open(filepath, O_CREAT | O_RDWR | O_TRUNC, 0777);
284     if (fd < 0) return errno;
285     total = 0;
286     while(1) {
287         len = rx_Read(acall, tbuffer, sizeof(tbuffer));
288         if (len < 0) {
289             close(fd);
290             unlink(filepath);
291             return 102;
292         }
293         if (len == 0) break;    /* no more input */
294         code = write(fd, tbuffer, len);
295         if (code != len) {
296             close(fd);
297             unlink(filepath);
298             return 100;
299         }
300         total += len;   /* track total written for safety check at end */
301     }
302     close(fd);
303     if (asize != total) {
304         unlink(filepath);
305         return 101; /* wrong size */
306     }
307
308     /* save old files */
309     *fpp = '\0';  /* remove ".NEW" from end of filepath */
310     SaveOldFiles(filepath);    /* don't care if it works, still install */
311
312     /* all done, rename to final name */
313     strcpy(tbuffer, filepath);
314     strcat(tbuffer, ".NEW");
315     code = (renamefile(tbuffer, filepath) ? errno : 0);
316
317     /* label file with same time for our sanity */
318 #ifdef AFS_NT40_ENV
319     utbuf.actime = utbuf.modtime = amtime;
320     _utime(filepath, &utbuf);
321 #else
322     tvb[0].tv_sec = tvb[1].tv_sec = amtime;
323     tvb[0].tv_usec = tvb[1].tv_usec = 0;
324     utimes(filepath, tvb);
325 #endif /* AFS_NT40_ENV */
326
327     if (mode)
328         chmod(filepath, mode);
329
330     if (code < 0) {
331       osi_auditU (acall, BOS_InstallEvent, code, AUD_STR, filepath, AUD_END);
332       return errno;
333     }
334     else return 0;
335 }
336
337 BOZO_SetCellName(acall, aname)
338 struct rx_call *acall;
339 char *aname; {
340     struct afsconf_cell tcell;
341     register afs_int32 code;
342     char caller[MAXKTCNAMELEN];
343     
344     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
345       code = BZACCESS;
346       goto fail;
347     }
348     if (DoLogging) bozo_Log("%s is executing SetCellName '%s'\n", caller, aname);
349
350     code = afsconf_GetCellInfo(bozo_confdir, (char *) 0, (char *) 0, &tcell);
351     if (code) 
352       goto fail;
353
354     /* Check that tcell has enough space for the new cellname. */
355     if (strlen(aname) > sizeof tcell.name - 1) {
356         bozo_Log("ERROR: SetCellName: cell name '%s' exceeds %ld bytes (cell name not changed)\n",
357                  aname, (long)(sizeof tcell.name - 1));
358         code = BZDOM;
359         goto fail;
360     }
361
362     strcpy(tcell.name, aname);
363     code = afsconf_SetCellInfo(bozo_confdir, AFSDIR_SERVER_ETC_DIRPATH, &tcell);
364
365   fail:
366     osi_auditU (acall, BOS_SetCellEvent, code, AUD_STR, aname, AUD_END);
367     return code;
368 }
369
370 BOZO_GetCellName(acall, aname)
371 struct rx_call *acall;
372 char **aname; {
373     register afs_int32 code;
374     char tname[MAXCELLCHARS];
375     
376     code = afsconf_GetLocalCell(bozo_confdir, tname, sizeof(tname));
377     if (code) {
378       /* must set output parameters even if aborting */
379       *aname = (char *) malloc(1);
380       **aname = 0;
381     }
382     else { 
383       *aname = (char *) malloc(strlen(tname)+1);
384       strcpy(*aname, tname);
385     }
386
387     return code;
388 }
389
390 BOZO_GetCellHost(acall, awhich, aname)
391 struct rx_call *acall;
392 afs_int32 awhich;
393 char **aname; {
394     register afs_int32 code;
395     struct afsconf_cell tcell;
396     register char *tp;
397
398     code = afsconf_GetCellInfo(bozo_confdir, (char *) 0, (char *) 0, &tcell);
399     if (code) goto fail;
400
401     if (awhich >= tcell.numServers) {
402         code = BZDOM;
403         goto fail;
404     }
405     
406     tp = tcell.hostName[awhich];
407     *aname = (char *) malloc(strlen(tp)+1);
408     strcpy(*aname, tp);
409     goto done;
410     
411 fail:
412     *aname = (char *) malloc(1);        /* return fake string */
413     **aname = 0;
414
415 done:
416     return code;
417 }
418
419 BOZO_DeleteCellHost(acall, aname)
420 struct rx_call *acall;
421 char *aname; {
422     register afs_int32 code;
423     struct afsconf_cell tcell;
424     afs_int32 which;
425     register int i;
426     char caller[MAXKTCNAMELEN];
427
428     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
429       code = BZACCESS;
430       goto fail;
431     }
432     if (DoLogging) 
433       bozo_Log("%s is executing DeleteCellHost '%s'\n", caller, aname);
434
435     code = afsconf_GetCellInfo(bozo_confdir, (char *) 0, (char *) 0, &tcell);
436     if (code) 
437       goto fail;
438
439     which = -1;
440     for(i=0;i<tcell.numServers;i++) {
441         if (strcmp(tcell.hostName[i], aname) == 0) {
442             which = i;
443             break;
444         }
445     }
446
447     if (which < 0) {
448       code = BZNOENT;
449       goto fail;
450     }
451
452     bzero(&tcell.hostAddr[which], sizeof(struct sockaddr_in));
453     bzero(tcell.hostName[which], MAXHOSTCHARS);
454     code = afsconf_SetCellInfo(bozo_confdir, AFSDIR_SERVER_ETC_DIRPATH, &tcell);
455
456   fail:
457     osi_auditU( acall, BOS_DeleteHostEvent, code, AUD_STR, aname, AUD_END);
458     return code;
459 }
460
461 BOZO_AddCellHost(acall, aname)
462 struct rx_call *acall;
463 char *aname; {
464     register afs_int32 code;
465     struct afsconf_cell tcell;
466     afs_int32 which;
467     register int i;
468     char caller[MAXKTCNAMELEN];
469
470     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
471       code = BZACCESS;
472       goto fail;
473     }
474     if (DoLogging) 
475       bozo_Log("%s is executing AddCellHost '%s'\n", caller, aname);
476
477     code = afsconf_GetCellInfo(bozo_confdir, (char *) 0, (char *) 0, &tcell);
478     if (code) 
479       goto fail;
480
481     which = -1;
482     for(i=0;i<tcell.numServers;i++) {
483         if (strcmp(tcell.hostName[i], aname) == 0) {
484             which = i;
485             break;
486         }
487     }
488     if (which < 0) {
489         which = tcell.numServers;
490         tcell.numServers++;
491
492         /*
493          * Check that tcell has enough space for an additional host.
494          *
495          * We assume that tcell.hostAddr[] and tcell.hostName[] have the
496          * same number of entries.
497          */
498         if (tcell.numServers > sizeof tcell.hostAddr/sizeof tcell.hostAddr[0]) {
499             bozo_Log("ERROR: AddCellHost: attempt to add more than %ld database servers (database server '%s' not added)\n",
500                      (long)(sizeof tcell.hostAddr/sizeof tcell.hostAddr[0]), aname);
501             code = BZDOM;
502             goto fail;
503         }
504
505         /* Check that tcell has enough space for the new hostname. */
506         if (strlen(aname) > sizeof tcell.hostName[0] - 1) {
507             bozo_Log("ERROR: AddCellHost: host name '%s' exceeds %ld bytes (not added)\n",
508                      aname, (long)(sizeof tcell.hostName[0] - 1));
509             code = BZDOM;
510             goto fail;
511         }
512     }
513
514     bzero(&tcell.hostAddr[which], sizeof(struct sockaddr_in));
515     strcpy(tcell.hostName[which], aname);
516     code = afsconf_SetCellInfo(bozo_confdir, AFSDIR_SERVER_ETC_DIRPATH, &tcell);
517
518   fail:
519     osi_auditU(acall, BOS_AddHostEvent, code, AUD_STR, aname, AUD_END);
520     return code;
521 }
522
523 BOZO_ListKeys(acall, an, akvno, akey, akeyinfo)
524 struct rx_call *acall;
525 int an;
526 afs_int32 *akvno;
527 struct bozo_keyInfo *akeyinfo;
528 struct bozo_key *akey;
529 {
530     struct afsconf_keys tkeys;
531     register afs_int32 code;
532     struct stat tstat;
533     int noauth;
534     char caller[MAXKTCNAMELEN];
535     rxkad_level enc_level = rxkad_clear;
536
537     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
538       code = BZACCESS;
539       goto fail;
540     }
541     if (DoLogging) bozo_Log("%s is executing ListKeys\n", caller);
542
543     code = afsconf_GetKeys(bozo_confdir, &tkeys);
544     if (code) 
545       goto fail;
546
547     if (tkeys.nkeys <= an) {
548       code = BZDOM;
549       goto fail;
550     }
551     *akvno = tkeys.key[an].kvno;
552     bzero(akeyinfo, sizeof(struct bozo_keyInfo));
553
554     noauth = afsconf_GetNoAuthFlag(bozo_confdir);
555     rxkad_GetServerInfo(acall->conn, &enc_level, 0, 0, 0, 0, 0);
556     /* 
557      * only return actual keys in noauth or if this is an encrypted connection
558      */
559
560     if ((noauth) || (enc_level == rxkad_crypt)) {
561         bcopy(tkeys.key[an].key, akey, 8);
562     }
563     else bzero (akey, 8);
564
565     code = stat(AFSDIR_SERVER_KEY_FILEPATH, &tstat);
566     if (code == 0) {
567         akeyinfo->mod_sec = tstat.st_mtime;
568     }
569     ka_KeyCheckSum (tkeys.key[an].key, &akeyinfo->keyCheckSum);
570     /* only errors is bad key parity */
571
572 fail:
573     if (noauth)
574       osi_auditU(acall, BOS_UnAuthListKeysEvent, code, AUD_END);
575     osi_auditU(acall, BOS_ListKeysEvent, code, AUD_END);
576     return code;
577 }
578
579 BOZO_AddKey(acall, an, akey)
580 struct rx_call *acall;
581 afs_int32 an;
582 struct bozo_key *akey; {
583     register afs_int32 code;
584     char caller[MAXKTCNAMELEN];
585     rxkad_level enc_level = rxkad_clear;
586     int noauth;
587
588     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
589       code = BZACCESS;
590       goto fail;
591     }
592     noauth = afsconf_GetNoAuthFlag(bozo_confdir);
593     rxkad_GetServerInfo(acall->conn, &enc_level, 0, 0, 0, 0, 0);
594     if ((!noauth) && (enc_level != rxkad_crypt)) {
595       code = BZENCREQ;
596       goto fail;
597     }
598     if (DoLogging) bozo_Log("%s is executing AddKey\n", caller);
599
600     code = afsconf_AddKey(bozo_confdir, an, akey, 0);
601     if (code == AFSCONF_KEYINUSE)       
602         code = BZKEYINUSE;      /* Unique code for afs rpc calls */
603 fail:
604     osi_auditU(acall, BOS_AddKeyEvent, code, AUD_END);
605     return code;
606 }
607
608 BOZO_SetNoAuthFlag(acall, aflag)
609 register struct rx_call *acall;
610 afs_int32 aflag; {
611     register afs_int32 code  = 0;
612     char caller[MAXKTCNAMELEN];
613
614     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
615       code = BZACCESS;
616       goto fail;
617     }
618     if (DoLogging) bozo_Log("%s is executing Set No Authentication\n", caller);
619
620     afsconf_SetNoAuthFlag(bozo_confdir, aflag);
621
622 fail:
623     osi_auditU(acall, BOS_SetNoAuthEvent, code, AUD_LONG, aflag, AUD_END);
624     return code;
625 }
626
627 BOZO_DeleteKey(acall, an)
628 struct rx_call *acall;
629 afs_int32 an; {
630     register afs_int32 code;
631     char caller[MAXKTCNAMELEN];
632
633     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
634       code = BZACCESS;
635       goto fail;
636     }
637     if (DoLogging) bozo_Log("%s is executing DeleteKey\n", caller);
638
639     code = afsconf_DeleteKey(bozo_confdir, an);
640
641 fail:
642     osi_auditU(acall, BOS_DeleteKeyEvent, code, AUD_END);
643     return code;
644 }
645
646
647 BOZO_ListSUsers(acall, an, aname)
648 struct rx_call *acall;
649 afs_int32 an;
650 register char **aname; {
651     register afs_int32 code;
652     register char *tp;
653
654     tp = *aname = (char *) malloc(256);
655     *tp = 0;    /* in case getnthuser doesn't null-terminate the string */
656     code = afsconf_GetNthUser(bozo_confdir, an, tp, 256);
657
658 fail:
659     osi_auditU(acall, BOS_ListSUserEvent, code, AUD_END);
660     return code;
661 }
662
663 BOZO_AddSUser(acall, aname)
664 struct rx_call *acall;
665 char *aname; {
666     register afs_int32 code;
667     char caller[MAXKTCNAMELEN];
668
669     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
670       code = BZACCESS;
671       goto fail;
672     }
673     if (DoLogging) 
674       bozo_Log("%s is executing Add SuperUser '%s'\n", caller, aname);
675
676     code = afsconf_AddUser(bozo_confdir, aname);
677
678 fail:
679     osi_auditU(acall, BOS_AddSUserEvent, code, AUD_END);
680     return code;
681 }
682
683 BOZO_DeleteSUser(acall, aname)
684 struct rx_call *acall;
685 char *aname; {
686     register afs_int32 code;
687     char caller[MAXKTCNAMELEN];
688
689     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
690       code = BZACCESS;
691       goto fail;
692     }
693
694     if (DoLogging) 
695       bozo_Log("%s is executing Delete SuperUser '%s'\n", caller, aname);
696
697     code = afsconf_DeleteUser(bozo_confdir, aname);
698
699 fail:
700     osi_auditU(acall, BOS_DeleteSUserEvent, code, AUD_END);
701     return code;
702 }
703
704 BOZO_CreateBnode(acall, atype, ainstance, ap1, ap2, ap3, ap4, ap5, notifier)
705 struct rx_call *acall;
706 char *atype;
707 char *ainstance;
708 char *ap1, *ap2, *ap3, *ap4, *ap5;
709 char *notifier; {
710     struct bnode *tb;
711     afs_int32 code;
712     char caller[MAXKTCNAMELEN];
713
714     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
715       code = BZACCESS;
716       goto fail;
717     }
718
719     code = bnode_Create(atype, ainstance, &tb, ap1, ap2, ap3, ap4, ap5, notifier,BSTAT_NORMAL);
720     if (!code)
721       bnode_SetStat(tb, BSTAT_NORMAL);
722
723   fail:
724     osi_auditU(acall, BOS_CreateBnodeEvent, code, AUD_END);
725     return code;
726 }
727
728 BOZO_WaitAll(acall)
729 register struct rx_call *acall; {
730     register afs_int32 code;
731     char caller[MAXKTCNAMELEN];
732
733     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
734       code = BZACCESS;
735       goto fail;
736     }
737
738     if (DoLogging) bozo_Log("%s is executing Wait for All\n", caller);
739
740     code = bnode_WaitAll();
741
742 fail:
743     osi_auditU(acall, BOS_WaitAllEvent, code, AUD_END);
744     return code;
745 }
746
747 BOZO_DeleteBnode(acall, ainstance)
748 struct rx_call *acall;
749 char *ainstance; {
750     register afs_int32 code;
751     char caller[MAXKTCNAMELEN];
752
753     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
754       code = BZACCESS;
755       goto fail;
756     }
757     if (DoLogging) bozo_Log("%s is executing DeleteBnode '%s'\n", caller, ainstance);
758
759     code = bnode_DeleteName(ainstance);
760
761 fail:
762     osi_auditU(acall, BOS_DeleteBnodeEvent, code, AUD_STR, ainstance, AUD_END);
763     return code;
764 }
765
766 static swproc(abnode, arock)
767 register struct bnode *abnode;
768 char *arock; {
769     if (abnode->goal == BSTAT_NORMAL) return 0; /* this one's not shutting down */
770     /* otherwise, we are shutting down */
771     bnode_Hold(abnode);
772     bnode_WaitStatus(abnode, BSTAT_SHUTDOWN);
773     bnode_Release(abnode);
774     return 0;   /* don't stop apply function early, no matter what */
775 }
776
777 static stproc(abnode, arock)
778 struct bnode *abnode;
779 char *arock; {
780     if (abnode->fileGoal == BSTAT_SHUTDOWN) return 0;   /* don't do these guys */
781
782     bnode_Hold(abnode);
783     bnode_SetStat(abnode, BSTAT_NORMAL);
784     bnode_Release(abnode);
785     return 0;
786 }
787
788 static sdproc(abnode, arock)
789 struct bnode *abnode;
790 char *arock; {
791     bnode_Hold(abnode);
792     bnode_SetStat(abnode, BSTAT_SHUTDOWN);
793     bnode_Release(abnode);
794     return 0;
795 }
796
797 /* shutdown and leave down */
798 BOZO_ShutdownAll(acall)
799 struct rx_call *acall; {
800     /* iterate over all bnodes, setting the status to temporarily disabled */
801     register afs_int32 code;
802     char caller[MAXKTCNAMELEN];
803     
804     /* check for authorization */
805     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) {
806       code = BZACCESS;
807       goto fail;
808     }
809     if (DoLogging) bozo_Log("%s is executing ShutdownAll\n", caller);
810
811     code = bnode_ApplyInstance(sdproc, (char *) 0);
812
813   fail:
814     osi_auditU (acall, BOS_ShutdownAllEvent, code, AUD_END);
815     return code;
816 }
817
818 /* shutdown and restart */
819 BOZO_RestartAll(acall)
820 struct rx_call *acall; {
821     register afs_int32 code;
822     char caller[MAXKTCNAMELEN];
823     
824     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) { 
825       code = BZACCESS; 
826       goto fail; 
827     }
828     if (DoLogging) bozo_Log("%s is executing RestartAll\n", caller);
829
830     /* start shutdown of all processes */
831     code = bnode_ApplyInstance(sdproc, (char *) 0);
832     if (code) goto fail;
833
834     /* wait for all done */
835     code = bnode_ApplyInstance(swproc, (char *) 0);
836     if (code) goto fail;
837
838     /* start them up again */
839     code = bnode_ApplyInstance(stproc, (char *) 0);
840
841   fail:
842     osi_auditU (acall, BOS_RestartAllEvent, code, AUD_END);
843     return code;
844 }
845
846 BOZO_ReBozo(acall)
847 register struct rx_call *acall; {
848     register afs_int32 code;
849     char caller[MAXKTCNAMELEN];
850
851     /* acall is null if called internally to restart bosserver */
852     if (acall && !afsconf_SuperUser(bozo_confdir, acall, caller)) { 
853       code = BZACCESS;
854       goto fail;
855     }
856     if (DoLogging) bozo_Log("%s is executing ReBozo\n", caller);
857
858     /* start shutdown of all processes */
859     code = bnode_ApplyInstance(sdproc, (char *) 0);
860     if (code) goto fail;
861
862     /* wait for all done */
863     code = bnode_ApplyInstance(swproc, (char *) 0);
864     if (code) goto fail;
865
866     if (acall) osi_auditU (acall, BOS_RebozoEvent, code, AUD_END);
867     else       osi_audit (BOS_RebozoIntEvent, code, AUD_END);
868
869     if (acall) rx_EndCall(acall, 0);   /* try to get it done */
870     rx_Finalize();
871     bozo_ReBozo();  /* this reexecs us, and doesn't return, of course */
872
873 fail:
874     /* Differentiate between external and internal ReBozo; prevents AFS_Aud_NoCall event */
875     if (acall) osi_auditU (acall, BOS_RebozoEvent, code, AUD_END);
876     else       osi_audit (BOS_RebozoIntEvent, code, AUD_END);
877     return code;   /* should only get here in unusual circumstances */
878 }
879
880 /* make sure all are running */
881 BOZO_StartupAll(acall)
882 struct rx_call *acall; {
883     register afs_int32 code;
884     char caller[MAXKTCNAMELEN];
885
886     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) { 
887       code = BZACCESS; 
888       goto fail; 
889     }
890     if (DoLogging) bozo_Log("%s is executing StartupAll\n", caller);
891     code = bnode_ApplyInstance(stproc, (char *) 0);
892
893 fail:
894     osi_auditU (acall, BOS_StartupAllEvent, code, AUD_END);
895     return code;
896 }
897
898 BOZO_Restart(acall, ainstance)
899 struct rx_call *acall;
900 register char *ainstance; {
901     register struct bnode *tb;
902     register afs_int32 code;
903     char caller[MAXKTCNAMELEN];
904     
905     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) { 
906       code = BZACCESS; 
907       goto fail;
908     }
909     if (DoLogging) bozo_Log("%s is executing Restart '%s'\n", caller, ainstance);
910
911     tb = bnode_FindInstance(ainstance);
912     if (!tb) {
913       code = BZNOENT;
914       goto fail;
915     }
916     
917     /* setup return code */
918     code = 0;
919
920     bnode_Hold(tb);
921     bnode_SetStat(tb, BSTAT_SHUTDOWN);
922     code = bnode_WaitStatus(tb, BSTAT_SHUTDOWN);    /* this can fail */
923     bnode_SetStat(tb, BSTAT_NORMAL);
924     bnode_Release(tb);
925
926 fail:
927     osi_auditU (acall, BOS_RestartEvent, code, AUD_STR, ainstance, AUD_END);
928     return code;
929 }
930
931 /* set temp status */
932 BOZO_SetTStatus(acall, ainstance, astatus)
933 struct rx_call *acall;
934 char *ainstance;
935 afs_int32 astatus; {
936     register struct bnode *tb;
937     register afs_int32 code;
938     char caller[MAXKTCNAMELEN];
939
940     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) { 
941       code = BZACCESS; 
942       goto fail;
943     }
944     if (DoLogging) bozo_Log("%s is executing SetTempStatus '%s'\n", caller, ainstance);
945
946     tb = bnode_FindInstance(ainstance);
947     if (!tb) {
948       code = BZNOENT;
949       goto fail;
950     }
951     bnode_Hold(tb);
952     code = bnode_SetStat(tb, astatus);
953     bnode_Release(tb);
954
955 fail:
956     osi_auditU (acall, BOS_SetTempStatusEvent, code, AUD_STR, ainstance, AUD_END);
957     return code;
958 }
959
960 BOZO_SetStatus(acall, ainstance, astatus)
961 struct rx_call *acall;
962 char *ainstance;
963 afs_int32 astatus; {
964     register struct bnode *tb;
965     register afs_int32 code;
966     char caller[MAXKTCNAMELEN];
967
968     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) { 
969       code = BZACCESS; 
970       goto fail; 
971     }
972     if (DoLogging) bozo_Log("%s is executing SetStatus '%s' (status = %d)\n", caller, ainstance, astatus);
973
974     tb = bnode_FindInstance(ainstance);
975     if (!tb) {
976       code = BZNOENT;
977       goto fail;
978     }
979     bnode_Hold(tb);
980     bnode_SetFileGoal(tb, astatus);
981     code = bnode_SetStat(tb, astatus);
982     bnode_Release(tb);
983
984 fail:
985     osi_auditU (acall, BOS_SetStatusEvent, code, AUD_STR, ainstance, AUD_END);
986     return code;
987 }
988
989 BOZO_GetStatus(acall, ainstance, astat, astatDescr)
990 struct rx_call *acall;
991 char *ainstance;
992 afs_int32 *astat;
993 char **astatDescr; {
994     register struct bnode *tb;
995     register afs_int32 code;
996
997     tb = bnode_FindInstance(ainstance);
998     if (!tb) {
999         code = BZNOENT;
1000         goto fail;
1001     }
1002     
1003     bnode_Hold(tb);
1004     code = bnode_GetStat(tb, astat);
1005     if (code) {
1006         bnode_Release(tb);
1007         goto fail;
1008     }
1009     
1010     *astatDescr = (char *) malloc(BOZO_BSSIZE);
1011     code = bnode_GetString(tb, *astatDescr, BOZO_BSSIZE);
1012     bnode_Release(tb);
1013     if (code) (*astatDescr)[0] = 0;     /* null string means no further info */
1014     return 0;
1015
1016 fail:
1017     *astatDescr = (char *) malloc(1);
1018     **astatDescr = 0;
1019     return code;
1020 }
1021
1022 struct eidata {
1023     char *iname;
1024     int counter;
1025 };
1026
1027 static eifunc(abnode, arock)
1028 struct bnode *abnode;
1029 struct eidata *arock; {
1030     if (arock->counter-- == 0) {
1031         /* done */
1032         strcpy(arock->iname, abnode->name);
1033         return 1;
1034     }
1035     else {
1036         /* not there yet */
1037         return 0;
1038     }
1039 }
1040
1041 static ZapFile(adir, aname)
1042 register char *adir;
1043 register char *aname; {
1044     char tbuffer[256];
1045     strcpy(tbuffer, adir);
1046     strcat(tbuffer, "/");
1047     strcat(tbuffer, aname);
1048     return unlink(tbuffer);
1049 }
1050
1051 BOZO_Prune(acall, aflags)
1052 struct rx_call *acall;
1053 afs_int32 aflags; {
1054     register afs_int32 code;
1055     DIR *dirp;
1056     register struct dirent *tde;
1057     register int i;
1058     char caller[MAXKTCNAMELEN];
1059
1060     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) { 
1061       code = BZACCESS;
1062       goto fail; 
1063     }
1064     if (DoLogging) bozo_Log("%s is executing Prune (flags=%d)\n", caller, aflags);
1065
1066     /* first scan AFS binary directory */
1067     dirp = opendir(AFSDIR_SERVER_BIN_DIRPATH);
1068     if (dirp) {
1069         for(tde=readdir(dirp);tde;tde=readdir(dirp)) {
1070             i = strlen(tde->d_name);
1071             if (aflags & BOZO_PRUNEOLD) {
1072                 if(i >= 4 && strncmp(tde->d_name+i-4, ".OLD", 4)==0)
1073                     ZapFile(AFSDIR_SERVER_BIN_DIRPATH, tde->d_name);
1074             }
1075             if (aflags & BOZO_PRUNEBAK) {
1076                 if(i >= 4 && strncmp(tde->d_name+i-4, ".BAK", 4)==0)
1077                     ZapFile(AFSDIR_SERVER_BIN_DIRPATH, tde->d_name);
1078             }
1079         }
1080         closedir(dirp);
1081     }
1082
1083     /* then scan AFS log directory */
1084     dirp = opendir(AFSDIR_SERVER_LOGS_DIRPATH);
1085     if (dirp) {
1086         for(tde=readdir(dirp);tde;tde=readdir(dirp)) {
1087             if (aflags & BOZO_PRUNECORE) {
1088                 if(strncmp(tde->d_name, AFSDIR_CORE_FILE, 4)==0)
1089                     ZapFile(AFSDIR_SERVER_LOGS_DIRPATH, tde->d_name);
1090             }
1091         }
1092         closedir(dirp);
1093     }
1094     code = 0;
1095
1096   fail:
1097     osi_auditU ( acall, BOS_PruneLogs, code, AUD_END);
1098     return code;
1099 }
1100
1101 BOZO_EnumerateInstance(acall, anum, ainstance)
1102 struct rx_call *acall;
1103 afs_int32 anum;
1104 char **ainstance; {
1105     struct eidata tdata;
1106
1107     *ainstance = (char *) malloc(BOZO_BSSIZE);
1108     **ainstance = 0;
1109     tdata.counter = anum;
1110     tdata.iname = *ainstance;
1111     bnode_ApplyInstance(eifunc, &tdata);
1112     if (tdata.counter >= 0) return BZDOM;       /* anum > # of actual instances */
1113     else return 0;
1114 }
1115
1116 struct bozo_bosEntryStats bozo_bosEntryStats[] = {
1117     {NULL, 1,1, 0755, 02}, /* AFSDIR_SERVER_AFS_DIRPATH    */
1118     {NULL, 1,1, 0755, 02}, /* AFSDIR_SERVER_ETC_DIRPATH    */
1119     {NULL, 1,1, 0755, 02}, /* AFSDIR_SERVER_BIN_DIRPATH    */
1120     {NULL, 1,1, 0755, 02}, /* AFSDIR_SERVER_LOGS_DIRPATH   */
1121     {NULL, 1,0, 0700, 07}, /* AFSDIR_SERVER_BACKUP_DIRPATH */
1122     {NULL, 1,1, 0700, 07}, /* AFSDIR_SERVER_DB_DIRPATH     */
1123     {NULL, 1,1, 0700, 07}, /* AFSDIR_SERVER_LOCAL_DIRPATH  */
1124     {NULL, 0,1, 0600, 07}, /* AFSDIR_SERVER_KEY_FILEPATH   */
1125     {NULL, 0,1, 0600, 03}};/* AFSDIR_SERVER_ULIST_FILEPATH */
1126 int bozo_nbosEntryStats =
1127     sizeof(bozo_bosEntryStats) / sizeof(bozo_bosEntryStats[0]);
1128
1129 /* This function performs initialization of the bozo_bosEntrystats[]
1130  * array. This array contains the list of dirs that the bosserver 
1131  * is interested in along with their recommended permissions
1132  * NOTE: This initialization is a bit ugly. This was caused because
1133  * the path names require procedural as opposed to static initialization.
1134  * The other fields in the struct are however, statically initialized.
1135  */
1136 int initBosEntryStats()
1137 {
1138   bozo_bosEntryStats[0].path = AFSDIR_SERVER_AFS_DIRPATH;
1139   bozo_bosEntryStats[1].path = AFSDIR_SERVER_ETC_DIRPATH;
1140   bozo_bosEntryStats[2].path = AFSDIR_SERVER_BIN_DIRPATH;
1141   bozo_bosEntryStats[3].path = AFSDIR_SERVER_LOGS_DIRPATH;
1142   bozo_bosEntryStats[4].path = AFSDIR_SERVER_BACKUP_DIRPATH;
1143   bozo_bosEntryStats[5].path = AFSDIR_SERVER_DB_DIRPATH;
1144   bozo_bosEntryStats[6].path = AFSDIR_SERVER_LOCAL_DIRPATH;
1145   bozo_bosEntryStats[7].path = AFSDIR_SERVER_KEY_FILEPATH;
1146   bozo_bosEntryStats[8].path = AFSDIR_SERVER_ULIST_FILEPATH;
1147
1148 }
1149 /* StatEachEntry - If it's not there, it is okay.  If anything else goes wrong
1150  * complain.  Otherwise check permissions: shouldn't allow write or (usually)
1151  * read. */
1152
1153 static int StatEachEntry (stats)
1154   IN struct bozo_bosEntryStats *stats;
1155 {
1156     struct stat info;
1157     if (stat (stats->path, &info)) {
1158         if (errno == ENOENT) return 1;  /* no such entry: just ignore it */
1159         return 0;                       /* something else went wrong */
1160     } else {
1161         int rights;
1162         if (((info.st_mode & S_IFDIR) != 0) != stats->dir)
1163             return 0;                   /* not expected type */
1164         if (stats->rootOwner && (info.st_uid != 0))
1165             return 0;                   /* not owned by root */
1166         rights = (info.st_mode & 0000777);
1167         if ((rights & stats->reqPerm) != stats->reqPerm)
1168             return 0;                   /* required permissions not present */
1169         if ((rights & stats->proPerm) != 0)
1170             return 0;                   /* prohibited permissions present */
1171     }
1172     return 1;
1173 }
1174
1175 /* DirAccessOK - checks the mode bits on the AFS dir and decendents and
1176  * returns 0 if some are not OK and 1 otherwise.  For efficiency, it doesn't do
1177  * this check more often than every 5 seconds. */
1178
1179 int DirAccessOK ()
1180 {
1181 #ifdef AFS_NT40_ENV
1182     /* underlying filesystem may not support directory protection */
1183     return 1;
1184 #else
1185     static afs_uint32 lastTime = 0;
1186     afs_uint32 now = FT_ApproxTime();
1187     static int lastResult = -1;
1188     int result;
1189     int i;
1190
1191     if ((now-lastTime) < 5) return lastResult;
1192     lastTime = now;
1193
1194     result = 1;
1195     for (i=0; i<bozo_nbosEntryStats; i++) {
1196         struct bozo_bosEntryStats *e = &bozo_bosEntryStats[i];
1197         if (!StatEachEntry (e)) {
1198             result = 0;
1199             break;
1200         }
1201     }
1202
1203     if (result != lastResult) {         /* log changes */
1204         bozo_Log ("Server directory access is %sokay\n",
1205                   (result ? "" : "not "));
1206     }
1207     lastResult = result;
1208     return lastResult;
1209 #endif /* AFS_NT40_ENV */
1210 }
1211
1212 int GetRequiredDirPerm (path)
1213   IN char *path;
1214 {
1215     int i;
1216     for (i=0; i<bozo_nbosEntryStats; i++)
1217         if (strcmp (path, bozo_bosEntryStats[i].path) == 0)
1218             return bozo_bosEntryStats[i].reqPerm;
1219     return -1;
1220 }
1221
1222 BOZO_GetInstanceInfo(acall, ainstance, atype, astatus)
1223   IN struct rx_call *acall;
1224   IN char *ainstance;
1225   OUT char **atype;
1226   OUT struct bozo_status *astatus;
1227 {
1228     register struct bnode *tb;
1229     
1230     tb = bnode_FindInstance(ainstance);
1231     *atype = (char *) malloc(BOZO_BSSIZE);
1232     **atype = 0;
1233     if (!tb) return BZNOENT;
1234     if (tb->type)
1235         strcpy(*atype, tb->type->name);
1236     else
1237         (*atype)[0] = 0;    /* null string */
1238     bzero(astatus, sizeof(struct bozo_status)); /* good defaults */
1239     astatus->goal = tb->goal;
1240     astatus->fileGoal = tb->fileGoal;
1241     astatus->procStartTime = tb->procStartTime;
1242     astatus->procStarts = tb->procStarts;
1243     astatus->lastAnyExit = tb->lastAnyExit;
1244     astatus->lastErrorExit = tb->lastErrorExit;
1245     astatus->errorCode = tb->errorCode;
1246     astatus->errorSignal = tb->errorSignal;
1247     if (tb->flags & BNODE_ERRORSTOP)
1248         astatus->flags |= BOZO_ERRORSTOP;
1249     if (bnode_HasCore(tb))
1250         astatus->flags |= BOZO_HASCORE;
1251     if (!DirAccessOK())
1252         astatus->flags |= BOZO_BADDIRACCESS;
1253     return 0;
1254 }
1255
1256 BOZO_GetInstanceParm(acall, ainstance, anum, aparm)
1257 struct rx_call *acall;
1258 char *ainstance;
1259 afs_int32 anum;
1260 char **aparm; {
1261     register struct bnode *tb;
1262     register char *tp;
1263     register afs_int32 code;
1264     
1265     tp = (char *) malloc(BOZO_BSSIZE);
1266     *aparm = tp;
1267     *tp = 0;        /* null-terminate string in error case */
1268     tb = bnode_FindInstance(ainstance);
1269     if (!tb) return BZNOENT;
1270     bnode_Hold(tb);
1271     if (anum == 999) {
1272         if (tb->notifier) {
1273             bcopy(tb->notifier, tp, strlen(tb->notifier)+1);
1274             code = 0;
1275         } else
1276             code = BZNOENT;     /* XXXXX */
1277     } else
1278         code = bnode_GetParm(tb, anum, tp, BOZO_BSSIZE);
1279     bnode_Release(tb);
1280
1281     /* Not Currently Audited */
1282     return code;
1283 }
1284
1285 BOZO_GetLog(acall, aname)
1286 register struct rx_call *acall;
1287 char *aname; {
1288     register afs_int32 code;
1289     FILE *tfile;
1290     int tc;
1291     char *logpath;
1292     char buffer;
1293     char caller[MAXKTCNAMELEN];
1294
1295     /* Check access since 'aname' could specify a file outside of the
1296      * AFS log directory (which is bosserver's working directory).
1297      */
1298     if (!afsconf_SuperUser(bozo_confdir, acall, caller)) { 
1299       code = BZACCESS; 
1300       goto fail; 
1301     }
1302
1303     /* construct local path from canonical (wire-format) path */
1304     if (ConstructLocalLogPath(aname, &logpath)) {
1305         return BZNOENT;
1306     }
1307     if (DoLogging) bozo_Log("%s is executing GetLog '%s'\n", caller, logpath);
1308     tfile = fopen(logpath, "r");
1309     free(logpath);
1310
1311     if (!tfile) {
1312         return BZNOENT;
1313     }
1314
1315     while (1) {
1316         tc = getc(tfile);
1317         if (tc == 0) continue;  /* our termination condition on other end */
1318         if (tc == EOF) break;
1319         buffer = tc;
1320         if (rx_Write(acall, &buffer, 1) != 1) {
1321             fclose(tfile);
1322             code = BZNET;
1323             goto fail;
1324         }
1325     }
1326
1327     /* all done, cleanup and return */
1328     fclose(tfile);
1329
1330     /* write out the end delimeter */
1331     buffer = 0;
1332     if (rx_Write(acall, &buffer, 1) != 1) return BZNET;
1333     code = 0;
1334
1335   fail:
1336     osi_auditU( acall, BOS_GetLogsEvent, code, AUD_END);
1337     return code;
1338 }
1339
1340 BOZO_GetInstanceStrings(acall, abnodeName, as1, as2, as3, as4)
1341 struct rx_call *acall;
1342 char *abnodeName;
1343 char **as1, **as2, **as3, **as4; {
1344     register struct bnode *tb;
1345
1346     *as2 = (char *) malloc(1);
1347     **as2 = 0;
1348     *as3 = (char *) malloc(1);
1349     **as3 = 0;
1350     *as4 = (char *) malloc(1);
1351     **as4 = 0;
1352     tb = bnode_FindInstance(abnodeName);
1353     if (!tb) goto fail;
1354
1355     /* now, return the appropriate error string, if any */
1356     if (tb->lastErrorName) {
1357         *as1 = (char *) malloc(strlen(tb->lastErrorName)+1);
1358         strcpy(*as1, tb->lastErrorName);
1359     }
1360     else {
1361         *as1 = (char *) malloc(1);
1362         **as1 = 0;
1363     }
1364     return 0;
1365
1366   fail:
1367     *as1 = (char *) malloc(1);
1368     **as1 = 0;
1369     return BZNOENT;
1370 }
1371
1372
1373 void bozo_ShutdownAndExit(int asignal)
1374 {
1375     int code;
1376
1377     bozo_Log("Shutdown of BOS server and processes in response to signal %d\n",
1378              asignal);
1379
1380     /* start shutdown of all processes */
1381     if ((code = bnode_ApplyInstance(sdproc, (char *) 0)) == 0) {
1382         /* wait for shutdown to complete */
1383         code = bnode_ApplyInstance(swproc, (char *) 0);
1384     }
1385
1386     if (code) {
1387         bozo_Log("Shutdown incomplete (code = %d); manual cleanup required\n",
1388                  code);
1389     }
1390
1391     rx_Finalize();
1392     exit(code);
1393 }