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.4 2005/09/20 21:31:52 jrjackson 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;
182 char *tmp_filename = NULL, *pc;
189 malloc_size_1 = malloc_inuse(&malloc_hist_1);
191 erroutput_type = (ERR_AMANDALOG|ERR_INTERACTIVE);
192 set_logerror(logerror);
195 config_name = stralloc(main_argv[1]);
196 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
198 char my_cwd[STR_SIZE];
200 if (getcwd(my_cwd, sizeof(my_cwd)) == NULL) {
201 error("cannot determine current working directory");
203 config_dir = stralloc2(my_cwd, "/");
204 if ((config_name = strrchr(my_cwd, '/')) != NULL) {
205 config_name = stralloc(config_name + 1);
211 our_features = am_init_feature_set();
212 our_feature_string = am_feature_to_string(our_features);
214 conffile = stralloc2(config_dir, CONFFILE_NAME);
215 if(read_conffile(conffile)) {
216 error("errors processing config file \"%s\"", conffile);
220 /* set up dgram port first thing */
223 if(dgram_bind(msg, &protocol_port) == -1)
224 error("could not bind result datagram port: %s", strerror(errno));
227 /* set both real and effective uid's to real uid, likewise for gid */
232 else error("must be run setuid root to communicate correctly");
236 "%s: pid %ld executable %s version %s, using port %d\n",
237 get_pname(), (long) getpid(),
238 main_argv[0], version(), protocol_port);
241 /* now, make sure we are a valid user */
243 if(getpwuid(getuid()) == NULL)
244 error("can't get login name for my uid %ld", (long)getuid());
246 signal(SIGPIPE, SIG_IGN);
248 interactive = isatty(0);
251 datestamp = construct_datestamp(NULL);
252 conf_dtimeout = getconf_int(CNF_DTIMEOUT);
254 service_ports_init();
255 proto_init(msg->socket, time(0), 16);
258 cmd = getcmd(&cmdargs);
279 cmdargs.argc++; /* true count of args */
282 if(a >= cmdargs.argc) {
283 error("error [dumper FILE-DUMP: not enough args: handle]");
285 handle = newstralloc(handle, cmdargs.argv[a++]);
287 if(a >= cmdargs.argc) {
288 error("error [dumper FILE-DUMP: not enough args: filename]");
290 filename = newstralloc(filename, cmdargs.argv[a++]);
292 if(a >= cmdargs.argc) {
293 error("error [dumper FILE-DUMP: not enough args: hostname]");
295 hostname = newstralloc(hostname, cmdargs.argv[a++]);
297 if(a >= cmdargs.argc) {
298 error("error [dumper FILE-DUMP: not enough args: features]");
300 am_release_feature_set(their_features);
301 their_features = am_string_to_feature(cmdargs.argv[a++]);
303 if(a >= cmdargs.argc) {
304 error("error [dumper FILE-DUMP: not enough args: diskname]");
306 diskname = newstralloc(diskname, cmdargs.argv[a++]);
308 if(a >= cmdargs.argc) {
309 error("error [dumper FILE-DUMP: not enough args: device]");
311 device = newstralloc(device, cmdargs.argv[a++]);
312 if(strcmp(device, "NODEVICE") == 0) amfree(device);
314 if(a >= cmdargs.argc) {
315 error("error [dumper FILE-DUMP: not enough args: level]");
317 level = atoi(cmdargs.argv[a++]);
319 if(a >= cmdargs.argc) {
320 error("error [dumper FILE-DUMP: not enough args: dumpdate]");
322 dumpdate = newstralloc(dumpdate, cmdargs.argv[a++]);
324 if(a >= cmdargs.argc) {
325 error("error [dumper FILE-DUMP: not enough args: chunksize]");
327 chunksize = am_floor(atoi(cmdargs.argv[a++]), DISK_BLOCK_KB);
329 if(a >= cmdargs.argc) {
330 error("error [dumper FILE-DUMP: not enough args: progname]");
332 progname = newstralloc(progname, cmdargs.argv[a++]);
334 if(a >= cmdargs.argc) {
335 error("error [dumper FILE-DUMP: not enough args: use]");
337 use = am_floor(atoi(cmdargs.argv[a++]), DISK_BLOCK_KB);
339 if(a >= cmdargs.argc) {
340 error("error [dumper FILE-DUMP: not enough args: options]");
342 options = newstralloc(options, cmdargs.argv[a++]);
344 if(a != cmdargs.argc) {
345 error("error [dumper FILE-DUMP: too many args: %d != %d]",
349 cont_filename[0] = '\0';
351 tmp_filename = newvstralloc(tmp_filename, filename, ".tmp", NULL);
352 pc = strrchr(tmp_filename, '/');
354 mkholdingdir(tmp_filename);
356 outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
358 int save_errno = errno;
359 q = squotef("[main holding file \"%s\": %s]",
360 tmp_filename, strerror(errno));
361 if(save_errno == ENOSPC) {
362 putresult(NO_ROOM, "%s %lu\n", handle, use);
363 putresult(TRYAGAIN, "%s %s\n", handle, q);
366 putresult(FAILED, "%s %s\n", handle, q);
373 check_options(options);
375 rc = startup_dump(hostname,
384 putresult(rc == 2? FAILED : TRYAGAIN, "%s %s\n",
387 log_add(L_FAIL, "%s %s %s %d [%s]", hostname, diskname,
388 datestamp, level, errstr);
391 /* do need to close if TRY-AGAIN, doesn't hurt otherwise */
404 split_size = (chunksize>use)?use:chunksize;
406 if(do_dump(mesgfd, datafd, indexfd, outfd)) {
413 if(abort_pending) putresult(ABORT_FINISHED, "%s\n", handle);
430 cmdargs.argc++; /* true count of args */
433 if(a >= cmdargs.argc) {
434 error("error [dumper PORT-DUMP: not enough args: handle]");
436 handle = newstralloc(handle, cmdargs.argv[a++]);
438 if(a >= cmdargs.argc) {
439 error("error [dumper PORT-DUMP: not enough args: port]");
441 taper_port = atoi(cmdargs.argv[a++]);
443 if(a >= cmdargs.argc) {
444 error("error [dumper PORT-DUMP: not enough args: hostname]");
446 hostname = newstralloc(hostname, cmdargs.argv[a++]);
448 if(a >= cmdargs.argc) {
449 error("error [dumper PORT-DUMP: not enough args: features]");
451 am_release_feature_set(their_features);
452 their_features = am_string_to_feature(cmdargs.argv[a++]);
454 if(a >= cmdargs.argc) {
455 error("error [dumper PORT-DUMP: not enough args: diskname]");
457 diskname = newstralloc(diskname, cmdargs.argv[a++]);
459 if(a >= cmdargs.argc) {
460 error("error [dumper PORT-DUMP: not enough args: device]");
462 device = newstralloc(device, cmdargs.argv[a++]);
463 if(strcmp(device,"NODEVICE") == 0) amfree(device);
465 if(a >= cmdargs.argc) {
466 error("error [dumper PORT-DUMP: not enough args: level]");
468 level = atoi(cmdargs.argv[a++]);
470 if(a >= cmdargs.argc) {
471 error("error [dumper PORT-DUMP: not enough args: dumpdate]");
473 dumpdate = newstralloc(dumpdate, cmdargs.argv[a++]);
475 if(a >= cmdargs.argc) {
476 error("error [dumper PORT-DUMP: not enough args: progname]");
478 progname = newstralloc(progname, cmdargs.argv[a++]);
480 if(a >= cmdargs.argc) {
481 error("error [dumper PORT-DUMP: not enough args: options]");
483 options = newstralloc(options, cmdargs.argv[a++]);
485 if(a != cmdargs.argc) {
486 error("error [dumper PORT-DUMP: too many args: %d != %d]",
490 filename = newstralloc(filename, "<taper program>");
491 cont_filename[0] = '\0';
493 /* connect outf to taper port */
495 outfd = stream_client("localhost", taper_port,
496 STREAM_BUFSIZE, -1, NULL);
498 q = squotef("[taper port open: %s]", strerror(errno));
499 putresult(FAILED, "%s %s\n", handle, q);
505 check_options(options);
507 rc = startup_dump(hostname,
516 putresult(rc == 2? FAILED : TRYAGAIN, "%s %s\n",
519 log_add(L_FAIL, "%s %s %s %d [%s]", hostname, diskname,
520 datestamp, level, errstr);
523 /* do need to close if TRY-AGAIN, doesn't hurt otherwise */
537 if(do_dump(mesgfd, datafd, indexfd, outfd)) {
544 if(abort_pending) putresult(ABORT_FINISHED, "%s\n", handle);
548 if(cmdargs.argc >= 1) {
549 q = squote(cmdargs.argv[1]);
550 } else if(cmdargs.argc >= 0) {
551 q = squote(cmdargs.argv[0]);
553 q = stralloc("(no input?)");
555 putresult(BAD_COMMAND, "%s\n", q);
558 while(wait(NULL) != -1);
559 } while(cmd != QUIT);
561 amfree(tmp_filename);
567 amfree(compress_suffix);
578 amfree(our_feature_string);
579 am_release_feature_set(our_features);
581 am_release_feature_set(their_features);
582 their_features = NULL;
584 malloc_size_2 = malloc_inuse(&malloc_hist_2);
586 if(malloc_size_1 != malloc_size_2) {
587 malloc_list(fileno(stderr), malloc_hist_1, malloc_hist_2);
594 int write_dataptr(outf)
601 while(dataout < datain) {
602 if((w = write(outf, dataout, datain - dataout)) < 0) {
608 dumpbytes += written;
609 dumpsize += (dumpbytes / 1024);
610 filesize += (dumpbytes / 1024);
613 if(errno != ENOSPC) {
614 errstr = squotef("data write: %s", strerror(errno));
618 * NO-ROOM is informational only. Later, RQ_MORE_DISK will be
619 * issued to use another holding disk.
621 putresult(NO_ROOM, "%s %lu\n", handle, use+split_size-dumpsize);
622 use = 0; /* force RQ_MORE_DISK */
623 split_size = dumpsize;
625 if(dataout == datain) {
627 * We flushed the whole buffer so reset to use it all.
629 dataout = datain = databuf;
634 int update_dataptr(p_outfd, size)
637 * Updates the buffer pointer for the input data buffer. The buffer is
638 * written if it is full or we are at EOF.
641 int outfd = *p_outfd;
643 char *arg_filename = NULL;
644 char *new_filename = NULL;
645 char *tmp_filename = NULL;
647 char sequence[NUM_STR_SIZE];
649 struct cmdargs cmdargs;
651 filetype_t save_type;
658 while(rc == 0 && ((size == 0 && dataout < datain) || datain >= datalimit)) {
662 /* We open a new chunkfile if */
663 /* We have something to write (dataout < datain) */
664 /* We have a split_size defined (split_size > 0) */
665 /* The current file is already filled (dumpsize >= split_size) */
667 while(dataout < datain && split_size > 0 && dumpsize >= split_size) {
668 amfree(new_filename);
671 * Probably no more space on this disk. Request more.
673 putresult(RQ_MORE_DISK, "%s\n", handle);
674 cmd = getcmd(&cmdargs);
675 if(cmd == CONTINUE) {
683 cmdargs.argc++; /* true count of args */
686 if(a >= cmdargs.argc) {
687 error("error [dumper CONTINUE: not enough args: filename]");
689 arg_filename = newstralloc(arg_filename, cmdargs.argv[a++]);
691 if(a >= cmdargs.argc) {
692 error("error [dumper CONTINUE: not enough args: chunksize]");
694 chunksize = atoi(cmdargs.argv[a++]);
695 chunksize = am_floor(chunksize, DISK_BLOCK_KB);
697 if(a >= cmdargs.argc) {
698 error("error [dumper CONTINUE: not enough args: use]");
700 use = atoi(cmdargs.argv[a++]);
702 if(a != cmdargs.argc) {
703 error("error [dumper CONTINUE: too many args: %d != %d]",
707 if(strcmp(filename, arg_filename) == 0) {
709 * Same disk, so use what room is left up to the
710 * next chunk boundary or the amount we were given,
713 left_in_chunk = chunksize - filesize;
714 if(left_in_chunk > use) {
718 split_size += left_in_chunk;
719 use -= left_in_chunk;
721 if(left_in_chunk > 0) {
723 * We still have space in this chunk.
729 * Different disk, so use new file.
731 filename = newstralloc(filename, arg_filename);
733 } else if(cmd == ABORT) {
735 errstr = newstralloc(errstr, "ERROR");
739 if(cmdargs.argc >= 1) {
740 q = squote(cmdargs.argv[1]);
741 } else if(cmdargs.argc >= 0) {
742 q = squote(cmdargs.argv[0]);
744 q = stralloc("(no input?)");
746 error("error [bad command after RQ-MORE-DISK: \"%s\"]", q);
750 ap_snprintf(sequence, sizeof(sequence), "%d", filename_seq);
751 new_filename = newvstralloc(new_filename,
756 tmp_filename = newvstralloc(tmp_filename,
760 pc = strrchr(tmp_filename, '/');
762 mkholdingdir(tmp_filename);
764 new_outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
765 if(new_outfd == -1) {
766 int save_errno = errno;
768 errstr = squotef("creating chunk holding file \"%s\": %s",
770 strerror(save_errno));
771 if(save_errno == ENOSPC) {
772 putresult(NO_ROOM, "%s %lu\n",
774 use + split_size - dumpsize);
775 use = 0; /* force RQ_MORE_DISK */
776 split_size = dumpsize;
783 save_type = file.type;
784 file.type = F_CONT_DUMPFILE;
785 file.cont_filename[0] = '\0';
786 if(write_tapeheader(new_outfd, &file)) {
787 int save_errno = errno;
790 unlink(tmp_filename);
791 if(save_errno == ENOSPC) {
792 putresult(NO_ROOM, "%s %lu\n",
794 use + split_size - dumpsize);
795 use = 0; /* force RQ_MORE_DISK */
796 split_size = dumpsize;
799 errstr = squotef("write_tapeheader file \"%s\": %s",
800 tmp_filename, strerror(errno));
804 if(lseek(outfd, (off_t)0, SEEK_SET) != 0) {
805 errstr = squotef("cannot lseek: %s", strerror(errno));
807 unlink(tmp_filename);
811 strncpy(file.cont_filename, new_filename,
812 sizeof(file.cont_filename));
813 file.cont_filename[sizeof(file.cont_filename)-1] = '\0';
814 file.type = save_type;
815 if(write_tapeheader(outfd, &file)) {
816 errstr = squotef("write_tapeheader file linked to \"%s\": %s",
817 tmp_filename, strerror(errno));
819 unlink(tmp_filename);
823 file.type = F_CONT_DUMPFILE;
824 strncpy(cont_filename, new_filename, sizeof(cont_filename));
825 cont_filename[sizeof(cont_filename)-1] = '\0';
828 *p_outfd = outfd = new_outfd;
831 dumpsize += DISK_BLOCK_KB;
832 filesize = DISK_BLOCK_KB;
833 split_size += (chunksize>use)?use:chunksize;
834 use = (chunksize>use)?0:use-chunksize;
838 rc = write_dataptr(outfd);
843 amfree(new_filename);
844 amfree(tmp_filename);
845 amfree(arg_filename);
850 static char *msgbuf = NULL;
851 int got_info_endline;
856 static void process_dumpeof()
858 /* process any partial line in msgbuf? !!! */
860 fprintf(errf,"? %s: error [partial line in msgbuf: %ld bytes]\n",
861 get_pname(), (long) strlen(msgbuf));
862 fprintf(errf,"? %s: error [partial line in msgbuf: \"%s\"]\n",
863 get_pname(), msgbuf);
865 if(!got_sizeline && dump_result < 2) {
866 /* make a note if there isn't already a failure */
867 fprintf(errf,"? %s: strange [missing size line from sendbackup]\n",
869 dump_result = max(dump_result, 2);
872 if(!got_endline && dump_result < 2) {
873 fprintf(errf,"? %s: strange [missing end line from sendbackup]\n",
875 dump_result = max(dump_result, 2);
879 /* Parse an information line from the client.
880 ** We ignore unknown parameters and only remember the last
881 ** of any duplicates.
883 static void parse_info_line(str)
886 if(strcmp(str, "end") == 0) {
887 got_info_endline = 1;
892 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
893 backup_name = newstralloc(backup_name, str + sizeof(sc)-1);
898 #define sc "RECOVER_CMD="
899 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
900 recover_cmd = newstralloc(recover_cmd, str + sizeof(sc)-1);
905 #define sc "COMPRESS_SUFFIX="
906 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
907 compress_suffix = newstralloc(compress_suffix, str + sizeof(sc)-1);
913 static void process_dumpline(str)
924 /* normal backup output line */
927 /* sendbackup detected something strange */
928 dump_result = max(dump_result, 1);
931 /* a sendbackup line, just check them all since there are only 5 */
932 #define sc "sendbackup: start"
933 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
937 #define sc "sendbackup: size"
938 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
941 skip_whitespace(s, ch);
943 origsize = (long)atof(str + sizeof(sc)-1);
949 #define sc "sendbackup: end"
950 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
955 #define sc "sendbackup: warning"
956 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
957 dump_result = max(dump_result, 1);
961 #define sc "sendbackup: error"
962 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
967 dump_result = max(dump_result, 2);
968 skip_whitespace(s, ch);
969 if(ch == '\0' || ch != '[') {
970 errstr = newvstralloc(errstr,
971 "bad remote error: ", str,
976 while(ch && ch != ']') ch = *s++;
978 errstr = newstralloc(errstr, fp);
983 #define sc "sendbackup: info"
984 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
987 skip_whitespace(s, ch);
988 parse_info_line(s - 1);
992 /* else we fall through to bad line */
994 fprintf(errf, "??%s", str);
995 dump_result = max(dump_result, 1);
998 fprintf(errf, "%s\n", str);
1001 static void add_msg_data(str, len)
1009 if((nl = strchr(str, '\n')) != NULL) {
1013 t = stralloc2(msgbuf, str);
1016 } else if(nl == NULL) {
1017 msgbuf = stralloc(str);
1021 if(nl == NULL) break;
1022 process_dumpline(msgbuf);
1023 if(msgbuf != str) free(msgbuf);
1025 len -= nl + 1 - str;
1031 static void log_msgout(typ)
1037 (void) fseek(errf, 0L, SEEK_SET);
1038 for(; (line = agets(errf)) != NULL; free(line)) {
1039 log_add(typ, "%s", line);
1046 void make_tapeheader(file, type)
1052 strncpy(file->datestamp , datestamp , sizeof(file->datestamp)-1);
1053 file->datestamp[sizeof(file->datestamp)-1] = '\0';
1054 strncpy(file->name , hostname , sizeof(file->name)-1);
1055 file->name[sizeof(file->name)-1] = '\0';
1056 strncpy(file->disk , diskname , sizeof(file->disk)-1);
1057 file->disk[sizeof(file->disk)-1] = '\0';
1058 file->dumplevel = level;
1059 strncpy(file->program , backup_name, sizeof(file->program)-1);
1060 file->program[sizeof(file->program)-1] = '\0';
1061 strncpy(file->recover_cmd, recover_cmd, sizeof(file->recover_cmd)-1);
1062 file->recover_cmd[sizeof(file->recover_cmd)-1] = '\0';
1063 file->blocksize = DISK_BLOCK_BYTES;
1067 ap_snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd),
1068 " %s %s |", UNCOMPRESS_PATH,
1069 #ifdef UNCOMPRESS_OPT
1075 strncpy(file->comp_suffix, COMPRESS_SUFFIX,sizeof(file->comp_suffix)-1);
1076 file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1079 file->uncompress_cmd[0] = '\0';
1080 file->compressed=compress_suffix!=NULL;
1081 if(compress_suffix) {
1082 strncpy(file->comp_suffix, compress_suffix,
1083 sizeof(file->comp_suffix)-1);
1084 file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1086 strncpy(file->comp_suffix, "N", sizeof(file->comp_suffix)-1);
1087 file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1090 strncpy(file->cont_filename, cont_filename, sizeof(file->cont_filename)-1);
1091 file->cont_filename[sizeof(file->cont_filename)-1] = '\0';
1094 /* Send an Amanda dump header to the output file.
1095 * returns true if an error occured, false on success
1098 int write_tapeheader(outfd, file)
1102 char buffer[DISK_BLOCK_BYTES];
1105 build_header(buffer, file, sizeof(buffer));
1107 written = fullwrite(outfd, buffer, sizeof(buffer));
1108 if(written == sizeof(buffer)) return 0;
1109 if(written < 0) return written;
1115 int do_dump(mesgfd, datafd, indexfd, outfd)
1116 int mesgfd, datafd, indexfd, outfd;
1118 int maxfd, nfound, size1, size2, eof1, eof2;
1120 fd_set readset, selectset;
1121 struct timeval timeout;
1123 int header_done; /* flag - header has been written */
1124 char *indexfile_tmp = NULL;
1125 char *indexfile_real = NULL;
1126 char level_str[NUM_STR_SIZE];
1127 char kb_str[NUM_STR_SIZE];
1128 char kps_str[NUM_STR_SIZE];
1129 char orig_kb_str[NUM_STR_SIZE];
1133 double dumptime; /* Time dump took in secs */
1134 int compresspid = -1, indexpid = -1, killerr;
1135 char *errfname = NULL;
1137 #ifndef DUMPER_SOCKET_BUFFERING
1138 #define DUMPER_SOCKET_BUFFERING 0
1141 #if !defined(SO_RCVBUF) || !defined(SO_RCVLOWAT)
1142 #undef DUMPER_SOCKET_BUFFERING
1143 #define DUMPER_SOCKET_BUFFERING 0
1146 #if DUMPER_SOCKET_BUFFERING
1147 int lowat = NETWORK_BLOCK_BYTES;
1149 int sizeof_recbuf = sizeof(recbuf);
1151 int lowwatset_count = 0;
1156 datain = dataout = databuf;
1157 datalimit = databuf + sizeof(databuf);
1158 dumpsize = dumpbytes = origsize = filesize = dump_result = 0;
1159 nb_header_block = 0;
1160 got_info_endline = got_sizeline = got_endline = 0;
1162 amfree(backup_name);
1163 amfree(recover_cmd);
1164 amfree(compress_suffix);
1166 ap_snprintf(level_str, sizeof(level_str), "%d", level);
1167 fn = sanitise_filename(diskname);
1168 errfname = newvstralloc(errfname,
1176 if((errf = fopen(errfname, "w+")) == NULL) {
1177 errstr = newvstralloc(errstr,
1178 "errfile open \"", errfname, "\": ",
1185 unlink(errfname); /* so it goes away on close */
1188 /* insert pipe in the *READ* side, if server-side compression is desired */
1194 pipe(outpipe); /* outpipe[0] is pipe's stdin, outpipe[1] is stdout. */
1195 datafd = outpipe[0];
1196 if(datafd < 0 || datafd >= FD_SETSIZE) {
1199 errstr = newstralloc(errstr, "descriptor out of range");
1204 switch(compresspid=fork()) {
1206 errstr = newstralloc2(errstr, "couldn't fork: ", strerror(errno));
1215 /* child acts on stdin/stdout */
1216 if (dup2(outpipe[1],1) == -1)
1217 fprintf(stderr, "err dup2 out: %s\n", strerror(errno));
1218 if (dup2(tmpfd, 0) == -1)
1219 fprintf(stderr, "err dup2 in: %s\n", strerror(errno));
1221 /* now spawn gzip -1 to take care of the rest */
1222 execlp(COMPRESS_PATH, COMPRESS_PATH,
1223 (srvcompress == srvcomp_best ? COMPRESS_BEST_OPT
1224 : COMPRESS_FAST_OPT),
1226 error("error: couldn't exec %s.\n", COMPRESS_PATH);
1228 /* Now the pipe has been inserted. */
1232 if (indexfd != -1) {
1233 indexfile_real = getindexfname(hostname, diskname, datestamp, level),
1234 indexfile_tmp = stralloc2(indexfile_real, ".tmp");
1236 if (mkpdir(indexfile_tmp, 02755, (uid_t)-1, (gid_t)-1) == -1) {
1237 errstr = newvstralloc(errstr,
1243 amfree(indexfile_real);
1244 amfree(indexfile_tmp);
1249 switch(indexpid=fork()) {
1251 errstr = newstralloc2(errstr, "couldn't fork: ", strerror(errno));
1256 indexfd = -1; /* redundant */
1259 if (dup2(indexfd, 0) == -1) {
1260 error("err dup2 in: %s", strerror(errno));
1262 indexfd = open(indexfile_tmp, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1264 error("err open %s: %s", indexfile_tmp, strerror(errno));
1265 if (dup2(indexfd,1) == -1)
1266 error("err dup2 out: %s", strerror(errno));
1268 execlp(COMPRESS_PATH, COMPRESS_PATH, COMPRESS_BEST_OPT, (char *)0);
1269 error("error: couldn't exec %s.", COMPRESS_PATH);
1273 NAUGHTY_BITS_INITIALIZE;
1275 maxfd = max(mesgfd, datafd) + 1;
1280 /* Just process messages for now. Once we have done the header
1281 ** we will start processing data too.
1283 FD_SET(mesgfd, &readset);
1285 if(datafd == -1) eof1 = 1; /* fake eof on data */
1287 #if DUMPER_SOCKET_BUFFERING
1289 #ifndef EST_PACKET_SIZE
1290 #define EST_PACKET_SIZE 512
1292 #ifndef EST_MIN_WINDOW
1293 #define EST_MIN_WINDOW EST_PACKET_SIZE*4 /* leave room for 2k in transit */
1297 recbuf = STREAM_BUFSIZE;
1298 if (setsockopt(datafd, SOL_SOCKET, SO_RCVBUF,
1299 (void *) &recbuf, sizeof_recbuf)) {
1300 const int errornumber = errno;
1301 fprintf(stderr, "%s: pid %ld setsockopt(SO_RCVBUF): %s\n",
1302 get_pname(), (long) getpid(), strerror(errornumber));
1304 if (getsockopt(datafd, SOL_SOCKET, SO_RCVBUF,
1305 (void *) &recbuf, (void *)&sizeof_recbuf)) {
1306 const int errornumber = errno;
1307 fprintf(stderr, "%s: pid %ld getsockopt(SO_RCVBUF): %s\n",
1308 get_pname(), (long) getpid(), strerror(errornumber));
1312 /* leave at least EST_MIN_WINDOW between lowwat and recbuf */
1313 if (recbuf-lowat < EST_MIN_WINDOW)
1314 lowat = recbuf-EST_MIN_WINDOW;
1316 /* if lowwat < ~512, don't bother */
1317 if (lowat < EST_PACKET_SIZE)
1319 fprintf(stderr, "%s: pid %ld receive size is %d, low water is %d\n",
1320 get_pname(), (long) getpid(), recbuf, lowat);
1324 while(!(eof1 && eof2)) {
1326 #if DUMPER_SOCKET_BUFFERING
1327 /* Set socket buffering */
1328 if (recbuf>0 && !lowwatset) {
1329 if (setsockopt(datafd, SOL_SOCKET, SO_RCVLOWAT,
1330 (void *) &lowat, sizeof(lowat))) {
1331 const int errornumber = errno;
1333 "%s: pid %ld setsockopt(SO_RCVLOWAT): %s\n",
1334 get_pname(), (long) getpid(), strerror(errornumber));
1341 timeout.tv_sec = conf_dtimeout;
1342 timeout.tv_usec = 0;
1343 memcpy(&selectset, &readset, sizeof(fd_set));
1345 nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
1347 /* check for errors or timeout */
1349 #if DUMPER_SOCKET_BUFFERING
1350 if (nfound==0 && lowwatset) {
1352 /* Disable socket buffering and ... */
1353 if (setsockopt(datafd, SOL_SOCKET, SO_RCVLOWAT,
1354 (void *) &zero, sizeof(zero))) {
1355 const int errornumber = errno;
1357 "%s: pid %ld setsockopt(SO_RCVLOWAT): %s\n",
1358 get_pname(), (long) getpid(), strerror(errornumber));
1362 /* ... try once more */
1363 timeout.tv_sec = conf_dtimeout;
1364 timeout.tv_usec = 0;
1365 memcpy(&selectset, &readset, sizeof(fd_set));
1366 nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
1371 errstr = newstralloc(errstr, "data timeout");
1376 errstr = newstralloc2(errstr, "select: ", strerror(errno));
1381 /* read/write any data */
1383 if(datafd >= 0 && FD_ISSET(datafd, &selectset)) {
1384 size1 = read(datafd, datain, datalimit - datain);
1386 errstr = newstralloc2(errstr, "data read: ", strerror(errno));
1390 if(update_dataptr(&outfd, size1)) {
1396 FD_CLR(datafd, &readset);
1401 if(mesgfd >= 0 && FD_ISSET(mesgfd, &selectset)) {
1402 size2 = read(mesgfd, mesgbuf, sizeof(mesgbuf)-1);
1405 errstr = newstralloc2(errstr, "mesg read: ", strerror(errno));
1411 FD_CLR(mesgfd, &readset);
1415 mesgbuf[size2] = '\0';
1416 add_msg_data(mesgbuf, size2);
1419 if (got_info_endline && !header_done) { /* time to do the header */
1420 make_tapeheader(&file, F_DUMPFILE);
1421 if (write_tapeheader(outfd, &file)) {
1422 int save_errno = errno;
1423 errstr = newstralloc2(errstr, "write_tapeheader: ",
1425 if(save_errno == ENOSPC) {
1426 putresult(NO_ROOM, "%s %lu\n", handle,
1427 use+split_size-dumpsize);
1428 use = 0; /* force RQ_MORE_DISK */
1429 split_size = dumpsize;
1437 dumpsize += DISK_BLOCK_KB;
1438 filesize += DISK_BLOCK_KB;
1441 strncat(cont_filename,filename,sizeof(cont_filename));
1442 cont_filename[sizeof(cont_filename)-1] = '\0';
1445 FD_SET(datafd, &readset); /* now we can read the data */
1450 #if DUMPER_SOCKET_BUFFERING
1451 if(lowwatset_count > 1) {
1452 fprintf(stderr, "%s: pid %ld low water set %d times\n",
1453 get_pname(), (long) getpid(), lowwatset_count);
1457 if(dump_result > 1) {
1462 runtime = stopclock();
1463 dumptime = runtime.r.tv_sec + runtime.r.tv_usec/1000000.0;
1465 dumpsize -= (nb_header_block * DISK_BLOCK_KB);/* don't count the header */
1466 if (dumpsize < 0) dumpsize = 0; /* XXX - maybe this should be fatal? */
1468 ap_snprintf(kb_str, sizeof(kb_str), "%ld", dumpsize);
1469 ap_snprintf(kps_str, sizeof(kps_str),
1471 dumptime ? dumpsize / dumptime : 0.0);
1472 ap_snprintf(orig_kb_str, sizeof(orig_kb_str), "%ld", origsize);
1473 errstr = newvstralloc(errstr,
1474 "sec ", walltime_str(runtime),
1476 " ", "kps ", kps_str,
1477 " ", "orig-kb ", orig_kb_str,
1479 q = squotef("[%s]", errstr);
1480 putresult(DONE, "%s %ld %ld %ld %s\n", handle, origsize, dumpsize,
1481 (long)(dumptime+0.5), q);
1484 switch(dump_result) {
1486 log_add(L_SUCCESS, "%s %s %s %d [%s]", hostname, diskname, datestamp, level, errstr);
1491 log_start_multiline();
1492 log_add(L_STRANGE, "%s %s %d [%s]", hostname, diskname, level, errstr);
1493 log_msgout(L_STRANGE);
1494 log_end_multiline();
1499 if(errf) afclose(errf);
1501 if (indexfile_tmp) {
1502 waitpid(indexpid,NULL,0);
1503 if(rename(indexfile_tmp, indexfile_real) != 0) {
1504 log_add(L_WARNING, "could not rename \"%s\" to \"%s\": %s",
1505 indexfile_tmp, indexfile_real, strerror(errno));
1507 amfree(indexfile_tmp);
1508 amfree(indexfile_real);
1515 #if DUMPER_SOCKET_BUFFERING
1516 if(lowwatset_count > 1) {
1517 fprintf(stderr, "%s: pid %ld low water set %d times\n",
1518 get_pname(), (long) getpid(), lowwatset_count);
1522 if(!abort_pending) {
1523 q = squotef("[%s]", errstr);
1525 putresult(FAILED, "%s %s\n", handle, q);
1527 putresult(TRYAGAIN, "%s %s\n", handle, q);
1531 /* kill all child process */
1532 if(compresspid != -1) {
1533 killerr = kill(compresspid,SIGTERM);
1535 fprintf(stderr,"%s: kill compress command\n",get_pname());
1537 else if ( killerr == -1 ) {
1539 fprintf(stderr,"%s: can't kill compress command: %s\n",
1540 get_pname(), strerror(errno));
1544 if(indexpid != -1) {
1545 killerr = kill(indexpid,SIGTERM);
1547 fprintf(stderr,"%s: kill index command\n",get_pname());
1549 else if ( killerr == -1 ) {
1551 fprintf(stderr,"%s: can't kill index command: %s\n",
1552 get_pname(),strerror(errno));
1556 if(!abort_pending) {
1557 log_start_multiline();
1558 log_add(L_FAIL, "%s %s %s %d [%s]", hostname, diskname,
1559 datestamp, level, errstr);
1563 log_end_multiline();
1566 if(errf) afclose(errf);
1568 if (indexfile_tmp) {
1569 unlink(indexfile_tmp);
1570 amfree(indexfile_tmp);
1571 amfree(indexfile_real);
1577 /* -------------------- */
1579 char *hostname, *disk;
1582 void sendbackup_response(p, pkt)
1588 int index_port = -1;
1596 am_release_feature_set(their_features);
1597 their_features = NULL;
1599 if(p->state == S_FAILED) {
1601 if(p->prevstate == S_REPWAIT) {
1602 errstr = newstralloc(errstr, "[reply timeout]");
1605 errstr = newstralloc(errstr, "[request timeout]");
1613 fprintf(stderr, "got %sresponse:\n----\n%s----\n\n",
1614 (p->state == S_FAILED) ? "NAK " : "", pkt->body);
1617 #ifdef KRB4_SECURITY
1618 if(krb4_auth && !check_mutual_authenticator(&cred.session, pkt, p)) {
1619 errstr = newstralloc(errstr, "mutual-authentication failed");
1630 if (s[-2] == '\n') {
1634 #define sc "OPTIONS "
1635 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1638 #define sc "features="
1639 t = strstr(line, sc);
1640 if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
1643 am_release_feature_set(their_features);
1644 if((their_features = am_string_to_feature(t)) == NULL) {
1645 errstr = newvstralloc(errstr,
1646 "bad features value: ",
1656 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1657 t = line + sizeof(sc)-1;
1662 skip_whitespace(t, tch);
1663 errstr = newvstralloc(errstr,
1664 (p->state == S_FAILED) ? "nak error: " : "",
1667 response_error = ((p->state == S_FAILED) ? 1 : 2);
1671 #define sc "CONNECT "
1672 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1676 t = strstr(line, sc);
1677 if(t != NULL && isspace((int)t[-1])) {
1680 data_port = atoi(t);
1684 t = strstr(line, sc);
1685 if(t != NULL && isspace((int)t[-1])) {
1688 mesg_port = atoi(t);
1692 t = strstr(line, sc);
1693 if(t != NULL && isspace((int)t[-1])) {
1696 index_port = atoi(t);
1701 errstr = newvstralloc(errstr,
1702 "unknown response: ",
1709 if (data_port == -1 || mesg_port == -1) {
1710 errstr = newvstralloc(errstr, "bad CONNECT response", NULL);
1715 datafd = stream_client(hostname, data_port, -1, -1, NULL);
1717 errstr = newvstralloc(errstr,
1718 "could not connect to data port: ",
1724 mesgfd = stream_client(hostname, mesg_port, -1, -1, NULL);
1726 errstr = newvstralloc(errstr,
1727 "could not connect to mesg port: ",
1731 datafd = -1; /* redundant */
1736 if (index_port != -1) {
1737 indexfd = stream_client(hostname, index_port, -1, -1, NULL);
1738 if (indexfd == -1) {
1739 errstr = newvstralloc(errstr,
1740 "could not connect to index port: ",
1745 datafd = mesgfd = -1; /* redundant */
1751 /* everything worked */
1753 #ifdef KRB4_SECURITY
1754 if(krb4_auth && kerberos_handshake(datafd, cred.session) == 0) {
1755 errstr = newstralloc(errstr,
1756 "mutual authentication in data stream failed");
1764 if(krb4_auth && kerberos_handshake(mesgfd, cred.session) == 0) {
1765 errstr = newstralloc(errstr,
1766 "mutual authentication in mesg stream failed");
1779 int startup_dump(hostname, disk, device, level, dumpdate, progname, options)
1780 char *hostname, *disk, *device, *dumpdate, *progname, *options;
1783 char level_string[NUM_STR_SIZE];
1787 int has_features = am_has_feature(their_features, fe_req_options_features);
1788 int has_hostname = am_has_feature(their_features, fe_req_options_hostname);
1789 int has_device = am_has_feature(their_features, fe_sendbackup_req_device);
1791 ap_snprintf(level_string, sizeof(level_string), "%d", level);
1792 req = vstralloc("SERVICE sendbackup\n",
1794 has_features ? "features=" : "",
1795 has_features ? our_feature_string : "",
1796 has_features ? ";" : "",
1797 has_hostname ? "hostname=" : "",
1798 has_hostname ? hostname : "",
1799 has_hostname ? ";" : "",
1803 " ", device && has_device ? device : "",
1806 " ", "OPTIONS ", options,
1810 datafd = mesgfd = indexfd = -1;
1812 #ifdef KRB4_SECURITY
1814 char rc_str[NUM_STR_SIZE];
1816 rc = make_krb_request(hostname, kamanda_port, req, NULL,
1817 STARTUP_TIMEOUT, sendbackup_response);
1819 char inst[256], realm[256];
1820 #define HOSTNAME_INSTANCE inst
1822 * This repeats a lot of work with make_krb_request, but it's
1823 * ultimately the kerberos library's fault: krb_mk_req calls
1824 * krb_get_cred, but doesn't make the session key available!
1825 * XXX: But admittedly, we could restructure a bit here and
1826 * at least eliminate the duplicate gethostbyname().
1828 if(host2krbname(hostname, inst, realm) == 0)
1831 rc = krb_get_cred(CLIENT_HOST_PRINCIPLE, CLIENT_HOST_INSTANCE,
1834 ap_snprintf(rc_str, sizeof(rc_str), "%d", rc);
1835 errstr = newvstralloc(errstr,
1837 ": krb4 error (krb_get_cred) ",
1839 ": ", krb_err_txt[rc],
1846 ap_snprintf(rc_str, sizeof(rc_str), "%d", rc);
1847 errstr = newvstralloc(errstr,
1849 ": krb4 error (make_krb_req) ",
1851 ": ", krb_err_txt[rc],
1858 rc = make_request(hostname, amanda_port, req, NULL,
1859 STARTUP_TIMEOUT, sendbackup_response);
1861 req = NULL; /* do not own this any more */
1864 errstr = newvstralloc(errstr,
1865 "[could not resolve name \"", hostname, "\"]",
1870 return response_error;