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 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
26 /* $Id: dumper.c,v 1.75.2.14.2.7.2.17.2.3 2005/03/31 13:08:05 martinea Exp $
28 * requests remote amandad processes to dump filesystems
40 #include "fileheader.h"
41 #include "amfeatures.h"
42 #include "server_util.h"
46 #include "dumper-krb4.c"
48 #define NAUGHTY_BITS_INITIALIZE /* I'd tell you what these do */
49 #define NAUGHTY_BITS /* but then I'd have to kill you */
61 #define CONNECT_TIMEOUT 5*60
62 #define MESGBUF_SIZE 4*1024
64 #define STARTUP_TIMEOUT 60
69 char databuf[DISK_BLOCK_BYTES];
70 char mesgbuf[MESGBUF_SIZE+1];
72 char *datain; /* where to read in data */
73 char *dataout; /* where to write out data */
74 char *datalimit; /* end of the data area */
76 long dumpsize; /* total size of dump */
79 long filesize; /* size of current holding disk file */
81 static enum { srvcomp_none, srvcomp_fast, srvcomp_best } srvcompress;
83 static FILE *errf = NULL;
84 char *filename = NULL; /* holding disk base file name */
85 string_t cont_filename;
86 char *hostname = NULL;
87 am_feature_t *their_features = NULL;
88 char *diskname = NULL;
91 char *progname = NULL;
93 char *dumpdate = NULL;
95 long use; /* space remaining in this hold disk */
97 char *backup_name = NULL;
98 char *recover_cmd = NULL;
99 char *compress_suffix = NULL;
104 long split_size; /* next dumpsize we will split at */
111 static am_feature_t *our_features = NULL;
112 static char *our_feature_string = NULL;
114 /* local functions */
115 int main P((int main_argc, char **main_argv));
116 int do_dump P((int mesgfd, int datafd, int indexfd, int outfd));
117 void check_options P((char *options));
118 void service_ports_init P((void));
119 int write_tapeheader P((int outfd, dumpfile_t *type));
120 int write_dataptr P((int outf));
121 int update_dataptr P((int *outf, int size));
122 static void process_dumpeof P((void));
123 static void process_dumpline P((char *str));
124 static void add_msg_data P((char *str, int len));
125 static void log_msgout P((logtype_t typ));
126 void sendbackup_response P((proto_t *p, pkt_t *pkt));
127 int startup_dump P((char *hostname, char *disk, char *device, int level,
128 char *dumpdate, char *progname, char *options));
131 void check_options(options)
135 krb4_auth = strstr(options, "krb4-auth;") != NULL;
136 kencrypt = strstr(options, "kencrypt;") != NULL;
138 if (strstr(options, "srvcomp-best;") != NULL)
139 srvcompress = srvcomp_best;
140 else if (strstr(options, "srvcomp-fast;") != NULL)
141 srvcompress = srvcomp_fast;
143 srvcompress = srvcomp_none;
146 void service_ports_init()
148 struct servent *amandad;
150 if((amandad = getservbyname(AMANDA_SERVICE_NAME, "udp")) == NULL) {
151 amanda_port = AMANDA_SERVICE_DEFAULT;
152 log_add(L_WARNING, "no %s/udp service, using default port %d",
153 AMANDA_SERVICE_NAME, AMANDA_SERVICE_DEFAULT);
156 amanda_port = ntohs(amandad->s_port);
159 if((amandad = getservbyname(KAMANDA_SERVICE_NAME, "udp")) == NULL) {
160 kamanda_port = KAMANDA_SERVICE_DEFAULT;
161 log_add(L_WARNING, "no %s/udp service, using default port %d",
162 KAMANDA_SERVICE_NAME, KAMANDA_SERVICE_DEFAULT);
165 kamanda_port = ntohs(amandad->s_port);
170 int main(main_argc, main_argv)
174 struct cmdargs cmdargs;
176 int outfd, protocol_port, taper_port, rc;
178 unsigned long malloc_hist_1, malloc_size_1;
179 unsigned long malloc_hist_2, malloc_size_2;
183 char *tmp_filename = NULL, *pc;
186 for(fd = 3; fd < FD_SETSIZE; fd++) {
188 * Make sure nobody spoofs us with a lot of extra open files
189 * that would cause an open we do to get a very high file
190 * descriptor, which in turn might be used as an index into
191 * an array (e.g. an fd_set).
198 malloc_size_1 = malloc_inuse(&malloc_hist_1);
200 erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
201 set_logerror(logerror);
204 config_name = stralloc(main_argv[1]);
205 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
207 char my_cwd[STR_SIZE];
209 if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
210 error("cannot determine current working directory");
212 config_dir = stralloc2(my_cwd, "/");
213 if ((config_name = strrchr(my_cwd, '/')) != NULL) {
214 config_name = stralloc(config_name + 1);
220 our_features = am_init_feature_set();
221 our_feature_string = am_feature_to_string(our_features);
223 conffile = stralloc2(config_dir, CONFFILE_NAME);
224 if(read_conffile(conffile)) {
225 error("errors processing config file \"%s\"", conffile);
229 /* set up dgram port first thing */
232 if(dgram_bind(msg, &protocol_port) == -1)
233 error("could not bind result datagram port: %s", strerror(errno));
236 /* set both real and effective uid's to real uid, likewise for gid */
241 else error("must be run setuid root to communicate correctly");
245 "%s: pid %ld executable %s version %s, using port %d\n",
246 get_pname(), (long) getpid(),
247 main_argv[0], version(), protocol_port);
250 /* now, make sure we are a valid user */
252 if(getpwuid(getuid()) == NULL)
253 error("can't get login name for my uid %ld", (long)getuid());
255 signal(SIGPIPE, SIG_IGN);
257 interactive = isatty(0);
260 datestamp = construct_datestamp(NULL);
261 conf_dtimeout = getconf_int(CNF_DTIMEOUT);
263 service_ports_init();
264 proto_init(msg->socket, time(0), 16);
267 cmd = getcmd(&cmdargs);
288 cmdargs.argc++; /* true count of args */
291 if(a >= cmdargs.argc) {
292 error("error [dumper FILE-DUMP: not enough args: handle]");
294 handle = newstralloc(handle, cmdargs.argv[a++]);
296 if(a >= cmdargs.argc) {
297 error("error [dumper FILE-DUMP: not enough args: filename]");
299 filename = newstralloc(filename, cmdargs.argv[a++]);
301 if(a >= cmdargs.argc) {
302 error("error [dumper FILE-DUMP: not enough args: hostname]");
304 hostname = newstralloc(hostname, cmdargs.argv[a++]);
306 if(a >= cmdargs.argc) {
307 error("error [dumper FILE-DUMP: not enough args: features]");
309 am_release_feature_set(their_features);
310 their_features = am_string_to_feature(cmdargs.argv[a++]);
312 if(a >= cmdargs.argc) {
313 error("error [dumper FILE-DUMP: not enough args: diskname]");
315 diskname = newstralloc(diskname, cmdargs.argv[a++]);
317 if(a >= cmdargs.argc) {
318 error("error [dumper FILE-DUMP: not enough args: device]");
320 device = newstralloc(device, cmdargs.argv[a++]);
321 if(strcmp(device, "NODEVICE") == 0) amfree(device);
323 if(a >= cmdargs.argc) {
324 error("error [dumper FILE-DUMP: not enough args: level]");
326 level = atoi(cmdargs.argv[a++]);
328 if(a >= cmdargs.argc) {
329 error("error [dumper FILE-DUMP: not enough args: dumpdate]");
331 dumpdate = newstralloc(dumpdate, cmdargs.argv[a++]);
333 if(a >= cmdargs.argc) {
334 error("error [dumper FILE-DUMP: not enough args: chunksize]");
336 chunksize = am_floor(atoi(cmdargs.argv[a++]), DISK_BLOCK_KB);
338 if(a >= cmdargs.argc) {
339 error("error [dumper FILE-DUMP: not enough args: progname]");
341 progname = newstralloc(progname, cmdargs.argv[a++]);
343 if(a >= cmdargs.argc) {
344 error("error [dumper FILE-DUMP: not enough args: use]");
346 use = am_floor(atoi(cmdargs.argv[a++]), DISK_BLOCK_KB);
348 if(a >= cmdargs.argc) {
349 error("error [dumper FILE-DUMP: not enough args: options]");
351 options = newstralloc(options, cmdargs.argv[a++]);
353 if(a != cmdargs.argc) {
354 error("error [dumper FILE-DUMP: too many args: %d != %d]",
358 cont_filename[0] = '\0';
360 tmp_filename = newvstralloc(tmp_filename, filename, ".tmp", NULL);
361 pc = strrchr(tmp_filename, '/');
363 mkholdingdir(tmp_filename);
365 outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
367 int save_errno = errno;
368 q = squotef("[main holding file \"%s\": %s]",
369 tmp_filename, strerror(errno));
370 if(save_errno == ENOSPC) {
371 putresult(NO_ROOM, "%s %lu\n", handle, use);
372 putresult(TRYAGAIN, "%s %s\n", handle, q);
375 putresult(FAILED, "%s %s\n", handle, q);
382 check_options(options);
384 rc = startup_dump(hostname,
393 putresult(rc == 2? FAILED : TRYAGAIN, "%s %s\n",
396 log_add(L_FAIL, "%s %s %s %d [%s]", hostname, diskname,
397 datestamp, level, errstr);
400 /* do need to close if TRY-AGAIN, doesn't hurt otherwise */
413 split_size = (chunksize>use)?use:chunksize;
415 if(do_dump(mesgfd, datafd, indexfd, outfd)) {
422 if(abort_pending) putresult(ABORT_FINISHED, "%s\n", handle);
439 cmdargs.argc++; /* true count of args */
442 if(a >= cmdargs.argc) {
443 error("error [dumper PORT-DUMP: not enough args: handle]");
445 handle = newstralloc(handle, cmdargs.argv[a++]);
447 if(a >= cmdargs.argc) {
448 error("error [dumper PORT-DUMP: not enough args: port]");
450 taper_port = atoi(cmdargs.argv[a++]);
452 if(a >= cmdargs.argc) {
453 error("error [dumper PORT-DUMP: not enough args: hostname]");
455 hostname = newstralloc(hostname, cmdargs.argv[a++]);
457 if(a >= cmdargs.argc) {
458 error("error [dumper PORT-DUMP: not enough args: features]");
460 am_release_feature_set(their_features);
461 their_features = am_string_to_feature(cmdargs.argv[a++]);
463 if(a >= cmdargs.argc) {
464 error("error [dumper PORT-DUMP: not enough args: diskname]");
466 diskname = newstralloc(diskname, cmdargs.argv[a++]);
468 if(a >= cmdargs.argc) {
469 error("error [dumper PORT-DUMP: not enough args: device]");
471 device = newstralloc(device, cmdargs.argv[a++]);
472 if(strcmp(device,"NODEVICE") == 0) amfree(device);
474 if(a >= cmdargs.argc) {
475 error("error [dumper PORT-DUMP: not enough args: level]");
477 level = atoi(cmdargs.argv[a++]);
479 if(a >= cmdargs.argc) {
480 error("error [dumper PORT-DUMP: not enough args: dumpdate]");
482 dumpdate = newstralloc(dumpdate, cmdargs.argv[a++]);
484 if(a >= cmdargs.argc) {
485 error("error [dumper PORT-DUMP: not enough args: progname]");
487 progname = newstralloc(progname, cmdargs.argv[a++]);
489 if(a >= cmdargs.argc) {
490 error("error [dumper PORT-DUMP: not enough args: options]");
492 options = newstralloc(options, cmdargs.argv[a++]);
494 if(a != cmdargs.argc) {
495 error("error [dumper PORT-DUMP: too many args: %d != %d]",
499 filename = newstralloc(filename, "<taper program>");
500 cont_filename[0] = '\0';
502 /* connect outf to taper port */
504 outfd = stream_client("localhost", taper_port,
505 STREAM_BUFSIZE, -1, NULL);
507 q = squotef("[taper port open: %s]", strerror(errno));
508 putresult(FAILED, "%s %s\n", handle, q);
514 check_options(options);
516 rc = startup_dump(hostname,
525 putresult(rc == 2? FAILED : TRYAGAIN, "%s %s\n",
528 log_add(L_FAIL, "%s %s %s %d [%s]", hostname, diskname,
529 datestamp, level, errstr);
532 /* do need to close if TRY-AGAIN, doesn't hurt otherwise */
546 if(do_dump(mesgfd, datafd, indexfd, outfd)) {
553 if(abort_pending) putresult(ABORT_FINISHED, "%s\n", handle);
557 if(cmdargs.argc >= 1) {
558 q = squote(cmdargs.argv[1]);
559 } else if(cmdargs.argc >= 0) {
560 q = squote(cmdargs.argv[0]);
562 q = stralloc("(no input?)");
564 putresult(BAD_COMMAND, "%s\n", q);
567 while(wait(NULL) != -1);
568 } while(cmd != QUIT);
570 amfree(tmp_filename);
576 amfree(compress_suffix);
587 amfree(our_feature_string);
588 am_release_feature_set(our_features);
590 am_release_feature_set(their_features);
591 their_features = NULL;
593 malloc_size_2 = malloc_inuse(&malloc_hist_2);
595 if(malloc_size_1 != malloc_size_2) {
596 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
603 int write_dataptr(outf)
610 while(dataout < datain) {
611 if((w = write(outf, dataout, datain - dataout)) < 0) {
617 dumpbytes += written;
618 dumpsize += (dumpbytes / 1024);
619 filesize += (dumpbytes / 1024);
622 if(errno != ENOSPC) {
623 errstr = squotef("data write: %s", strerror(errno));
627 * NO-ROOM is informational only. Later, RQ_MORE_DISK will be
628 * issued to use another holding disk.
630 putresult(NO_ROOM, "%s %lu\n", handle, use+split_size-dumpsize);
631 use = 0; /* force RQ_MORE_DISK */
632 split_size = dumpsize;
634 if(dataout == datain) {
636 * We flushed the whole buffer so reset to use it all.
638 dataout = datain = databuf;
643 int update_dataptr(p_outfd, size)
646 * Updates the buffer pointer for the input data buffer. The buffer is
647 * written if it is full or we are at EOF.
650 int outfd = *p_outfd;
652 char *arg_filename = NULL;
653 char *new_filename = NULL;
654 char *tmp_filename = NULL;
656 char sequence[NUM_STR_SIZE];
658 struct cmdargs cmdargs;
660 filetype_t save_type;
667 while(rc == 0 && ((size == 0 && dataout < datain) || datain >= datalimit)) {
671 /* We open a new chunkfile if */
672 /* We have something to write (dataout < datain) */
673 /* We have a split_size defined (split_size > 0) */
674 /* The current file is already filled (dumpsize >= split_size) */
676 while(dataout < datain && split_size > 0 && dumpsize >= split_size) {
677 amfree(new_filename);
680 * Probably no more space on this disk. Request more.
682 putresult(RQ_MORE_DISK, "%s\n", handle);
683 cmd = getcmd(&cmdargs);
684 if(cmd == CONTINUE) {
692 cmdargs.argc++; /* true count of args */
695 if(a >= cmdargs.argc) {
696 error("error [dumper CONTINUE: not enough args: filename]");
698 arg_filename = newstralloc(arg_filename, cmdargs.argv[a++]);
700 if(a >= cmdargs.argc) {
701 error("error [dumper CONTINUE: not enough args: chunksize]");
703 chunksize = atoi(cmdargs.argv[a++]);
704 chunksize = am_floor(chunksize, DISK_BLOCK_KB);
706 if(a >= cmdargs.argc) {
707 error("error [dumper CONTINUE: not enough args: use]");
709 use = atoi(cmdargs.argv[a++]);
711 if(a != cmdargs.argc) {
712 error("error [dumper CONTINUE: too many args: %d != %d]",
716 if(strcmp(filename, arg_filename) == 0) {
718 * Same disk, so use what room is left up to the
719 * next chunk boundary or the amount we were given,
722 left_in_chunk = chunksize - filesize;
723 if(left_in_chunk > use) {
727 split_size += left_in_chunk;
728 use -= left_in_chunk;
730 if(left_in_chunk > 0) {
732 * We still have space in this chunk.
738 * Different disk, so use new file.
740 filename = newstralloc(filename, arg_filename);
742 } else if(cmd == ABORT) {
744 errstr = newstralloc(errstr, "ERROR");
748 if(cmdargs.argc >= 1) {
749 q = squote(cmdargs.argv[1]);
750 } else if(cmdargs.argc >= 0) {
751 q = squote(cmdargs.argv[0]);
753 q = stralloc("(no input?)");
755 error("error [bad command after RQ-MORE-DISK: \"%s\"]", q);
759 ap_snprintf(sequence, sizeof(sequence), "%d", filename_seq);
760 new_filename = newvstralloc(new_filename,
765 tmp_filename = newvstralloc(tmp_filename,
769 pc = strrchr(tmp_filename, '/');
771 mkholdingdir(tmp_filename);
773 new_outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
774 if(new_outfd == -1) {
775 int save_errno = errno;
777 errstr = squotef("creating chunk holding file \"%s\": %s",
779 strerror(save_errno));
780 if(save_errno == ENOSPC) {
781 putresult(NO_ROOM, "%s %lu\n",
783 use + split_size - dumpsize);
784 use = 0; /* force RQ_MORE_DISK */
785 split_size = dumpsize;
792 save_type = file.type;
793 file.type = F_CONT_DUMPFILE;
794 file.cont_filename[0] = '\0';
795 if(write_tapeheader(new_outfd, &file)) {
796 int save_errno = errno;
799 unlink(tmp_filename);
800 if(save_errno == ENOSPC) {
801 putresult(NO_ROOM, "%s %lu\n",
803 use + split_size - dumpsize);
804 use = 0; /* force RQ_MORE_DISK */
805 split_size = dumpsize;
808 errstr = squotef("write_tapeheader file \"%s\": %s",
809 tmp_filename, strerror(errno));
813 if(lseek(outfd, (off_t)0, SEEK_SET) != 0) {
814 errstr = squotef("cannot lseek: %s", strerror(errno));
816 unlink(tmp_filename);
820 strncpy(file.cont_filename, new_filename,
821 sizeof(file.cont_filename));
822 file.cont_filename[sizeof(file.cont_filename)-1] = '\0';
823 file.type = save_type;
824 if(write_tapeheader(outfd, &file)) {
825 errstr = squotef("write_tapeheader file linked to \"%s\": %s",
826 tmp_filename, strerror(errno));
828 unlink(tmp_filename);
832 file.type = F_CONT_DUMPFILE;
833 strncpy(cont_filename, new_filename, sizeof(cont_filename));
834 cont_filename[sizeof(cont_filename)-1] = '\0';
837 *p_outfd = outfd = new_outfd;
840 dumpsize += DISK_BLOCK_KB;
841 filesize = DISK_BLOCK_KB;
842 split_size += (chunksize>use)?use:chunksize;
843 use = (chunksize>use)?0:use-chunksize;
847 rc = write_dataptr(outfd);
852 amfree(new_filename);
853 amfree(tmp_filename);
854 amfree(arg_filename);
859 static char *msgbuf = NULL;
860 int got_info_endline;
865 static void process_dumpeof()
867 /* process any partial line in msgbuf? !!! */
869 fprintf(errf,"? %s: error [partial line in msgbuf: %ld bytes]\n",
870 get_pname(), (long) strlen(msgbuf));
871 fprintf(errf,"? %s: error [partial line in msgbuf: \"%s\"]\n",
872 get_pname(), msgbuf);
874 if(!got_sizeline && dump_result < 2) {
875 /* make a note if there isn't already a failure */
876 fprintf(errf,"? %s: strange [missing size line from sendbackup]\n",
878 dump_result = max(dump_result, 2);
881 if(!got_endline && dump_result < 2) {
882 fprintf(errf,"? %s: strange [missing end line from sendbackup]\n",
884 dump_result = max(dump_result, 2);
888 /* Parse an information line from the client.
889 ** We ignore unknown parameters and only remember the last
890 ** of any duplicates.
892 static void parse_info_line(str)
895 if(strcmp(str, "end") == 0) {
896 got_info_endline = 1;
901 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
902 backup_name = newstralloc(backup_name, str + sizeof(sc)-1);
907 #define sc "RECOVER_CMD="
908 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
909 recover_cmd = newstralloc(recover_cmd, str + sizeof(sc)-1);
914 #define sc "COMPRESS_SUFFIX="
915 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
916 compress_suffix = newstralloc(compress_suffix, str + sizeof(sc)-1);
922 static void process_dumpline(str)
933 /* normal backup output line */
936 /* sendbackup detected something strange */
937 dump_result = max(dump_result, 1);
940 /* a sendbackup line, just check them all since there are only 5 */
941 #define sc "sendbackup: start"
942 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
946 #define sc "sendbackup: size"
947 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
950 skip_whitespace(s, ch);
952 origsize = (long)atof(str + sizeof(sc)-1);
958 #define sc "sendbackup: end"
959 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
964 #define sc "sendbackup: warning"
965 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
966 dump_result = max(dump_result, 1);
970 #define sc "sendbackup: error"
971 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
976 dump_result = max(dump_result, 2);
977 skip_whitespace(s, ch);
978 if(ch == '\0' || ch != '[') {
979 errstr = newvstralloc(errstr,
980 "bad remote error: ", str,
985 while(ch && ch != ']') ch = *s++;
987 errstr = newstralloc(errstr, fp);
992 #define sc "sendbackup: info"
993 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
996 skip_whitespace(s, ch);
997 parse_info_line(s - 1);
1001 /* else we fall through to bad line */
1003 fprintf(errf, "??%s", str);
1004 dump_result = max(dump_result, 1);
1007 fprintf(errf, "%s\n", str);
1010 static void add_msg_data(str, len)
1018 if((nl = strchr(str, '\n')) != NULL) {
1022 t = stralloc2(msgbuf, str);
1025 } else if(nl == NULL) {
1026 msgbuf = stralloc(str);
1030 if(nl == NULL) break;
1031 process_dumpline(msgbuf);
1032 if(msgbuf != str) free(msgbuf);
1034 len -= nl + 1 - str;
1040 static void log_msgout(typ)
1046 (void) fseek(errf, 0L, SEEK_SET);
1047 for(; (line = agets(errf)) != NULL; free(line)) {
1048 log_add(typ, "%s", line);
1055 void make_tapeheader(file, type)
1061 strncpy(file->datestamp , datestamp , sizeof(file->datestamp)-1);
1062 file->datestamp[sizeof(file->datestamp)-1] = '\0';
1063 strncpy(file->name , hostname , sizeof(file->name)-1);
1064 file->name[sizeof(file->name)-1] = '\0';
1065 strncpy(file->disk , diskname , sizeof(file->disk)-1);
1066 file->disk[sizeof(file->disk)-1] = '\0';
1067 file->dumplevel = level;
1068 strncpy(file->program , backup_name, sizeof(file->program)-1);
1069 file->program[sizeof(file->program)-1] = '\0';
1070 strncpy(file->recover_cmd, recover_cmd, sizeof(file->recover_cmd)-1);
1071 file->recover_cmd[sizeof(file->recover_cmd)-1] = '\0';
1072 file->blocksize = DISK_BLOCK_BYTES;
1076 ap_snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd),
1077 " %s %s |", UNCOMPRESS_PATH,
1078 #ifdef UNCOMPRESS_OPT
1084 strncpy(file->comp_suffix, COMPRESS_SUFFIX,sizeof(file->comp_suffix)-1);
1085 file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1088 file->uncompress_cmd[0] = '\0';
1089 file->compressed=compress_suffix!=NULL;
1090 if(compress_suffix) {
1091 strncpy(file->comp_suffix, compress_suffix,
1092 sizeof(file->comp_suffix)-1);
1093 file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1095 strncpy(file->comp_suffix, "N", sizeof(file->comp_suffix)-1);
1096 file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1099 strncpy(file->cont_filename, cont_filename, sizeof(file->cont_filename)-1);
1100 file->cont_filename[sizeof(file->cont_filename)-1] = '\0';
1103 /* Send an Amanda dump header to the output file.
1104 * returns true if an error occured, false on success
1107 int write_tapeheader(outfd, file)
1111 char buffer[DISK_BLOCK_BYTES];
1114 build_header(buffer, file, sizeof(buffer));
1116 written = fullwrite(outfd, buffer, sizeof(buffer));
1117 if(written == sizeof(buffer)) return 0;
1118 if(written < 0) return written;
1124 int do_dump(mesgfd, datafd, indexfd, outfd)
1125 int mesgfd, datafd, indexfd, outfd;
1127 int maxfd, nfound, size1, size2, eof1, eof2;
1129 fd_set readset, selectset;
1130 struct timeval timeout;
1132 int header_done; /* flag - header has been written */
1133 char *indexfile_tmp = NULL;
1134 char *indexfile_real = NULL;
1135 char level_str[NUM_STR_SIZE];
1136 char kb_str[NUM_STR_SIZE];
1137 char kps_str[NUM_STR_SIZE];
1138 char orig_kb_str[NUM_STR_SIZE];
1142 double dumptime; /* Time dump took in secs */
1143 int compresspid = -1, indexpid = -1, killerr;
1144 char *errfname = NULL;
1146 #ifndef DUMPER_SOCKET_BUFFERING
1147 #define DUMPER_SOCKET_BUFFERING 0
1150 #if !defined(SO_RCVBUF) || !defined(SO_RCVLOWAT)
1151 #undef DUMPER_SOCKET_BUFFERING
1152 #define DUMPER_SOCKET_BUFFERING 0
1155 #if DUMPER_SOCKET_BUFFERING
1156 int lowat = NETWORK_BLOCK_BYTES;
1158 int sizeof_recbuf = sizeof(recbuf);
1160 int lowwatset_count = 0;
1165 datain = dataout = databuf;
1166 datalimit = databuf + sizeof(databuf);
1167 dumpsize = dumpbytes = origsize = filesize = dump_result = 0;
1168 nb_header_block = 0;
1169 got_info_endline = got_sizeline = got_endline = 0;
1171 amfree(backup_name);
1172 amfree(recover_cmd);
1173 amfree(compress_suffix);
1175 ap_snprintf(level_str, sizeof(level_str), "%d", level);
1176 fn = sanitise_filename(diskname);
1177 errfname = newvstralloc(errfname,
1185 if((errf = fopen(errfname, "w+")) == NULL) {
1186 errstr = newvstralloc(errstr,
1187 "errfile open \"", errfname, "\": ",
1194 unlink(errfname); /* so it goes away on close */
1197 /* insert pipe in the *READ* side, if server-side compression is desired */
1203 pipe(outpipe); /* outpipe[0] is pipe's stdin, outpipe[1] is stdout. */
1204 datafd = outpipe[0];
1205 if(datafd < 0 || datafd >= FD_SETSIZE) {
1208 errstr = newstralloc(errstr, "descriptor out of range");
1213 switch(compresspid=fork()) {
1215 errstr = newstralloc2(errstr, "couldn't fork: ", strerror(errno));
1224 /* child acts on stdin/stdout */
1225 if (dup2(outpipe[1],1) == -1)
1226 fprintf(stderr, "err dup2 out: %s\n", strerror(errno));
1227 if (dup2(tmpfd, 0) == -1)
1228 fprintf(stderr, "err dup2 in: %s\n", strerror(errno));
1229 for(tmpfd = 3; tmpfd <= FD_SETSIZE; ++tmpfd) {
1232 /* now spawn gzip -1 to take care of the rest */
1233 execlp(COMPRESS_PATH, COMPRESS_PATH,
1234 (srvcompress == srvcomp_best ? COMPRESS_BEST_OPT
1235 : COMPRESS_FAST_OPT),
1237 error("error: couldn't exec %s.\n", COMPRESS_PATH);
1239 /* Now the pipe has been inserted. */
1243 if (indexfd != -1) {
1246 indexfile_real = getindexfname(hostname, diskname, datestamp, level),
1247 indexfile_tmp = stralloc2(indexfile_real, ".tmp");
1249 if (mkpdir(indexfile_tmp, 02755, (uid_t)-1, (gid_t)-1) == -1) {
1250 errstr = newvstralloc(errstr,
1256 amfree(indexfile_real);
1257 amfree(indexfile_tmp);
1262 switch(indexpid=fork()) {
1264 errstr = newstralloc2(errstr, "couldn't fork: ", strerror(errno));
1269 indexfd = -1; /* redundant */
1272 if (dup2(indexfd, 0) == -1) {
1273 error("err dup2 in: %s", strerror(errno));
1275 indexfd = open(indexfile_tmp, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1277 error("err open %s: %s", indexfile_tmp, strerror(errno));
1278 if (dup2(indexfd,1) == -1)
1279 error("err dup2 out: %s", strerror(errno));
1280 for(tmpfd = 3; tmpfd <= FD_SETSIZE; ++tmpfd) {
1283 execlp(COMPRESS_PATH, COMPRESS_PATH, COMPRESS_BEST_OPT, (char *)0);
1284 error("error: couldn't exec %s.", COMPRESS_PATH);
1288 NAUGHTY_BITS_INITIALIZE;
1290 maxfd = max(mesgfd, datafd) + 1;
1295 /* Just process messages for now. Once we have done the header
1296 ** we will start processing data too.
1298 FD_SET(mesgfd, &readset);
1300 if(datafd == -1) eof1 = 1; /* fake eof on data */
1302 #if DUMPER_SOCKET_BUFFERING
1304 #ifndef EST_PACKET_SIZE
1305 #define EST_PACKET_SIZE 512
1307 #ifndef EST_MIN_WINDOW
1308 #define EST_MIN_WINDOW EST_PACKET_SIZE*4 /* leave room for 2k in transit */
1312 recbuf = STREAM_BUFSIZE;
1313 if (setsockopt(datafd, SOL_SOCKET, SO_RCVBUF,
1314 (void *) &recbuf, sizeof_recbuf)) {
1315 const int errornumber = errno;
1316 fprintf(stderr, "%s: pid %ld setsockopt(SO_RCVBUF): %s\n",
1317 get_pname(), (long) getpid(), strerror(errornumber));
1319 if (getsockopt(datafd, SOL_SOCKET, SO_RCVBUF,
1320 (void *) &recbuf, (void *)&sizeof_recbuf)) {
1321 const int errornumber = errno;
1322 fprintf(stderr, "%s: pid %ld getsockopt(SO_RCVBUF): %s\n",
1323 get_pname(), (long) getpid(), strerror(errornumber));
1327 /* leave at least EST_MIN_WINDOW between lowwat and recbuf */
1328 if (recbuf-lowat < EST_MIN_WINDOW)
1329 lowat = recbuf-EST_MIN_WINDOW;
1331 /* if lowwat < ~512, don't bother */
1332 if (lowat < EST_PACKET_SIZE)
1334 fprintf(stderr, "%s: pid %ld receive size is %d, low water is %d\n",
1335 get_pname(), (long) getpid(), recbuf, lowat);
1339 while(!(eof1 && eof2)) {
1341 #if DUMPER_SOCKET_BUFFERING
1342 /* Set socket buffering */
1343 if (recbuf>0 && !lowwatset) {
1344 if (setsockopt(datafd, SOL_SOCKET, SO_RCVLOWAT,
1345 (void *) &lowat, sizeof(lowat))) {
1346 const int errornumber = errno;
1348 "%s: pid %ld setsockopt(SO_RCVLOWAT): %s\n",
1349 get_pname(), (long) getpid(), strerror(errornumber));
1356 timeout.tv_sec = conf_dtimeout;
1357 timeout.tv_usec = 0;
1358 memcpy(&selectset, &readset, sizeof(fd_set));
1360 nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
1362 /* check for errors or timeout */
1364 #if DUMPER_SOCKET_BUFFERING
1365 if (nfound==0 && lowwatset) {
1367 /* Disable socket buffering and ... */
1368 if (setsockopt(datafd, SOL_SOCKET, SO_RCVLOWAT,
1369 (void *) &zero, sizeof(zero))) {
1370 const int errornumber = errno;
1372 "%s: pid %ld setsockopt(SO_RCVLOWAT): %s\n",
1373 get_pname(), (long) getpid(), strerror(errornumber));
1377 /* ... try once more */
1378 timeout.tv_sec = conf_dtimeout;
1379 timeout.tv_usec = 0;
1380 memcpy(&selectset, &readset, sizeof(fd_set));
1381 nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
1386 errstr = newstralloc(errstr, "data timeout");
1391 errstr = newstralloc2(errstr, "select: ", strerror(errno));
1396 /* read/write any data */
1398 if(datafd >= 0 && FD_ISSET(datafd, &selectset)) {
1399 size1 = read(datafd, datain, datalimit - datain);
1401 errstr = newstralloc2(errstr, "data read: ", strerror(errno));
1405 if(update_dataptr(&outfd, size1)) {
1411 FD_CLR(datafd, &readset);
1416 if(mesgfd >= 0 && FD_ISSET(mesgfd, &selectset)) {
1417 size2 = read(mesgfd, mesgbuf, sizeof(mesgbuf)-1);
1420 errstr = newstralloc2(errstr, "mesg read: ", strerror(errno));
1426 FD_CLR(mesgfd, &readset);
1430 mesgbuf[size2] = '\0';
1431 add_msg_data(mesgbuf, size2);
1434 if (got_info_endline && !header_done) { /* time to do the header */
1435 make_tapeheader(&file, F_DUMPFILE);
1436 if (write_tapeheader(outfd, &file)) {
1437 int save_errno = errno;
1438 errstr = newstralloc2(errstr, "write_tapeheader: ",
1440 if(save_errno == ENOSPC) {
1441 putresult(NO_ROOM, "%s %lu\n", handle,
1442 use+split_size-dumpsize);
1443 use = 0; /* force RQ_MORE_DISK */
1444 split_size = dumpsize;
1452 dumpsize += DISK_BLOCK_KB;
1453 filesize += DISK_BLOCK_KB;
1456 strncat(cont_filename,filename,sizeof(cont_filename));
1457 cont_filename[sizeof(cont_filename)-1] = '\0';
1460 FD_SET(datafd, &readset); /* now we can read the data */
1465 #if DUMPER_SOCKET_BUFFERING
1466 if(lowwatset_count > 1) {
1467 fprintf(stderr, "%s: pid %ld low water set %d times\n",
1468 get_pname(), (long) getpid(), lowwatset_count);
1472 if(dump_result > 1) {
1477 runtime = stopclock();
1478 dumptime = runtime.r.tv_sec + runtime.r.tv_usec/1000000.0;
1480 dumpsize -= (nb_header_block * DISK_BLOCK_KB);/* don't count the header */
1481 if (dumpsize < 0) dumpsize = 0; /* XXX - maybe this should be fatal? */
1483 ap_snprintf(kb_str, sizeof(kb_str), "%ld", dumpsize);
1484 ap_snprintf(kps_str, sizeof(kps_str),
1486 dumptime ? dumpsize / dumptime : 0.0);
1487 ap_snprintf(orig_kb_str, sizeof(orig_kb_str), "%ld", origsize);
1488 errstr = newvstralloc(errstr,
1489 "sec ", walltime_str(runtime),
1491 " ", "kps ", kps_str,
1492 " ", "orig-kb ", orig_kb_str,
1494 q = squotef("[%s]", errstr);
1495 putresult(DONE, "%s %ld %ld %ld %s\n", handle, origsize, dumpsize,
1496 (long)(dumptime+0.5), q);
1499 switch(dump_result) {
1501 log_add(L_SUCCESS, "%s %s %s %d [%s]", hostname, diskname, datestamp, level, errstr);
1506 log_start_multiline();
1507 log_add(L_STRANGE, "%s %s %d [%s]", hostname, diskname, level, errstr);
1508 log_msgout(L_STRANGE);
1509 log_end_multiline();
1514 if(errf) afclose(errf);
1516 if (indexfile_tmp) {
1517 waitpid(indexpid,NULL,0);
1518 if(rename(indexfile_tmp, indexfile_real) != 0) {
1519 log_add(L_WARNING, "could not rename \"%s\" to \"%s\": %s",
1520 indexfile_tmp, indexfile_real, strerror(errno));
1522 amfree(indexfile_tmp);
1523 amfree(indexfile_real);
1530 #if DUMPER_SOCKET_BUFFERING
1531 if(lowwatset_count > 1) {
1532 fprintf(stderr, "%s: pid %ld low water set %d times\n",
1533 get_pname(), (long) getpid(), lowwatset_count);
1537 if(!abort_pending) {
1538 q = squotef("[%s]", errstr);
1540 putresult(FAILED, "%s %s\n", handle, q);
1542 putresult(TRYAGAIN, "%s %s\n", handle, q);
1546 /* kill all child process */
1547 if(compresspid != -1) {
1548 killerr = kill(compresspid,SIGTERM);
1550 fprintf(stderr,"%s: kill compress command\n",get_pname());
1552 else if ( killerr == -1 ) {
1554 fprintf(stderr,"%s: can't kill compress command: %s\n",
1555 get_pname(), strerror(errno));
1559 if(indexpid != -1) {
1560 killerr = kill(indexpid,SIGTERM);
1562 fprintf(stderr,"%s: kill index command\n",get_pname());
1564 else if ( killerr == -1 ) {
1566 fprintf(stderr,"%s: can't kill index command: %s\n",
1567 get_pname(),strerror(errno));
1571 if(!abort_pending) {
1572 log_start_multiline();
1573 log_add(L_FAIL, "%s %s %s %d [%s]", hostname, diskname,
1574 datestamp, level, errstr);
1578 log_end_multiline();
1581 if(errf) afclose(errf);
1583 if (indexfile_tmp) {
1584 unlink(indexfile_tmp);
1585 amfree(indexfile_tmp);
1586 amfree(indexfile_real);
1592 /* -------------------- */
1594 char *hostname, *disk;
1597 void sendbackup_response(p, pkt)
1603 int index_port = -1;
1611 am_release_feature_set(their_features);
1612 their_features = NULL;
1614 if(p->state == S_FAILED) {
1616 if(p->prevstate == S_REPWAIT) {
1617 errstr = newstralloc(errstr, "[reply timeout]");
1620 errstr = newstralloc(errstr, "[request timeout]");
1628 fprintf(stderr, "got %sresponse:\n----\n%s----\n\n",
1629 (p->state == S_FAILED) ? "NAK " : "", pkt->body);
1632 #ifdef KRB4_SECURITY
1633 if(krb4_auth && !check_mutual_authenticator(&cred.session, pkt, p)) {
1634 errstr = newstralloc(errstr, "mutual-authentication failed");
1645 if (s[-2] == '\n') {
1649 #define sc "OPTIONS "
1650 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1653 #define sc "features="
1654 t = strstr(line, sc);
1655 if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
1658 am_release_feature_set(their_features);
1659 if((their_features = am_string_to_feature(t)) == NULL) {
1660 errstr = newvstralloc(errstr,
1661 "bad features value: ",
1671 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1672 t = line + sizeof(sc)-1;
1677 skip_whitespace(t, tch);
1678 errstr = newvstralloc(errstr,
1679 (p->state == S_FAILED) ? "nak error: " : "",
1682 response_error = ((p->state == S_FAILED) ? 1 : 2);
1686 #define sc "CONNECT "
1687 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1691 t = strstr(line, sc);
1692 if(t != NULL && isspace((int)t[-1])) {
1695 data_port = atoi(t);
1699 t = strstr(line, sc);
1700 if(t != NULL && isspace((int)t[-1])) {
1703 mesg_port = atoi(t);
1707 t = strstr(line, sc);
1708 if(t != NULL && isspace((int)t[-1])) {
1711 index_port = atoi(t);
1716 errstr = newvstralloc(errstr,
1717 "unknown response: ",
1724 if (data_port == -1 || mesg_port == -1) {
1725 errstr = newvstralloc(errstr, "bad CONNECT response", NULL);
1730 datafd = stream_client(hostname, data_port, -1, -1, NULL);
1732 errstr = newvstralloc(errstr,
1733 "could not connect to data port: ",
1739 mesgfd = stream_client(hostname, mesg_port, -1, -1, NULL);
1741 errstr = newvstralloc(errstr,
1742 "could not connect to mesg port: ",
1746 datafd = -1; /* redundant */
1751 if (index_port != -1) {
1752 indexfd = stream_client(hostname, index_port, -1, -1, NULL);
1753 if (indexfd == -1) {
1754 errstr = newvstralloc(errstr,
1755 "could not connect to index port: ",
1760 datafd = mesgfd = -1; /* redundant */
1766 /* everything worked */
1768 #ifdef KRB4_SECURITY
1769 if(krb4_auth && kerberos_handshake(datafd, cred.session) == 0) {
1770 errstr = newstralloc(errstr,
1771 "mutual authentication in data stream failed");
1779 if(krb4_auth && kerberos_handshake(mesgfd, cred.session) == 0) {
1780 errstr = newstralloc(errstr,
1781 "mutual authentication in mesg stream failed");
1794 int startup_dump(hostname, disk, device, level, dumpdate, progname, options)
1795 char *hostname, *disk, *device, *dumpdate, *progname, *options;
1798 char level_string[NUM_STR_SIZE];
1802 int has_features = am_has_feature(their_features, fe_req_options_features);
1803 int has_hostname = am_has_feature(their_features, fe_req_options_hostname);
1804 int has_device = am_has_feature(their_features, fe_sendbackup_req_device);
1806 ap_snprintf(level_string, sizeof(level_string), "%d", level);
1807 req = vstralloc("SERVICE sendbackup\n",
1809 has_features ? "features=" : "",
1810 has_features ? our_feature_string : "",
1811 has_features ? ";" : "",
1812 has_hostname ? "hostname=" : "",
1813 has_hostname ? hostname : "",
1814 has_hostname ? ";" : "",
1818 " ", device && has_device ? device : "",
1821 " ", "OPTIONS ", options,
1825 datafd = mesgfd = indexfd = -1;
1827 #ifdef KRB4_SECURITY
1829 char rc_str[NUM_STR_SIZE];
1831 rc = make_krb_request(hostname, kamanda_port, req, NULL,
1832 STARTUP_TIMEOUT, sendbackup_response);
1834 char inst[256], realm[256];
1835 #define HOSTNAME_INSTANCE inst
1837 * This repeats a lot of work with make_krb_request, but it's
1838 * ultimately the kerberos library's fault: krb_mk_req calls
1839 * krb_get_cred, but doesn't make the session key available!
1840 * XXX: But admittedly, we could restructure a bit here and
1841 * at least eliminate the duplicate gethostbyname().
1843 if(host2krbname(hostname, inst, realm) == 0)
1846 rc = krb_get_cred(CLIENT_HOST_PRINCIPLE, CLIENT_HOST_INSTANCE,
1849 ap_snprintf(rc_str, sizeof(rc_str), "%d", rc);
1850 errstr = newvstralloc(errstr,
1852 ": krb4 error (krb_get_cred) ",
1854 ": ", krb_err_txt[rc],
1861 ap_snprintf(rc_str, sizeof(rc_str), "%d", rc);
1862 errstr = newvstralloc(errstr,
1864 ": krb4 error (make_krb_req) ",
1866 ": ", krb_err_txt[rc],
1873 rc = make_request(hostname, amanda_port, req, NULL,
1874 STARTUP_TIMEOUT, sendbackup_response);
1876 req = NULL; /* do not own this any more */
1879 errstr = newvstralloc(errstr,
1880 "[could not resolve name \"", hostname, "\"]",
1885 return response_error;