307135f8a9501c13a06d4eb1695d31e5f71b73cd
[debian/amanda] / server-src / driverio.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
6  * Permission to use, copy, modify, distribute, and sell this software and its
7  * documentation for any purpose is hereby granted without fee, provided that
8  * the above copyright notice appear in all copies and that both that
9  * copyright notice and this permission notice appear in supporting
10  * documentation, and that the name of U.M. not be used in advertising or
11  * publicity pertaining to distribution of the software without specific,
12  * written prior permission.  U.M. makes no representations about the
13  * suitability of this software for any purpose.  It is provided "as is"
14  * without express or implied warranty.
15  *
16  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
17  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
18  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
19  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
20  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
21  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
22  *
23  * Author: James da Silva, Systems Design and Analysis Group
24  *                         Computer Science Department
25  *                         University of Maryland at College Park
26  */
27 /*
28  * $Id: driverio.c,v 1.35.2.14.4.2.2.5 2003/01/01 23:28:54 martinea Exp $
29  *
30  * I/O-related functions for driver program
31  */
32 #include "amanda.h"
33 #include "clock.h"
34 #include "conffile.h"
35 #include "diskfile.h"
36 #include "infofile.h"
37 #include "logfile.h"
38 #include "token.h"
39 #include "server_util.h"
40
41 #define GLOBAL          /* the global variables defined here */
42 #include "driverio.h"
43
44 void init_driverio()
45 {
46     dumper_t *dumper;
47
48     taper = -1;
49
50     for(dumper = dmptable; dumper < dmptable + MAX_DUMPERS; dumper++) {
51         dumper->outfd = -1;
52     }
53 }
54
55
56 void addfd(fd, readset, maxfd)
57 int    fd;
58 fd_set *readset;
59 int    *maxfd;
60 {
61     if(fd < 0 || fd >= FD_SETSIZE) {
62         error("addfd: descriptor %d out of range (0 .. %d)\n",
63               fd, FD_SETSIZE-1);
64     }
65     if(readset != NULL)
66         FD_SET(fd, readset);
67     if(maxfd != NULL)
68         if(fd > *maxfd) *maxfd = fd;
69 }
70
71 char *childstr(fd)
72 int fd;
73 {
74     static char *str = NULL;
75     char fd_str[NUM_STR_SIZE];
76     dumper_t *dumper;
77
78     if(fd == taper) return "taper";
79
80     for(dumper = dmptable; dumper < dmptable + MAX_DUMPERS; dumper++)
81         if(dumper->outfd == fd) return dumper->name;
82
83     ap_snprintf(fd_str, sizeof(fd_str), "%d", fd);
84     str = newvstralloc(str, "unknown child (fd ", fd_str, ")", NULL);
85     return str;
86 }
87
88
89 void startup_tape_process(taper_program)
90 char *taper_program;
91 {
92     int fd[2];
93
94     if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1)
95         error("taper pipe: %s", strerror(errno));
96     if(fd[0] < 0 || fd[0] >= FD_SETSIZE) {
97         error("taper socketpair 0: descriptor %d out of range (0 .. %d)\n",
98               fd[0], FD_SETSIZE-1);
99     }
100     if(fd[1] < 0 || fd[1] >= FD_SETSIZE) {
101         error("taper socketpair 1: descriptor %d out of range (0 .. %d)\n",
102               fd[1], FD_SETSIZE-1);
103     }
104
105     switch(taper_pid = fork()) {
106     case -1:
107         error("fork taper: %s", strerror(errno));
108     case 0:     /* child process */
109         aclose(fd[0]);
110         if(dup2(fd[1], 0) == -1 || dup2(fd[1], 1) == -1)
111             error("taper dup2: %s", strerror(errno));
112         execle(taper_program, "taper", config_name, (char *)0, safe_env());
113         error("exec %s: %s", taper_program, strerror(errno));
114     default:    /* parent process */
115         aclose(fd[1]);
116         taper = fd[0];
117         addfd(taper, &readset, &maxfd);
118     }
119 }
120
121 void startup_dump_process(dumper, dumper_program)
122 dumper_t *dumper;
123 char *dumper_program;
124 {
125     int fd[2];
126
127     if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1)
128         error("%s pipe: %s", dumper->name, strerror(errno));
129
130     switch(dumper->pid = fork()) {
131     case -1:
132         error("fork %s: %s", dumper->name, strerror(errno));
133     case 0:             /* child process */
134         aclose(fd[0]);
135         if(dup2(fd[1], 0) == -1 || dup2(fd[1], 1) == -1)
136             error("%s dup2: %s", dumper->name, strerror(errno));
137         execle(dumper_program,
138                dumper->name ? dumper->name : "dumper",
139                config_name,
140                (char *)0,
141                safe_env());
142         error("exec %s (%s): %s", dumper_program,
143               dumper->name, strerror(errno));
144     default:    /* parent process */
145         aclose(fd[1]);
146         dumper->infd = dumper->outfd = fd[0];
147         addfd(dumper->outfd, &readset, &maxfd);
148         dumper->busy = dumper->down = 0;
149         dumper->dp = NULL;
150         fprintf(stderr,"driver: started %s pid %d\n",
151                 dumper->name, dumper->pid);
152         fflush(stderr);
153     }
154 }
155
156 void startup_dump_processes(dumper_program, inparallel)
157 char *dumper_program;
158 int inparallel;
159 {
160     int i;
161     dumper_t *dumper;
162     char number[NUM_STR_SIZE];
163
164     for(dumper = dmptable, i = 0; i < inparallel; dumper++, i++) {
165         ap_snprintf(number, sizeof(number), "%d", i);
166         dumper->name = stralloc2("dumper", number);
167
168         startup_dump_process(dumper, dumper_program);
169     }
170 }
171
172 cmd_t getresult(fd, show, result_argc, result_argv, max_arg)
173 int fd;
174 int show;
175 int *result_argc;
176 char **result_argv;
177 int max_arg;
178 {
179     int arg;
180     cmd_t t;
181     char *line;
182
183     if((line = areads(fd)) == NULL) {
184         if(errno) {
185             error("reading result from %s: %s", childstr(fd), strerror(errno));
186         }
187         *result_argc = 0;                               /* EOF */
188     } else {
189         *result_argc = split(line, result_argv, max_arg, " ");
190     }
191     amfree(line);
192
193     if(show) {
194         printf("driver: result time %s from %s:",
195                walltime_str(curclock()),
196                childstr(fd));
197         for(arg = 1; arg <= *result_argc; arg++)
198             printf(" %s", result_argv[arg]);
199         printf("\n");
200         fflush(stdout);
201     }
202
203 #ifdef DEBUG
204     printf("argc = %d\n", *result_argc);
205     for(arg = 0; arg < *result_argc; arg++)
206         printf("argv[%d] = \"%s\"\n", arg, result_argv[arg]);
207 #endif
208
209     if(*result_argc < 1) return BOGUS;
210
211     for(t = (cmd_t)(BOGUS+1); t < LAST_TOK; t++)
212         if(strcmp(result_argv[1], cmdstr[t]) == 0) return t;
213
214     return BOGUS;
215 }
216
217
218 int taper_cmd(cmd, /* optional */ ptr, destname, level, datestamp)
219 cmd_t cmd;
220 void *ptr;
221 char *destname;
222 int level;
223 char *datestamp;
224 {
225     char *cmdline = NULL;
226     char number[NUM_STR_SIZE];
227     disk_t *dp;
228     int l, n, s;
229     char *features;
230
231     switch(cmd) {
232     case START_TAPER:
233         cmdline = vstralloc(cmdstr[cmd], " ", (char *)ptr, "\n", NULL);
234         break;
235     case FILE_WRITE:
236         dp = (disk_t *) ptr;
237         ap_snprintf(number, sizeof(number), "%d", level);
238         features = am_feature_to_string(dp->host->features);
239         cmdline = vstralloc(cmdstr[cmd],
240                             " ", disk2serial(dp),
241                             " ", destname,
242                             " ", dp->host->hostname,
243                             " ", features,
244                             " ", dp->name,
245                             " ", number,
246                             " ", datestamp,
247                             "\n", NULL);
248         amfree(features);
249         break;
250     case PORT_WRITE:
251         dp = (disk_t *) ptr;
252         ap_snprintf(number, sizeof(number), "%d", level);
253         features = am_feature_to_string(dp->host->features);
254         cmdline = vstralloc(cmdstr[cmd],
255                             " ", disk2serial(dp),
256                             " ", dp->host->hostname,
257                             " ", features,
258                             " ", dp->name,
259                             " ", number,
260                             " ", datestamp,
261                             "\n", NULL);
262         amfree(features);
263         break;
264     case QUIT:
265         cmdline = stralloc2(cmdstr[cmd], "\n");
266         break;
267     default:
268         error("Don't know how to send %s command to taper", cmdstr[cmd]);
269     }
270     /*
271      * Note: cmdline already has a '\n'.
272      */
273     printf("driver: send-cmd time %s to taper: %s",
274            walltime_str(curclock()), cmdline);
275     fflush(stdout);
276     for(l = 0, n = strlen(cmdline); l < n; l += s) {
277         if((s = write(taper, cmdline + l, n - l)) < 0) {
278             printf("writing taper command: %s\n", strerror(errno));
279             fflush(stdout);
280             amfree(cmdline);
281             return 0;
282         }
283     }
284     amfree(cmdline);
285     return 1;
286 }
287
288 int dumper_cmd(dumper, cmd, /* optional */ dp)
289 dumper_t *dumper;
290 cmd_t cmd;
291 disk_t *dp;
292 {
293     char *cmdline = NULL;
294     char number[NUM_STR_SIZE];
295     char chunksize[NUM_STR_SIZE];
296     char use[NUM_STR_SIZE];
297     int l, n, s;
298     char *o;
299     int activehd=0;
300     assignedhd_t **h=NULL;
301     char *device;
302     char *features;
303
304     if(dp && sched(dp) && sched(dp)->holdp) {
305         h = sched(dp)->holdp;
306         activehd = sched(dp)->activehd;
307     }
308
309     if(dp && dp->device) {
310         device = dp->device;
311     }
312     else {
313         device = "NODEVICE";
314     }
315
316     switch(cmd) {
317     case FILE_DUMP:
318         holdalloc(h[activehd]->disk)->allocated_dumpers++;
319         ap_snprintf(number, sizeof(number), "%d", sched(dp)->level);
320         ap_snprintf(chunksize, sizeof(chunksize), "%ld", h[0]->disk->chunksize);
321         ap_snprintf(use, sizeof(use), "%ld", h[0]->reserved );
322         features = am_feature_to_string(dp->host->features);
323         o = optionstr(dp, dp->host->features, NULL);
324         cmdline = vstralloc(cmdstr[cmd],
325                             " ", disk2serial(dp),
326                             " ", sched(dp)->destname,
327                             " ", dp->host->hostname,
328                             " ", features,
329                             " ", dp->name,
330                             " ", device,
331                             " ", number,
332                             " ", sched(dp)->dumpdate,
333                             " ", chunksize,
334                             " ", dp->program,
335                             " ", use,
336                             " |", o,
337                             "\n", NULL);
338         amfree(features);
339         amfree(o);
340         break;
341     case PORT_DUMP:
342         ap_snprintf(number, sizeof(number), "%d", sched(dp)->level);
343         features = am_feature_to_string(dp->host->features);
344         o = optionstr(dp, dp->host->features, NULL);
345         cmdline = vstralloc(cmdstr[cmd],
346                             " ", disk2serial(dp),
347                             " ", sched(dp)->destname,
348                             " ", dp->host->hostname,
349                             " ", features,
350                             " ", dp->name,
351                             " ", device,
352                             " ", number,
353                             " ", sched(dp)->dumpdate,
354                             " ", dp->program,
355                             " |", o,
356                             "\n", NULL);
357         amfree(features);
358         amfree(o);
359         break;
360     case QUIT:
361     case ABORT:
362         if( dp ) {
363             cmdline = vstralloc(cmdstr[cmd],
364                                 " ", sched(dp)->destname,
365                                 "\n", NULL );
366         } else {
367             cmdline = stralloc2(cmdstr[cmd], "\n");
368         }
369         break;
370     case CONTINUE:
371         if( dp ) {
372             holdalloc(h[activehd]->disk)->allocated_dumpers++;
373             ap_snprintf(chunksize, sizeof(chunksize), "%ld", 
374                         h[activehd]->disk->chunksize );
375             ap_snprintf(use, sizeof(use), "%ld", 
376                         h[activehd]->reserved - h[activehd]->used );
377             cmdline = vstralloc(cmdstr[cmd],
378                                 " ", h[activehd]->destname,
379                                 " ", chunksize,
380                                 " ", use,
381                                 "\n", NULL );
382         } else {
383             cmdline = stralloc2(cmdstr[cmd], "\n");
384         }
385         break;
386     default:
387         error("Don't know how to send %s command to dumper", cmdstr[cmd]);
388     }
389     /*
390      * Note: cmdline already has a '\n'.
391      */
392     if(dumper->down) {
393         printf("driver: send-cmd time %s ignored to down dumper %s: %s",
394                walltime_str(curclock()), dumper->name, cmdline);
395     } else {
396         printf("driver: send-cmd time %s to %s: %s",
397                walltime_str(curclock()), dumper->name, cmdline);
398         fflush(stdout);
399         for(l = 0, n = strlen(cmdline); l < n; l += s) {
400             if((s = write(dumper->infd, cmdline + l, n - l)) < 0) {
401                 printf("writing %s command: %s\n", dumper->name, 
402                        strerror(errno));
403                 fflush(stdout);
404                 amfree(cmdline);
405                 return 0;
406             }
407         }
408     }
409     amfree(cmdline);
410     return 1;
411 }
412
413 #define MAX_SERIAL MAX_DUMPERS+1        /* one for the taper */
414
415 long generation = 1;
416
417 struct serial_s {
418     long gen;
419     disk_t *dp;
420 } stable[MAX_SERIAL];
421
422 disk_t *serial2disk(str)
423 char *str;
424 {
425     int rc, s;
426     long gen;
427
428     rc = sscanf(str, "%d-%ld", &s, &gen);
429     if(rc != 2) {
430         error("error [serial2disk \"%s\" parse error]", str);
431     } else if (s < 0 || s >= MAX_SERIAL) {
432         error("error [serial out of range 0..%d: %d]", MAX_SERIAL, s);
433     }
434     if(gen != stable[s].gen)
435         printf("driver: error time %s serial gen mismatch\n",
436                walltime_str(curclock()));
437     return stable[s].dp;
438 }
439
440 void free_serial(str)
441 char *str;
442 {
443     int rc, s;
444     long gen;
445
446     rc = sscanf(str, "%d-%ld", &s, &gen);
447     if(!(rc == 2 && s >= 0 && s < MAX_SERIAL)) {
448         /* nuke self to get core dump for Brett */
449         fprintf(stderr, "driver: free_serial: str \"%s\" rc %d s %d\n",
450                 str, rc, s);
451         fflush(stderr);
452         abort();
453     }
454
455     if(gen != stable[s].gen)
456         printf("driver: error time %s serial gen mismatch\n",
457                walltime_str(curclock()));
458     stable[s].gen = 0;
459 }
460
461
462 char *disk2serial(dp)
463 disk_t *dp;
464 {
465     int s;
466     static char str[NUM_STR_SIZE];
467
468     /* find unused serial number */
469     for(s = 0; s < MAX_SERIAL; s++) if(stable[s].gen == 0) break;
470     if(s >= MAX_SERIAL) {
471         printf("driver: error time %s bug: out of serial numbers\n",
472                walltime_str(curclock()));
473         s = 0;
474     }
475
476     stable[s].gen = generation++;
477     stable[s].dp = dp;
478
479     ap_snprintf(str, sizeof(str), "%02d-%05ld", s, stable[s].gen);
480     return str;
481 }
482
483 void update_info_dumper(dp, origsize, dumpsize, dumptime)
484      disk_t *dp;
485      long origsize;
486      long dumpsize;
487      long dumptime;
488 {
489     int level, i;
490     info_t info;
491     stats_t *infp;
492     perf_t *perfp;
493     char *conf_infofile;
494
495     level = sched(dp)->level;
496
497     conf_infofile = getconf_str(CNF_INFOFILE);
498     if (*conf_infofile == '/') {
499         conf_infofile = stralloc(conf_infofile);
500     } else {
501         conf_infofile = stralloc2(config_dir, conf_infofile);
502     }
503     if (open_infofile(conf_infofile)) {
504         error("could not open info db \"%s\"", conf_infofile);
505     }
506     amfree(conf_infofile);
507
508     get_info(dp->host->hostname, dp->name, &info);
509
510     /* Clean up information about this and higher-level dumps.  This
511        assumes that update_info_dumper() is always run before
512        update_info_taper(). */
513     for (i = level; i < DUMP_LEVELS; ++i) {
514       infp = &info.inf[i];
515       infp->size = -1;
516       infp->csize = -1;
517       infp->secs = -1;
518       infp->date = (time_t)-1;
519       infp->label[0] = '\0';
520       infp->filenum = 0;
521     }
522
523     /* now store information about this dump */
524     infp = &info.inf[level];
525     infp->size = origsize;
526     infp->csize = dumpsize;
527     infp->secs = dumptime;
528     infp->date = sched(dp)->timestamp;
529
530     if(level == 0) perfp = &info.full;
531     else perfp = &info.incr;
532
533     /* Update the stats, but only if the new values are meaningful */
534     if(dp->compress != COMP_NONE && origsize > 0L) {
535         newperf(perfp->comp, dumpsize/(float)origsize);
536     }
537     if(dumptime > 0L) {
538         if(dumptime >= dumpsize)
539             newperf(perfp->rate, 1);
540         else
541             newperf(perfp->rate, dumpsize/dumptime);
542     }
543
544     if(getconf_int(CNF_RESERVE)<100) {
545         info.command = NO_COMMAND;
546     }
547
548     if(level == info.last_level)
549         info.consecutive_runs++;
550     else {
551         info.last_level = level;
552         info.consecutive_runs = 1;
553     }
554     if(put_info(dp->host->hostname, dp->name, &info))
555         error("infofile update failed (%s,%s)\n", dp->host->hostname, dp->name);
556
557     close_infofile();
558 }
559
560 void update_info_taper(dp, label, filenum, level)
561 disk_t *dp;
562 char *label;
563 int filenum;
564 int level;
565 {
566     info_t info;
567     stats_t *infp;
568     int rc;
569
570     rc = open_infofile(getconf_str(CNF_INFOFILE));
571     if(rc)
572         error("could not open infofile %s: %s (%d)", getconf_str(CNF_INFOFILE),
573               strerror(errno), rc);
574
575     get_info(dp->host->hostname, dp->name, &info);
576
577     infp = &info.inf[level];
578     /* XXX - should we record these two if no-record? */
579     strncpy(infp->label, label, sizeof(infp->label)-1);
580     infp->label[sizeof(infp->label)-1] = '\0';
581     infp->filenum = filenum;
582
583     info.command = NO_COMMAND;
584
585     if(put_info(dp->host->hostname, dp->name, &info))
586         error("infofile update failed (%s,%s)\n", dp->host->hostname, dp->name);
587
588     close_infofile();
589 }
590
591 /* Free an array of pointers to assignedhd_t after freeing the
592  * assignedhd_t themselves. The array must be NULL-terminated.
593  */
594 void free_assignedhd( ahd )
595 assignedhd_t **ahd;
596 {
597     int i;
598
599     if( !ahd ) { return; }
600
601     for( i = 0; ahd[i]; i++ ) {
602         amfree(ahd[i]->destname);
603         amfree(ahd[i]);
604     }
605     amfree(ahd);
606 }