2a79027b5e956bba28fcecdbc9ffe262956c0a99
[openafs.git] / src / vol / salvaged.c
1 /*
2  * Copyright 2006-2007, Sine Nomine Associates 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 /* 
11  * demand attach fs
12  * online salvager daemon
13  */
14
15 /* Main program file. Define globals. */
16 #define MAIN 1
17
18 #include <afsconfig.h>
19 #include <afs/param.h>
20
21
22 #include <stdlib.h>
23 #include <stdio.h>
24 #include <string.h>
25 #include <dirent.h>
26 #include <sys/stat.h>
27 #include <time.h>
28 #include <errno.h>
29 #ifdef AFS_NT40_ENV
30 #include <io.h>
31 #include <WINNT/afsevent.h>
32 #else
33 #include <sys/param.h>
34 #include <sys/file.h>
35 #ifndef ITIMER_REAL
36 #include <sys/time.h>
37 #endif /* ITIMER_REAL */
38 #endif
39 #if     defined(AFS_AIX_ENV) || defined(AFS_SUN4_ENV)
40 #define WCOREDUMP(x)    (x & 0200)
41 #endif
42 #include <rx/xdr.h>
43 #include <afs/afsint.h>
44 #include <afs/assert.h>
45 #if !defined(AFS_SGI_ENV) && !defined(AFS_NT40_ENV)
46 #if defined(AFS_VFSINCL_ENV)
47 #include <sys/vnode.h>
48 #ifdef  AFS_SUN5_ENV
49 #include <sys/fs/ufs_inode.h>
50 #else
51 #if defined(AFS_DARWIN_ENV) || defined(AFS_XBSD_ENV)
52 #include <ufs/ufs/dinode.h>
53 #include <ufs/ffs/fs.h>
54 #else
55 #include <ufs/inode.h>
56 #endif
57 #endif
58 #else /* AFS_VFSINCL_ENV */
59 #ifdef  AFS_OSF_ENV
60 #include <ufs/inode.h>
61 #else /* AFS_OSF_ENV */
62 #if !defined(AFS_LINUX20_ENV) && !defined(AFS_XBSD_ENV)
63 #include <sys/inode.h>
64 #endif
65 #endif
66 #endif /* AFS_VFSINCL_ENV */
67 #endif /* AFS_SGI_ENV */
68 #ifdef  AFS_AIX_ENV
69 #include <sys/vfs.h>
70 #include <sys/lockf.h>
71 #else
72 #ifdef  AFS_HPUX_ENV
73 #include <unistd.h>
74 #include <checklist.h>
75 #else
76 #if defined(AFS_SGI_ENV)
77 #include <unistd.h>
78 #include <fcntl.h>
79 #include <mntent.h>
80 #else
81 #if     defined(AFS_SUN_ENV) || defined(AFS_SUN5_ENV)
82 #ifdef    AFS_SUN5_ENV
83 #include <unistd.h>
84 #include <sys/mnttab.h>
85 #include <sys/mntent.h>
86 #else
87 #include <mntent.h>
88 #endif
89 #else
90 #endif /* AFS_SGI_ENV */
91 #endif /* AFS_HPUX_ENV */
92 #endif
93 #endif
94 #include <fcntl.h>
95 #ifndef AFS_NT40_ENV
96 #include <afs/osi_inode.h>
97 #endif
98 #include <afs/cmd.h>
99 #include <afs/afsutil.h>
100 #include <afs/fileutil.h>
101 #include <afs/procmgmt.h>       /* signal(), kill(), wait(), etc. */
102 #include <afs/dir.h>
103 #ifndef AFS_NT40_ENV
104 #include <syslog.h>
105 #endif
106
107 #include "nfs.h"
108 #include "lwp.h"
109 #include "lock.h"
110 #include <afs/afssyscalls.h>
111 #include "ihandle.h"
112 #include "vnode.h"
113 #include "volume.h"
114 #include "partition.h"
115 #include "daemon_com.h"
116 #include "fssync.h"
117 #include "salvsync.h"
118 #include "viceinode.h"
119 #include "salvage.h"
120 #include "vol-salvage.h"
121 #ifdef AFS_NT40_ENV
122 #include <pthread.h>
123 #endif
124
125
126 #if !defined(AFS_DEMAND_ATTACH_FS)
127 #error "online salvager only supported for demand attach fileserver"
128 #endif /* AFS_DEMAND_ATTACH_FS */
129
130 #if defined(AFS_NT40_ENV)
131 #error "online salvager not supported on NT"
132 #endif /* AFS_NT40_ENV */
133
134
135 /* Forward declarations */
136 /*@printflike@*/ void Log(const char *format, ...);
137 /*@printflike@*/ void Abort(const char *format, ...);
138
139
140 /*@+fcnmacros +macrofcndecl@*/
141 #ifdef O_LARGEFILE
142 #define afs_fopen       fopen64
143 #else /* !O_LARGEFILE */
144 #define afs_fopen       fopen
145 #endif /* !O_LARGEFILE */
146 /*@=fcnmacros =macrofcndecl@*/
147
148
149
150 static volatile int current_workers = 0;
151 static volatile struct rx_queue pending_q;
152 static pthread_mutex_t worker_lock;
153 static pthread_cond_t worker_cv;
154
155 static void * SalvageChildReaperThread(void *);
156 static int DoSalvageVolume(struct SalvageQueueNode * node, int slot);
157
158 static void SalvageServer(void);
159 static void SalvageClient(VolumeId vid, char * pname);
160
161 static int Reap_Child(char * prog, int * pid, int * status);
162
163 static void * SalvageLogCleanupThread(void *);
164 static int SalvageLogCleanup(int pid);
165
166 static void * SalvageLogScanningThread(void *);
167 static void ScanLogs(struct rx_queue *log_watch_queue);
168
169 struct log_cleanup_node {
170     struct rx_queue q;
171     int pid;
172 };
173
174 struct {
175     struct rx_queue queue_head;
176     pthread_cond_t queue_change_cv;
177 } log_cleanup_queue;
178
179
180 #define DEFAULT_PARALLELISM 4 /* allow 4 parallel salvage workers by default */
181
182 static int
183 handleit(struct cmd_syndesc *as, void *arock)
184 {
185     register struct cmd_item *ti;
186     char pname[100], *temp;
187     afs_int32 seenpart = 0, seenvol = 0, vid = 0;
188
189 #ifdef AFS_SGI_VNODE_GLUE
190     if (afs_init_kernel_config(-1) < 0) {
191         printf
192             ("Can't determine NUMA configuration, not starting salvager.\n");
193         exit(1);
194     }
195 #endif
196
197     if (as->parms[2].items)     /* -debug */
198         debug = 1;
199     if (as->parms[3].items)     /* -nowrite */
200         Testing = 1;
201     if (as->parms[4].items)     /* -inodes */
202         ListInodeOption = 1;
203     if (as->parms[5].items)     /* -oktozap */
204         OKToZap = 1;
205     if (as->parms[6].items)     /* -rootinodes */
206         ShowRootFiles = 1;
207     if (as->parms[8].items)     /* -ForceReads */
208         forceR = 1;
209     if ((ti = as->parms[9].items)) {    /* -Parallel # */
210         temp = ti->data;
211         if (strncmp(temp, "all", 3) == 0) {
212             PartsPerDisk = 1;
213             temp += 3;
214         }
215         if (strlen(temp) != 0) {
216             Parallel = atoi(temp);
217             if (Parallel < 1)
218                 Parallel = 1;
219             if (Parallel > MAXPARALLEL) {
220                 printf("Setting parallel salvages to maximum of %d \n",
221                        MAXPARALLEL);
222                 Parallel = MAXPARALLEL;
223             }
224         }
225     } else {
226         Parallel = MIN(DEFAULT_PARALLELISM, MAXPARALLEL);
227     }
228     if ((ti = as->parms[10].items)) {   /* -tmpdir */
229         DIR *dirp;
230
231         tmpdir = ti->data;
232         dirp = opendir(tmpdir);
233         if (!dirp) {
234             printf
235                 ("Can't open temporary placeholder dir %s; using current partition \n",
236                  tmpdir);
237             tmpdir = NULL;
238         } else
239             closedir(dirp);
240     }
241     if ((ti = as->parms[11].items))     /* -showlog */
242         ShowLog = 1;
243     if ((ti = as->parms[12].items)) {   /* -orphans */
244         if (Testing)
245             orphans = ORPH_IGNORE;
246         else if (strcmp(ti->data, "remove") == 0
247                  || strcmp(ti->data, "r") == 0)
248             orphans = ORPH_REMOVE;
249         else if (strcmp(ti->data, "attach") == 0
250                  || strcmp(ti->data, "a") == 0)
251             orphans = ORPH_ATTACH;
252     }
253 #ifndef AFS_NT40_ENV            /* ignore options on NT */
254     if ((ti = as->parms[13].items)) {   /* -syslog */
255         useSyslog = 1;
256         ShowLog = 0;
257     }
258     if ((ti = as->parms[14].items)) {   /* -syslogfacility */
259         useSyslogFacility = atoi(ti->data);
260     }
261
262     if ((ti = as->parms[15].items)) {   /* -datelogs */
263         TimeStampLogFile((char *)AFSDIR_SERVER_SALSRVLOG_FILEPATH);
264     }
265 #endif
266
267     if ((ti = as->parms[16].items)) {   /* -client */
268         if ((ti = as->parms[0].items)) {        /* -partition */
269             seenpart = 1;
270             strlcpy(pname, ti->data, sizeof(pname));
271         }
272         if ((ti = as->parms[1].items)) {        /* -volumeid */
273             seenvol = 1;
274             vid = atoi(ti->data);
275         }
276
277         if (!seenpart || !seenvol) {
278             printf("You must specify '-partition' and '-volumeid' with the '-client' option\n");
279             exit(-1);
280         }
281
282         SalvageClient(vid, pname);
283
284     } else {  /* salvageserver mode */
285         SalvageServer();
286     }
287     return (0);
288 }
289
290
291 #ifndef AFS_NT40_ENV
292 #include "AFS_component_version_number.c"
293 #endif
294 #define MAX_ARGS 128
295 #ifdef AFS_NT40_ENV
296 char *save_args[MAX_ARGS];
297 int n_save_args = 0;
298 pthread_t main_thread;
299 #endif
300
301 static char commandLine[150];
302
303 int
304 main(int argc, char **argv)
305 {
306     struct cmd_syndesc *ts;
307     int err = 0;
308
309     int i;
310
311 #ifdef  AFS_AIX32_ENV
312     /*
313      * The following signal action for AIX is necessary so that in case of a 
314      * crash (i.e. core is generated) we can include the user's data section 
315      * in the core dump. Unfortunately, by default, only a partial core is
316      * generated which, in many cases, isn't too useful.
317      */
318     struct sigaction nsa;
319
320     sigemptyset(&nsa.sa_mask);
321     nsa.sa_handler = SIG_DFL;
322     nsa.sa_flags = SA_FULLDUMP;
323     sigaction(SIGABRT, &nsa, NULL);
324     sigaction(SIGSEGV, &nsa, NULL);
325 #endif
326
327     /* Initialize directory paths */
328     if (!(initAFSDirPath() & AFSDIR_SERVER_PATHS_OK)) {
329 #ifdef AFS_NT40_ENV
330         ReportErrorEventAlt(AFSEVT_SVR_NO_INSTALL_DIR, 0, argv[0], 0);
331 #endif
332         fprintf(stderr, "%s: Unable to obtain AFS server directory.\n",
333                 argv[0]);
334         exit(2);
335     }
336 #ifdef AFS_NT40_ENV
337     main_thread = pthread_self();
338     if (spawnDatap && spawnDataLen) {
339         /* This is a child per partition salvager. Don't setup log or
340          * try to lock the salvager lock.
341          */
342         if (nt_SetupPartitionSalvage(spawnDatap, spawnDataLen) < 0)
343             exit(3);
344     } else {
345 #endif
346         for (commandLine[0] = '\0', i = 0; i < argc; i++) {
347             if (i > 0)
348                 strlcat(commandLine, " ", sizeof(commandLine));
349             strlcat(commandLine, argv[i], sizeof(commandLine));
350         }
351
352 #ifndef AFS_NT40_ENV
353         if (geteuid() != 0) {
354             printf("Salvager must be run as root.\n");
355             fflush(stdout);
356             Exit(0);
357         }
358 #endif
359
360         /* bad for normal help flag processing, but can do nada */
361
362 #ifdef AFS_NT40_ENV
363     }
364 #endif
365
366     ts = cmd_CreateSyntax("initcmd", handleit, NULL, "initialize the program");
367     cmd_AddParm(ts, "-partition", CMD_SINGLE, CMD_OPTIONAL,
368                 "Name of partition to salvage");
369     cmd_AddParm(ts, "-volumeid", CMD_SINGLE, CMD_OPTIONAL,
370                 "Volume Id to salvage");
371     cmd_AddParm(ts, "-debug", CMD_FLAG, CMD_OPTIONAL,
372                 "Run in Debugging mode");
373     cmd_AddParm(ts, "-nowrite", CMD_FLAG, CMD_OPTIONAL,
374                 "Run readonly/test mode");
375     cmd_AddParm(ts, "-inodes", CMD_FLAG, CMD_OPTIONAL,
376                 "Just list affected afs inodes - debugging flag");
377     cmd_AddParm(ts, "-oktozap", CMD_FLAG, CMD_OPTIONAL,
378                 "Give permission to destroy bogus inodes/volumes - debugging flag");
379     cmd_AddParm(ts, "-rootinodes", CMD_FLAG, CMD_OPTIONAL,
380                 "Show inodes owned by root - debugging flag");
381     cmd_AddParm(ts, "-salvagedirs", CMD_FLAG, CMD_OPTIONAL,
382                 "Force rebuild/salvage of all directories");
383     cmd_AddParm(ts, "-blockreads", CMD_FLAG, CMD_OPTIONAL,
384                 "Read smaller blocks to handle IO/bad blocks");
385     cmd_AddParm(ts, "-parallel", CMD_SINGLE, CMD_OPTIONAL,
386                 "# of max parallel partition salvaging");
387     cmd_AddParm(ts, "-tmpdir", CMD_SINGLE, CMD_OPTIONAL,
388                 "Name of dir to place tmp files ");
389     cmd_AddParm(ts, "-showlog", CMD_FLAG, CMD_OPTIONAL,
390                 "Show log file upon completion");
391     cmd_AddParm(ts, "-orphans", CMD_SINGLE, CMD_OPTIONAL,
392                 "ignore | remove | attach");
393
394     /* note - syslog isn't avail on NT, but if we make it conditional, have
395      * to deal with screwy offsets for cmd params */
396     cmd_AddParm(ts, "-syslog", CMD_FLAG, CMD_OPTIONAL,
397                 "Write salvage log to syslogs");
398     cmd_AddParm(ts, "-syslogfacility", CMD_SINGLE, CMD_OPTIONAL,
399                 "Syslog facility number to use");
400     cmd_AddParm(ts, "-datelogs", CMD_FLAG, CMD_OPTIONAL,
401                 "Include timestamp in logfile filename");
402
403     cmd_AddParm(ts, "-client", CMD_FLAG, CMD_OPTIONAL,
404                 "Use SALVSYNC to ask salvageserver to salvage a volume");
405
406     err = cmd_Dispatch(argc, argv);
407     Exit(err);
408     return 0; /* not reached */
409 }
410
411 static void
412 SalvageClient(VolumeId vid, char * pname)
413 {
414     int done = 0;
415     afs_int32 code;
416     SYNC_response res;
417     SALVSYNC_response_hdr sres;
418
419     VInitVolumePackage(volumeUtility, 5, 5, DONT_CONNECT_FS, 0);
420     SALVSYNC_clientInit();
421     
422     code = SALVSYNC_SalvageVolume(vid, pname, SALVSYNC_SALVAGE, SALVSYNC_OPERATOR, 0, NULL);
423     if (code != SYNC_OK) {
424         goto sync_error;
425     }
426
427     res.payload.buf = (void *) &sres;
428     res.payload.len = sizeof(sres);
429
430     while(!done) {
431         sleep(2);
432         code = SALVSYNC_SalvageVolume(vid, pname, SALVSYNC_QUERY, SALVSYNC_WHATEVER, 0, &res);
433         if (code != SYNC_OK) {
434             goto sync_error;
435         }
436         switch (sres.state) {
437         case SALVSYNC_STATE_ERROR:
438             printf("salvageserver reports salvage ended in an error; check log files for more details\n");
439         case SALVSYNC_STATE_DONE:
440         case SALVSYNC_STATE_UNKNOWN:
441             done = 1;
442         }
443     }
444     SALVSYNC_clientFinis();
445     return;
446
447  sync_error:
448     if (code == SYNC_DENIED) {
449         printf("salvageserver refused to salvage volume %u on partition %s\n",
450                vid, pname);
451     } else if (code == SYNC_BAD_COMMAND) {
452         printf("SALVSYNC protocol mismatch; please make sure fileserver, volserver, salvageserver and salvager are same version\n");
453     } else if (code == SYNC_COM_ERROR) {
454         printf("SALVSYNC communications error\n");
455     }
456     SALVSYNC_clientFinis();
457     exit(-1);
458 }
459
460 static int * child_slot;
461
462 static void
463 SalvageServer(void)
464 {
465     int pid, ret;
466     struct SalvageQueueNode * node;
467     pthread_t tid;
468     pthread_attr_t attrs;
469     int slot;
470
471     /* All entries to the log will be appended.  Useful if there are
472      * multiple salvagers appending to the log.
473      */
474
475     CheckLogFile((char *)AFSDIR_SERVER_SALSRVLOG_FILEPATH);
476 #ifndef AFS_NT40_ENV
477 #ifdef AFS_LINUX20_ENV
478     fcntl(fileno(logFile), F_SETFL, O_APPEND);  /* Isn't this redundant? */
479 #else
480     fcntl(fileno(logFile), F_SETFL, FAPPEND);   /* Isn't this redundant? */
481 #endif
482 #endif
483     setlinebuf(logFile);
484
485     fprintf(logFile, "%s\n", cml_version_number);
486     Log("Starting OpenAFS Online Salvage Server %s (%s)\n", SalvageVersion, commandLine);
487     
488     /* Get and hold a lock for the duration of the salvage to make sure
489      * that no other salvage runs at the same time.  The routine
490      * VInitVolumePackage (called below) makes sure that a file server or
491      * other volume utilities don't interfere with the salvage.
492      */
493     
494     /* even demand attach online salvager
495      * still needs this because we don't want
496      * a stand-alone salvager to conflict with
497      * the salvager daemon */
498     ObtainSalvageLock();
499
500     child_slot = (int *) malloc(Parallel * sizeof(int));
501     assert(child_slot != NULL);
502     memset(child_slot, 0, Parallel * sizeof(int));
503             
504     /* initialize things */
505     VInitVolumePackage(salvageServer, 5, 5,
506                        1, 0);
507     DInit(10);
508     queue_Init(&pending_q);
509     queue_Init(&log_cleanup_queue);
510     assert(pthread_mutex_init(&worker_lock, NULL) == 0);
511     assert(pthread_cond_init(&worker_cv, NULL) == 0);
512     assert(pthread_cond_init(&log_cleanup_queue.queue_change_cv, NULL) == 0);
513     assert(pthread_attr_init(&attrs) == 0);
514
515     /* start up the reaper and log cleaner threads */
516     assert(pthread_attr_setdetachstate(&attrs, PTHREAD_CREATE_DETACHED) == 0);
517     assert(pthread_create(&tid, 
518                           &attrs, 
519                           &SalvageChildReaperThread,
520                           NULL) == 0);
521     assert(pthread_create(&tid, 
522                           &attrs, 
523                           &SalvageLogCleanupThread,
524                           NULL) == 0);
525     assert(pthread_create(&tid,
526                           &attrs,
527                           &SalvageLogScanningThread,
528                           NULL) == 0);
529
530     /* loop forever serving requests */
531     while (1) {
532         node = SALVSYNC_getWork();
533         assert(node != NULL);
534
535         Log("dispatching child to salvage volume %u...\n",
536             node->command.sop.parent);
537
538         VOL_LOCK;
539         /* find a slot */
540         for (slot = 0; slot < Parallel; slot++) {
541           if (!child_slot[slot])
542             break;
543         }
544         assert (slot < Parallel);
545
546     do_fork:
547         pid = Fork();
548         if (pid == 0) {
549             VOL_UNLOCK;
550             ret = DoSalvageVolume(node, slot);
551             Exit(ret);
552         } else if (pid < 0) {
553             Log("failed to fork child worker process\n");
554             sleep(1);
555             goto do_fork;
556         } else {
557             child_slot[slot] = pid;
558             node->pid = pid;
559             VOL_UNLOCK;
560             
561             assert(pthread_mutex_lock(&worker_lock) == 0);
562             current_workers++;
563             
564             /* let the reaper thread know another worker was spawned */
565             assert(pthread_cond_broadcast(&worker_cv) == 0);
566             
567             /* if we're overquota, wait for the reaper */
568             while (current_workers >= Parallel) {
569                 assert(pthread_cond_wait(&worker_cv, &worker_lock) == 0);
570             }
571             assert(pthread_mutex_unlock(&worker_lock) == 0);
572         }
573     }
574 }
575
576 static int
577 DoSalvageVolume(struct SalvageQueueNode * node, int slot)
578 {
579     char childLog[AFSDIR_PATH_MAX];
580     struct DiskPartition64 * partP;
581
582     /* do not allow further forking inside salvager */
583     canfork = 0;
584
585     /* do not attempt to close parent's logFile handle as
586      * another thread may have held the lock on the FILE
587      * structure when fork was called! */
588
589     afs_snprintf(childLog, sizeof(childLog), "%s.%d", 
590                  AFSDIR_SERVER_SLVGLOG_FILEPATH, getpid());
591
592     logFile = afs_fopen(childLog, "a");
593     if (!logFile) {             /* still nothing, use stdout */
594         logFile = stdout;
595         ShowLog = 0;
596     }
597
598     if (node->command.sop.parent <= 0) {
599         Log("salvageServer: invalid volume id specified; salvage aborted\n");
600         return 1;
601     }
602     
603     partP = VGetPartition(node->command.sop.partName, 0);
604     if (!partP) {
605         Log("salvageServer: Unknown or unmounted partition %s; salvage aborted\n", 
606             node->command.sop.partName);
607         return 1;
608     }
609
610     /* Salvage individual volume; don't notify fs */
611     SalvageFileSys1(partP, node->command.sop.parent);
612
613     fclose(logFile);
614     return 0;
615 }
616
617
618 static void *
619 SalvageChildReaperThread(void * args)
620 {
621     int slot, pid, status;
622     struct log_cleanup_node * cleanup;
623
624     assert(pthread_mutex_lock(&worker_lock) == 0);
625
626     /* loop reaping our children */
627     while (1) {
628         /* wait() won't block unless we have children, so
629          * block on the cond var if we're childless */
630         while (current_workers == 0) {
631             assert(pthread_cond_wait(&worker_cv, &worker_lock) == 0);
632         }
633
634         assert(pthread_mutex_unlock(&worker_lock) == 0);
635
636         cleanup = (struct log_cleanup_node *) malloc(sizeof(struct log_cleanup_node));
637
638         while (Reap_Child("salvageserver", &pid, &status) < 0) {
639             /* try to prevent livelock if something goes wrong */
640             sleep(1);
641         }
642
643         VOL_LOCK;
644         for (slot = 0; slot < Parallel; slot++) {
645             if (child_slot[slot] == pid)
646                 break;
647         }
648         assert(slot < Parallel);
649         child_slot[slot] = 0;
650         VOL_UNLOCK;
651
652         SALVSYNC_doneWorkByPid(pid, status);
653
654         assert(pthread_mutex_lock(&worker_lock) == 0);
655
656         if (cleanup) {
657             cleanup->pid = pid;
658             queue_Append(&log_cleanup_queue, cleanup);
659             assert(pthread_cond_signal(&log_cleanup_queue.queue_change_cv) == 0);
660         }
661
662         /* ok, we've reaped a child */
663         current_workers--;
664         assert(pthread_cond_broadcast(&worker_cv) == 0);
665     }
666
667     return NULL;
668 }
669
670 static int
671 Reap_Child(char *prog, int * pid, int * status)
672 {
673     int ret;
674     ret = wait(status);
675
676     if (ret >= 0) {
677         *pid = ret;
678         if (WCOREDUMP(*status))
679             Log("\"%s\" core dumped!\n", prog);
680         if ((WIFSIGNALED(*status) != 0) ||
681             ((WEXITSTATUS(*status) != 0) &&
682              (WEXITSTATUS(*status) != SALSRV_EXIT_VOLGROUP_LINK)))
683             Log("\"%s\" (pid=%d) terminated abnormally!\n", prog, ret);
684     } else {
685         Log("wait returned -1\n");
686     }
687     return ret;
688 }
689
690 /*
691  * thread to combine salvager child logs
692  * back into the main salvageserver log
693  */
694 static void *
695 SalvageLogCleanupThread(void * arg)
696 {
697     struct log_cleanup_node * cleanup;
698
699     assert(pthread_mutex_lock(&worker_lock) == 0);
700
701     while (1) {
702         while (queue_IsEmpty(&log_cleanup_queue)) {
703             assert(pthread_cond_wait(&log_cleanup_queue.queue_change_cv, &worker_lock) == 0);
704         }
705
706         while (queue_IsNotEmpty(&log_cleanup_queue)) {
707             cleanup = queue_First(&log_cleanup_queue, log_cleanup_node);
708             queue_Remove(cleanup);
709             assert(pthread_mutex_unlock(&worker_lock) == 0);
710             SalvageLogCleanup(cleanup->pid);
711             free(cleanup);
712             assert(pthread_mutex_lock(&worker_lock) == 0);
713         }           
714     }
715
716     assert(pthread_mutex_unlock(&worker_lock) == 0);
717     return NULL;
718 }
719
720 #define LOG_XFER_BUF_SIZE 65536
721 static int
722 SalvageLogCleanup(int pid)
723 {
724     int pidlog, len;
725     char fn[AFSDIR_PATH_MAX];
726     static char buf[LOG_XFER_BUF_SIZE];
727
728     afs_snprintf(fn, sizeof(fn), "%s.%d", 
729                  AFSDIR_SERVER_SLVGLOG_FILEPATH, pid);
730     
731
732     pidlog = open(fn, O_RDONLY);
733     unlink(fn);
734     if (pidlog < 0)
735         return 1;
736
737     len = read(pidlog, buf, LOG_XFER_BUF_SIZE);
738     while (len) {
739         fwrite(buf, len, 1, logFile);
740         len = read(pidlog, buf, LOG_XFER_BUF_SIZE);
741     }
742
743     close(pidlog);
744
745     return 0;
746 }
747
748 /* wake up every five minutes to see if a non-child salvage has finished */
749 #define SALVAGE_SCAN_POLL_INTERVAL 300
750
751 /**
752  * Thread to look for SalvageLog.$pid files that are not from our child
753  * worker salvagers, and notify SalvageLogCleanupThread to clean them
754  * up. This can happen if we restart during salvages, or the
755  * salvageserver crashes or something.
756  *
757  * @param arg  unused
758  *
759  * @return always NULL
760  */
761 static void *
762 SalvageLogScanningThread(void * arg)
763 {
764     struct rx_queue log_watch_queue;
765
766     queue_Init(&log_watch_queue);
767
768     {
769         DIR *dp;
770         struct dirent *dirp;
771         char prefix[AFSDIR_PATH_MAX];
772         size_t prefix_len;
773
774         afs_snprintf(prefix, sizeof(prefix), "%s.", AFSDIR_SLVGLOG_FILE);
775         prefix_len = strlen(prefix);
776
777         dp = opendir(AFSDIR_LOGS_DIR);
778         assert(dp);
779
780         while ((dirp = readdir(dp)) != NULL) {
781             pid_t pid;
782             struct log_cleanup_node *cleanup;
783             int i;
784
785             if (strncmp(dirp->d_name, prefix, prefix_len) != 0) {
786                 /* not a salvage logfile; skip */
787                 continue;
788             }
789
790             errno = 0;
791             pid = strtol(dirp->d_name + prefix_len, NULL, 10);
792
793             if (errno != 0) {
794                 /* file is SalvageLog.<something> but <something> isn't
795                  * a pid, so skip */
796                  continue;
797             }
798
799             VOL_LOCK;
800             for (i = 0; i < Parallel; ++i) {
801                 if (pid == child_slot[i]) {
802                     break;
803                 }
804             }
805             VOL_UNLOCK;
806             if (i < Parallel) {
807                 /* this pid is one of our children, so the reaper thread
808                  * will take care of it; skip */
809                 continue;
810             }
811
812             cleanup =
813                 (struct log_cleanup_node *) malloc(sizeof(struct log_cleanup_node));
814             cleanup->pid = pid;
815
816             queue_Append(&log_watch_queue, cleanup);
817         }
818
819         closedir(dp);
820     }
821
822     ScanLogs(&log_watch_queue);
823
824     while (queue_IsNotEmpty(&log_watch_queue)) {
825         sleep(SALVAGE_SCAN_POLL_INTERVAL);
826         ScanLogs(&log_watch_queue);
827     }
828
829     return NULL;
830 }
831
832 /**
833  * look through log_watch_queue, and if any processes are not still
834  * running, hand them off to the SalvageLogCleanupThread
835  *
836  * @param log_watch_queue  a queue of PIDs that we should clean up if
837  * that PID has died
838  */
839 static void
840 ScanLogs(struct rx_queue *log_watch_queue)
841 {
842     struct log_cleanup_node *cleanup, *next;
843
844     assert(pthread_mutex_lock(&worker_lock) == 0);
845
846     for (queue_Scan(log_watch_queue, cleanup, next, log_cleanup_node)) {
847         /* if a process is still running, assume it's the salvage process
848          * still going, and keep waiting for it */
849         if (kill(cleanup->pid, 0) < 0 && errno == ESRCH) {
850             queue_Remove(cleanup);
851             queue_Append(&log_cleanup_queue, cleanup);
852             assert(pthread_cond_signal(&log_cleanup_queue.queue_change_cv) == 0);
853         }
854     }
855
856     assert(pthread_mutex_unlock(&worker_lock) == 0);
857 }