2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Author: James da Silva, Systems Design and Analysis Group
24 * Computer Science Department
25 * University of Maryland at College Park
28 * $Id: driverio.c,v 1.81 2006/03/11 21:57:18 martinea Exp $
30 * I/O-related functions for driver program
40 #include "server_util.h"
42 #define GLOBAL /* the global variables defined here */
47 static const char *childstr P((int));
55 for(dumper = dmptable; dumper < dmptable + MAX_DUMPERS; dumper++) {
65 static char buf[NUM_STR_SIZE + 32];
71 for (dumper = dmptable; dumper < dmptable + MAX_DUMPERS; dumper++) {
73 return (dumper->name);
74 if (dumper->chunker->fd == fd)
75 return (dumper->chunker->name);
77 snprintf(buf, sizeof(buf), "unknown child (fd %d)", fd);
82 void startup_tape_process(taper_program)
87 if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1)
88 error("taper pipe: %s", strerror(errno));
89 if(fd[0] < 0 || fd[0] >= FD_SETSIZE) {
90 error("taper socketpair 0: descriptor %d out of range (0 .. %d)\n",
93 if(fd[1] < 0 || fd[1] >= FD_SETSIZE) {
94 error("taper socketpair 1: descriptor %d out of range (0 .. %d)\n",
98 switch(taper_pid = fork()) {
100 error("fork taper: %s", strerror(errno));
101 case 0: /* child process */
103 if(dup2(fd[1], 0) == -1 || dup2(fd[1], 1) == -1)
104 error("taper dup2: %s", strerror(errno));
105 execle(taper_program, "taper", config_name, (char *)0, safe_env());
106 error("exec %s: %s", taper_program, strerror(errno));
107 default: /* parent process */
110 taper_ev_read = NULL;
114 void startup_dump_process(dumper, dumper_program)
116 char *dumper_program;
120 if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1)
121 error("%s pipe: %s", dumper->name, strerror(errno));
123 switch(dumper->pid = fork()) {
125 error("fork %s: %s", dumper->name, strerror(errno));
126 case 0: /* child process */
128 if(dup2(fd[1], 0) == -1 || dup2(fd[1], 1) == -1)
129 error("%s dup2: %s", dumper->name, strerror(errno));
130 execle(dumper_program,
131 dumper->name ? dumper->name : "dumper",
135 error("exec %s (%s): %s", dumper_program,
136 dumper->name, strerror(errno));
137 default: /* parent process */
140 dumper->ev_read = NULL;
141 dumper->busy = dumper->down = 0;
143 fprintf(stderr,"driver: started %s pid %d\n",
144 dumper->name, dumper->pid);
149 void startup_dump_processes(dumper_program, inparallel)
150 char *dumper_program;
155 char number[NUM_STR_SIZE];
157 for(dumper = dmptable, i = 0; i < inparallel; dumper++, i++) {
158 snprintf(number, sizeof(number), "%d", i);
159 dumper->name = stralloc2("dumper", number);
160 dumper->chunker = &chktable[i];
161 chktable[i].name = stralloc2("chunker", number);
162 chktable[i].dumper = dumper;
165 startup_dump_process(dumper, dumper_program);
169 void startup_chunk_process(chunker, chunker_program)
171 char *chunker_program;
175 if(socketpair(AF_UNIX, SOCK_STREAM, 0, fd) == -1)
176 error("%s pipe: %s", chunker->name, strerror(errno));
178 switch(chunker->pid = fork()) {
180 error("fork %s: %s", chunker->name, strerror(errno));
181 case 0: /* child process */
183 if(dup2(fd[1], 0) == -1 || dup2(fd[1], 1) == -1)
184 error("%s dup2: %s", chunker->name, strerror(errno));
185 execle(chunker_program,
186 chunker->name ? chunker->name : "chunker",
190 error("exec %s (%s): %s", chunker_program,
191 chunker->name, strerror(errno));
192 default: /* parent process */
196 chunker->ev_read = NULL;
197 fprintf(stderr,"driver: started %s pid %d\n",
198 chunker->name, chunker->pid);
203 cmd_t getresult(fd, show, result_argc, result_argv, max_arg)
214 if((line = areads(fd)) == NULL) {
216 error("reading result from %s: %s", childstr(fd), strerror(errno));
218 *result_argc = 0; /* EOF */
220 *result_argc = split(line, result_argv, max_arg, " ");
224 printf("driver: result time %s from %s:",
225 walltime_str(curclock()),
228 for(arg = 1; arg <= *result_argc; arg++) {
229 printf(" %s", result_argv[arg]);
240 printf("argc = %d\n", *result_argc);
241 for(arg = 0; arg < *result_argc; arg++)
242 printf("argv[%d] = \"%s\"\n", arg, result_argv[arg]);
245 if(*result_argc < 1) return BOGUS;
247 for(t = (cmd_t)(BOGUS+1); t < LAST_TOK; t++)
248 if(strcmp(result_argv[1], cmdstr[t]) == 0) return t;
254 int taper_cmd(cmd, /* optional */ ptr, destname, level, datestamp)
261 char *cmdline = NULL;
262 char number[NUM_STR_SIZE];
263 char splitsize[NUM_STR_SIZE];
264 char fallback_splitsize[NUM_STR_SIZE];
265 char *diskbuffer = NULL;
271 cmdline = vstralloc(cmdstr[cmd], " ", (char *)ptr, "\n", NULL);
275 snprintf(number, sizeof(number), "%d", level);
276 snprintf(splitsize, sizeof(splitsize), "%ld", dp->tape_splitsize);
277 features = am_feature_to_string(dp->host->features);
278 cmdline = vstralloc(cmdstr[cmd],
279 " ", disk2serial(dp),
281 " ", dp->host->hostname,
292 snprintf(number, sizeof(number), "%d", level);
295 If we haven't been given a place to buffer split dumps to disk,
296 make the argument something besides and empty string so's taper
299 if(!dp->split_diskbuffer || dp->split_diskbuffer[0] == '\0'){
302 diskbuffer = dp->split_diskbuffer;
304 snprintf(splitsize, sizeof(splitsize), "%ld", dp->tape_splitsize);
305 snprintf(fallback_splitsize, sizeof(fallback_splitsize),
306 "%ld", dp->fallback_splitsize);
307 features = am_feature_to_string(dp->host->features);
308 cmdline = vstralloc(cmdstr[cmd],
309 " ", disk2serial(dp),
310 " ", dp->host->hostname,
317 " ", fallback_splitsize,
322 cmdline = stralloc2(cmdstr[cmd], "\n");
325 error("Don't know how to send %s command to taper", cmdstr[cmd]);
328 * Note: cmdline already has a '\n'.
330 printf("driver: send-cmd time %s to taper: %s",
331 walltime_str(curclock()), cmdline);
333 if (fullwrite(taper, cmdline, strlen(cmdline)) < 0) {
334 printf("writing taper command: %s\n", strerror(errno));
343 int dumper_cmd(dumper, cmd, /* optional */ dp)
348 char *cmdline = NULL;
349 char number[NUM_STR_SIZE];
350 char numberport[NUM_STR_SIZE];
353 assignedhd_t **h=NULL;
357 if(dp && sched(dp) && sched(dp)->holdp) {
358 h = sched(dp)->holdp;
359 activehd = sched(dp)->activehd;
362 if(dp && dp->device) {
372 snprintf(number, sizeof(number), "%d", sched(dp)->level);
373 snprintf(numberport, sizeof(numberport), "%d", dumper->output_port);
374 features = am_feature_to_string(dp->host->features);
375 o = optionstr(dp, dp->host->features, NULL);
376 cmdline = vstralloc(cmdstr[cmd],
377 " ", disk2serial(dp),
379 " ", dp->host->hostname,
384 " ", sched(dp)->dumpdate,
391 error("PORT-DUMP without disk pointer\n");
398 cmdline = vstralloc(cmdstr[cmd],
399 " ", sched(dp)->destname,
402 cmdline = stralloc2(cmdstr[cmd], "\n");
406 error("Don't know how to send %s command to dumper", cmdstr[cmd]);
409 * Note: cmdline already has a '\n'.
412 printf("driver: send-cmd time %s ignored to down dumper %s: %s",
413 walltime_str(curclock()), dumper->name, cmdline);
415 printf("driver: send-cmd time %s to %s: %s",
416 walltime_str(curclock()), dumper->name, cmdline);
418 if (fullwrite(dumper->fd, cmdline, strlen(cmdline)) < 0) {
419 printf("writing %s command: %s\n", dumper->name, strerror(errno));
429 int chunker_cmd(chunker, cmd, dp)
434 char *cmdline = NULL;
435 char number[NUM_STR_SIZE];
436 char chunksize[NUM_STR_SIZE];
437 char use[NUM_STR_SIZE];
440 assignedhd_t **h=NULL;
443 if(dp && sched(dp) && sched(dp)->holdp) {
444 h = sched(dp)->holdp;
445 activehd = sched(dp)->activehd;
451 holdalloc(h[activehd]->disk)->allocated_dumpers++;
452 snprintf(number, sizeof(number), "%d", sched(dp)->level);
453 snprintf(chunksize, sizeof(chunksize), "%ld", h[0]->disk->chunksize);
454 snprintf(use, sizeof(use), "%ld", h[0]->reserved );
455 features = am_feature_to_string(dp->host->features);
456 o = optionstr(dp, dp->host->features, NULL);
457 cmdline = vstralloc(cmdstr[cmd],
458 " ", disk2serial(dp),
459 " ", sched(dp)->destname,
460 " ", dp->host->hostname,
464 " ", sched(dp)->dumpdate,
473 error("Write command without disk and holding disk.\n",
480 holdalloc(h[activehd]->disk)->allocated_dumpers++;
481 snprintf(chunksize, sizeof(chunksize), "%ld",
482 h[activehd]->disk->chunksize );
483 snprintf(use, sizeof(use), "%ld",
484 h[activehd]->reserved - h[activehd]->used );
485 cmdline = vstralloc(cmdstr[cmd],
486 " ", disk2serial(dp),
487 " ", h[activehd]->destname,
492 cmdline = stralloc2(cmdstr[cmd], "\n");
496 cmdline = stralloc2(cmdstr[cmd], "\n");
501 cmdline = vstralloc(cmdstr[cmd],
502 " ", disk2serial(dp),
505 cmdline = vstralloc(cmdstr[cmd], "\n");
509 error("Don't know how to send %s command to chunker", cmdstr[cmd]);
512 * Note: cmdline already has a '\n'.
514 printf("driver: send-cmd time %s to %s: %s",
515 walltime_str(curclock()), chunker->name, cmdline);
517 if (fullwrite(chunker->fd, cmdline, strlen(cmdline)) < 0) {
518 printf("writing %s command: %s\n", chunker->name, strerror(errno));
527 #define MAX_SERIAL MAX_DUMPERS+1 /* one for the taper */
534 } stable[MAX_SERIAL];
536 disk_t *serial2disk(str)
542 rc = sscanf(str, "%d-%ld", &s, &gen);
544 error("error [serial2disk \"%s\" parse error]", str);
545 } else if (s < 0 || s >= MAX_SERIAL) {
546 error("error [serial out of range 0..%d: %d]", MAX_SERIAL, s);
548 if(gen != stable[s].gen)
549 printf("driver: error time %s serial gen mismatch %s\n",
550 walltime_str(curclock()), str);
554 void free_serial(str)
560 rc = sscanf(str, "%d-%ld", &s, &gen);
561 if(!(rc == 2 && s >= 0 && s < MAX_SERIAL)) {
562 /* nuke self to get core dump for Brett */
563 fprintf(stderr, "driver: free_serial: str \"%s\" rc %d s %d\n",
569 if(gen != stable[s].gen)
570 printf("driver: error time %s serial gen mismatch\n",
571 walltime_str(curclock()));
577 void free_serial_dp(dp)
582 for(s = 0; s < MAX_SERIAL; s++) {
583 if(stable[s].dp == dp) {
590 printf("driver: error time %s serial not found\n",
591 walltime_str(curclock()));
595 void check_unfree_serial()
599 /* find used serial number */
600 for(s = 0; s < MAX_SERIAL; s++) {
601 if(stable[s].gen != 0 || stable[s].dp != NULL) {
602 printf("driver: error time %s bug: serial in use: %02d-%05ld\n",
603 walltime_str(curclock()), s, stable[s].gen);
608 char *disk2serial(dp)
612 static char str[NUM_STR_SIZE];
614 for(s = 0; s < MAX_SERIAL; s++) {
615 if(stable[s].dp == dp) {
616 snprintf(str, sizeof(str), "%02d-%05ld", s, stable[s].gen);
621 /* find unused serial number */
622 for(s = 0; s < MAX_SERIAL; s++)
623 if(stable[s].gen == 0 && stable[s].dp == NULL)
625 if(s >= MAX_SERIAL) {
626 printf("driver: error time %s bug: out of serial numbers\n",
627 walltime_str(curclock()));
631 stable[s].gen = generation++;
634 snprintf(str, sizeof(str), "%02d-%05ld", s, stable[s].gen);
638 void update_info_dumper(dp, origsize, dumpsize, dumptime)
650 level = sched(dp)->level;
652 conf_infofile = getconf_str(CNF_INFOFILE);
653 if (*conf_infofile == '/') {
654 conf_infofile = stralloc(conf_infofile);
656 conf_infofile = stralloc2(config_dir, conf_infofile);
658 if (open_infofile(conf_infofile)) {
659 error("could not open info db \"%s\"", conf_infofile);
661 amfree(conf_infofile);
663 get_info(dp->host->hostname, dp->name, &info);
665 /* Clean up information about this and higher-level dumps. This
666 assumes that update_info_dumper() is always run before
667 update_info_taper(). */
668 for (i = level; i < DUMP_LEVELS; ++i) {
673 infp->date = (time_t)-1;
674 infp->label[0] = '\0';
678 /* now store information about this dump */
679 infp = &info.inf[level];
680 infp->size = origsize;
681 infp->csize = dumpsize;
682 infp->secs = dumptime;
683 infp->date = sched(dp)->timestamp;
685 if(level == 0) perfp = &info.full;
686 else perfp = &info.incr;
688 /* Update the stats, but only if the new values are meaningful */
689 if(dp->compress != COMP_NONE && origsize > 0L) {
690 newperf(perfp->comp, dumpsize/(float)origsize);
693 if(dumptime >= dumpsize)
694 newperf(perfp->rate, 1);
696 newperf(perfp->rate, dumpsize/dumptime);
699 if(getconf_int(CNF_RESERVE)<100) {
700 info.command = NO_COMMAND;
703 if(level == info.last_level)
704 info.consecutive_runs++;
706 info.last_level = level;
707 info.consecutive_runs = 1;
710 if(origsize >=0 && dumpsize >=0) {
711 for(i=NB_HISTORY-1;i>0;i--) {
712 info.history[i] = info.history[i-1];
715 info.history[0].level = level;
716 info.history[0].size = origsize;
717 info.history[0].csize = dumpsize;
718 info.history[0].date = sched(dp)->timestamp;
719 info.history[0].secs = dumptime;
722 if(put_info(dp->host->hostname, dp->name, &info))
723 error("infofile update failed (%s,%s)\n", dp->host->hostname, dp->name);
728 void update_info_taper(dp, label, filenum, level)
738 rc = open_infofile(getconf_str(CNF_INFOFILE));
740 error("could not open infofile %s: %s (%d)", getconf_str(CNF_INFOFILE),
741 strerror(errno), rc);
743 get_info(dp->host->hostname, dp->name, &info);
745 infp = &info.inf[level];
746 /* XXX - should we record these two if no-record? */
747 strncpy(infp->label, label, sizeof(infp->label)-1);
748 infp->label[sizeof(infp->label)-1] = '\0';
749 infp->filenum = filenum;
751 info.command = NO_COMMAND;
753 if(put_info(dp->host->hostname, dp->name, &info))
754 error("infofile update failed (%s,%s)\n", dp->host->hostname, dp->name);
759 /* Free an array of pointers to assignedhd_t after freeing the
760 * assignedhd_t themselves. The array must be NULL-terminated.
762 void free_assignedhd( ahd )
767 if( !ahd ) { return; }
769 for( i = 0; ahd[i]; i++ ) {
770 amfree(ahd[i]->destname);