Imported Upstream version 2.4.5
[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.2.4 2005/03/16 18:15:28 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                                 " ", disk2serial(dp),
379                                 " ", h[activehd]->destname,
380                                 " ", chunksize,
381                                 " ", use,
382                                 "\n", NULL );
383         } else {
384             cmdline = stralloc2(cmdstr[cmd], "\n");
385         }
386         break;
387     default:
388         error("Don't know how to send %s command to dumper", cmdstr[cmd]);
389     }
390     /*
391      * Note: cmdline already has a '\n'.
392      */
393     if(dumper->down) {
394         printf("driver: send-cmd time %s ignored to down dumper %s: %s",
395                walltime_str(curclock()), dumper->name, cmdline);
396     } else {
397         printf("driver: send-cmd time %s to %s: %s",
398                walltime_str(curclock()), dumper->name, cmdline);
399         fflush(stdout);
400         for(l = 0, n = strlen(cmdline); l < n; l += s) {
401             if((s = write(dumper->infd, cmdline + l, n - l)) < 0) {
402                 printf("writing %s command: %s\n", dumper->name, 
403                        strerror(errno));
404                 fflush(stdout);
405                 amfree(cmdline);
406                 return 0;
407             }
408         }
409     }
410     amfree(cmdline);
411     return 1;
412 }
413
414 #define MAX_SERIAL MAX_DUMPERS+1        /* one for the taper */
415
416 long generation = 1;
417
418 struct serial_s {
419     long gen;
420     disk_t *dp;
421 } stable[MAX_SERIAL];
422
423 disk_t *serial2disk(str)
424 char *str;
425 {
426     int rc, s;
427     long gen;
428
429     rc = sscanf(str, "%d-%ld", &s, &gen);
430     if(rc != 2) {
431         error("error [serial2disk \"%s\" parse error]", str);
432     } else if (s < 0 || s >= MAX_SERIAL) {
433         error("error [serial out of range 0..%d: %d]", MAX_SERIAL, s);
434     }
435     if(gen != stable[s].gen)
436         printf("driver: error time %s serial gen mismatch\n",
437                walltime_str(curclock()));
438     return stable[s].dp;
439 }
440
441 void free_serial(str)
442 char *str;
443 {
444     int rc, s;
445     long gen;
446
447     rc = sscanf(str, "%d-%ld", &s, &gen);
448     if(!(rc == 2 && s >= 0 && s < MAX_SERIAL)) {
449         /* nuke self to get core dump for Brett */
450         fprintf(stderr, "driver: free_serial: str \"%s\" rc %d s %d\n",
451                 str, rc, s);
452         fflush(stderr);
453         abort();
454     }
455
456     if(gen != stable[s].gen)
457         printf("driver: error time %s serial gen mismatch\n",
458                walltime_str(curclock()));
459     stable[s].gen = 0;
460     stable[s].dp = NULL;
461 }
462
463
464 char *disk2serial(dp)
465 disk_t *dp;
466 {
467     int s;
468     static char str[NUM_STR_SIZE];
469
470     for(s = 0; s < MAX_SERIAL; s++) {
471         if(stable[s].dp == dp) {
472             ap_snprintf(str, sizeof(str), "%02d-%05ld", s, stable[s].gen);
473             return str;
474         }
475     }
476
477     /* find unused serial number */
478     for(s = 0; s < MAX_SERIAL; s++)
479         if(stable[s].gen == 0 && stable[s].dp == NULL)
480             break;
481     if(s >= MAX_SERIAL) {
482         printf("driver: error time %s bug: out of serial numbers\n",
483                walltime_str(curclock()));
484         s = 0;
485     }
486
487     stable[s].gen = generation++;
488     stable[s].dp = dp;
489
490     ap_snprintf(str, sizeof(str), "%02d-%05ld", s, stable[s].gen);
491     return str;
492 }
493
494 void update_info_dumper(dp, origsize, dumpsize, dumptime)
495      disk_t *dp;
496      long origsize;
497      long dumpsize;
498      long dumptime;
499 {
500     int level, i;
501     info_t info;
502     stats_t *infp;
503     perf_t *perfp;
504     char *conf_infofile;
505
506     level = sched(dp)->level;
507
508     conf_infofile = getconf_str(CNF_INFOFILE);
509     if (*conf_infofile == '/') {
510         conf_infofile = stralloc(conf_infofile);
511     } else {
512         conf_infofile = stralloc2(config_dir, conf_infofile);
513     }
514     if (open_infofile(conf_infofile)) {
515         error("could not open info db \"%s\"", conf_infofile);
516     }
517     amfree(conf_infofile);
518
519     get_info(dp->host->hostname, dp->name, &info);
520
521     /* Clean up information about this and higher-level dumps.  This
522        assumes that update_info_dumper() is always run before
523        update_info_taper(). */
524     for (i = level; i < DUMP_LEVELS; ++i) {
525       infp = &info.inf[i];
526       infp->size = -1;
527       infp->csize = -1;
528       infp->secs = -1;
529       infp->date = (time_t)-1;
530       infp->label[0] = '\0';
531       infp->filenum = 0;
532     }
533
534     /* now store information about this dump */
535     infp = &info.inf[level];
536     infp->size = origsize;
537     infp->csize = dumpsize;
538     infp->secs = dumptime;
539     infp->date = sched(dp)->timestamp;
540
541     if(level == 0) perfp = &info.full;
542     else perfp = &info.incr;
543
544     /* Update the stats, but only if the new values are meaningful */
545     if(dp->compress != COMP_NONE && origsize > 0L) {
546         newperf(perfp->comp, dumpsize/(float)origsize);
547     }
548     if(dumptime > 0L) {
549         if(dumptime >= dumpsize)
550             newperf(perfp->rate, 1);
551         else
552             newperf(perfp->rate, dumpsize/dumptime);
553     }
554
555     if(getconf_int(CNF_RESERVE)<100) {
556         info.command = NO_COMMAND;
557     }
558
559     if(level == info.last_level)
560         info.consecutive_runs++;
561     else {
562         info.last_level = level;
563         info.consecutive_runs = 1;
564     }
565
566     for(i=NB_HISTORY-1;i>0;i--) {
567         info.history[i] = info.history[i-1];
568     }
569
570     info.history[0].level = level;
571     info.history[0].size  = origsize;
572     info.history[0].csize = dumpsize;
573     info.history[0].date  = sched(dp)->timestamp;
574     info.history[0].secs  = dumptime;
575
576     if(put_info(dp->host->hostname, dp->name, &info))
577         error("infofile update failed (%s,%s)\n", dp->host->hostname, dp->name);
578
579     close_infofile();
580 }
581
582 void update_info_taper(dp, label, filenum, level)
583 disk_t *dp;
584 char *label;
585 int filenum;
586 int level;
587 {
588     info_t info;
589     stats_t *infp;
590     int rc;
591
592     rc = open_infofile(getconf_str(CNF_INFOFILE));
593     if(rc)
594         error("could not open infofile %s: %s (%d)", getconf_str(CNF_INFOFILE),
595               strerror(errno), rc);
596
597     get_info(dp->host->hostname, dp->name, &info);
598
599     infp = &info.inf[level];
600     /* XXX - should we record these two if no-record? */
601     strncpy(infp->label, label, sizeof(infp->label)-1);
602     infp->label[sizeof(infp->label)-1] = '\0';
603     infp->filenum = filenum;
604
605     info.command = NO_COMMAND;
606
607     if(put_info(dp->host->hostname, dp->name, &info))
608         error("infofile update failed (%s,%s)\n", dp->host->hostname, dp->name);
609
610     close_infofile();
611 }
612
613 /* Free an array of pointers to assignedhd_t after freeing the
614  * assignedhd_t themselves. The array must be NULL-terminated.
615  */
616 void free_assignedhd( ahd )
617 assignedhd_t **ahd;
618 {
619     int i;
620
621     if( !ahd ) { return; }
622
623     for( i = 0; ahd[i]; i++ ) {
624         amfree(ahd[i]->destname);
625         amfree(ahd[i]);
626     }
627     amfree(ahd);
628 }