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 2003/10/30 18:09:27 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 while(size > 0 && split_size > 0 && dumpsize >= split_size) {
672 amfree(new_filename);
675 * Probably no more space on this disk. Request more.
677 putresult(RQ_MORE_DISK, "%s\n", handle);
678 cmd = getcmd(&cmdargs);
679 if(cmd == CONTINUE) {
686 cmdargs.argc++; /* true count of args */
689 if(a >= cmdargs.argc) {
690 error("error [dumper CONTINUE: not enough args: filename]");
692 arg_filename = newstralloc(arg_filename, cmdargs.argv[a++]);
694 if(a >= cmdargs.argc) {
695 error("error [dumper CONTINUE: not enough args: chunksize]");
697 chunksize = atoi(cmdargs.argv[a++]);
698 chunksize = am_floor(chunksize, DISK_BLOCK_KB);
700 if(a >= cmdargs.argc) {
701 error("error [dumper CONTINUE: not enough args: use]");
703 use = atoi(cmdargs.argv[a++]);
705 if(a != cmdargs.argc) {
706 error("error [dumper CONTINUE: too many args: %d != %d]",
710 if(strcmp(filename, arg_filename) == 0) {
712 * Same disk, so use what room is left up to the
713 * next chunk boundary or the amount we were given,
716 left_in_chunk = chunksize - filesize;
717 if(left_in_chunk > use) {
721 split_size += left_in_chunk;
722 use -= left_in_chunk;
724 if(left_in_chunk > 0) {
726 * We still have space in this chunk.
732 * Different disk, so use new file.
734 filename = newstralloc(filename, arg_filename);
736 } else if(cmd == ABORT) {
738 errstr = newstralloc(errstr, "ERROR");
742 if(cmdargs.argc >= 1) {
743 q = squote(cmdargs.argv[1]);
744 } else if(cmdargs.argc >= 0) {
745 q = squote(cmdargs.argv[0]);
747 q = stralloc("(no input?)");
749 error("error [bad command after RQ-MORE-DISK: \"%s\"]", q);
753 ap_snprintf(sequence, sizeof(sequence), "%d", filename_seq);
754 new_filename = newvstralloc(new_filename,
759 tmp_filename = newvstralloc(tmp_filename,
763 pc = strrchr(tmp_filename, '/');
765 mkholdingdir(tmp_filename);
767 new_outfd = open(tmp_filename, O_RDWR|O_CREAT|O_TRUNC, 0600);
768 if(new_outfd == -1) {
769 int save_errno = errno;
771 errstr = squotef("creating chunk holding file \"%s\": %s",
773 strerror(save_errno));
774 if(save_errno == ENOSPC) {
775 putresult(NO_ROOM, "%s %lu\n",
777 use + split_size - dumpsize);
778 use = 0; /* force RQ_MORE_DISK */
779 split_size = dumpsize;
786 save_type = file.type;
787 file.type = F_CONT_DUMPFILE;
788 file.cont_filename[0] = '\0';
789 if(write_tapeheader(new_outfd, &file)) {
790 int save_errno = errno;
793 unlink(tmp_filename);
794 if(save_errno == ENOSPC) {
795 putresult(NO_ROOM, "%s %lu\n",
797 use + split_size - dumpsize);
798 use = 0; /* force RQ_MORE_DISK */
799 split_size = dumpsize;
802 errstr = squotef("write_tapeheader file \"%s\": %s",
803 tmp_filename, strerror(errno));
807 if(lseek(outfd, (off_t)0, SEEK_SET) != 0) {
808 errstr = squotef("cannot lseek: %s", strerror(errno));
810 unlink(tmp_filename);
814 strncpy(file.cont_filename, new_filename,
815 sizeof(file.cont_filename));
816 file.cont_filename[sizeof(file.cont_filename)-1] = '\0';
817 file.type = save_type;
818 if(write_tapeheader(outfd, &file)) {
819 errstr = squotef("write_tapeheader file linked to \"%s\": %s",
820 tmp_filename, strerror(errno));
822 unlink(tmp_filename);
826 file.type = F_CONT_DUMPFILE;
827 strncpy(cont_filename, new_filename, sizeof(cont_filename));
828 cont_filename[sizeof(cont_filename)-1] = '\0';
831 *p_outfd = outfd = new_outfd;
834 dumpsize += DISK_BLOCK_KB;
835 filesize = DISK_BLOCK_KB;
836 split_size += (chunksize>use)?use:chunksize;
837 use = (chunksize>use)?0:use-chunksize;
841 rc = write_dataptr(outfd);
846 amfree(new_filename);
847 amfree(tmp_filename);
848 amfree(arg_filename);
853 static char *msgbuf = NULL;
854 int got_info_endline;
859 static void process_dumpeof()
861 /* process any partial line in msgbuf? !!! */
863 fprintf(errf,"? %s: error [partial line in msgbuf: %ld bytes]\n",
864 get_pname(), (long) strlen(msgbuf));
865 fprintf(errf,"? %s: error [partial line in msgbuf: \"%s\"]\n",
866 get_pname(), msgbuf);
868 if(!got_sizeline && dump_result < 2) {
869 /* make a note if there isn't already a failure */
870 fprintf(errf,"? %s: strange [missing size line from sendbackup]\n",
872 dump_result = max(dump_result, 2);
875 if(!got_endline && dump_result < 2) {
876 fprintf(errf,"? %s: strange [missing end line from sendbackup]\n",
878 dump_result = max(dump_result, 2);
882 /* Parse an information line from the client.
883 ** We ignore unknown parameters and only remember the last
884 ** of any duplicates.
886 static void parse_info_line(str)
889 if(strcmp(str, "end") == 0) {
890 got_info_endline = 1;
895 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
896 backup_name = newstralloc(backup_name, str + sizeof(sc)-1);
901 #define sc "RECOVER_CMD="
902 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
903 recover_cmd = newstralloc(recover_cmd, str + sizeof(sc)-1);
908 #define sc "COMPRESS_SUFFIX="
909 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
910 compress_suffix = newstralloc(compress_suffix, str + sizeof(sc)-1);
916 static void process_dumpline(str)
927 /* normal backup output line */
930 /* sendbackup detected something strange */
931 dump_result = max(dump_result, 1);
934 /* a sendbackup line, just check them all since there are only 5 */
935 #define sc "sendbackup: start"
936 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
940 #define sc "sendbackup: size"
941 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
944 skip_whitespace(s, ch);
946 origsize = (long)atof(str + sizeof(sc)-1);
952 #define sc "sendbackup: end"
953 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
958 #define sc "sendbackup: error"
959 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
964 dump_result = max(dump_result, 2);
965 skip_whitespace(s, ch);
966 if(ch == '\0' || ch != '[') {
967 errstr = newvstralloc(errstr,
968 "bad remote error: ", str,
973 while(ch && ch != ']') ch = *s++;
975 errstr = newstralloc(errstr, fp);
980 #define sc "sendbackup: info"
981 if(strncmp(str, sc, sizeof(sc)-1) == 0) {
984 skip_whitespace(s, ch);
985 parse_info_line(s - 1);
989 /* else we fall through to bad line */
991 fprintf(errf, "??%s", str);
992 dump_result = max(dump_result, 1);
995 fprintf(errf, "%s\n", str);
998 static void add_msg_data(str, len)
1006 if((nl = strchr(str, '\n')) != NULL) {
1010 t = stralloc2(msgbuf, str);
1013 } else if(nl == NULL) {
1014 msgbuf = stralloc(str);
1018 if(nl == NULL) break;
1019 process_dumpline(msgbuf);
1020 if(msgbuf != str) free(msgbuf);
1022 len -= nl + 1 - str;
1028 static void log_msgout(typ)
1034 (void) fseek(errf, 0L, SEEK_SET);
1035 for(; (line = agets(errf)) != NULL; free(line)) {
1036 log_add(typ, "%s", line);
1043 void make_tapeheader(file, type)
1049 strncpy(file->datestamp , datestamp , sizeof(file->datestamp)-1);
1050 file->datestamp[sizeof(file->datestamp)-1] = '\0';
1051 strncpy(file->name , hostname , sizeof(file->name)-1);
1052 file->name[sizeof(file->name)-1] = '\0';
1053 strncpy(file->disk , diskname , sizeof(file->disk)-1);
1054 file->disk[sizeof(file->disk)-1] = '\0';
1055 file->dumplevel = level;
1056 strncpy(file->program , backup_name, sizeof(file->program)-1);
1057 file->program[sizeof(file->program)-1] = '\0';
1058 strncpy(file->recover_cmd, recover_cmd, sizeof(file->recover_cmd)-1);
1059 file->recover_cmd[sizeof(file->recover_cmd)-1] = '\0';
1060 file->blocksize = DISK_BLOCK_BYTES;
1064 ap_snprintf(file->uncompress_cmd, sizeof(file->uncompress_cmd),
1065 " %s %s |", UNCOMPRESS_PATH,
1066 #ifdef UNCOMPRESS_OPT
1072 strncpy(file->comp_suffix, COMPRESS_SUFFIX,sizeof(file->comp_suffix)-1);
1073 file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1076 file->uncompress_cmd[0] = '\0';
1077 file->compressed=compress_suffix!=NULL;
1078 if(compress_suffix) {
1079 strncpy(file->comp_suffix, compress_suffix,
1080 sizeof(file->comp_suffix)-1);
1081 file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1083 strncpy(file->comp_suffix, "N", sizeof(file->comp_suffix)-1);
1084 file->comp_suffix[sizeof(file->comp_suffix)-1] = '\0';
1087 strncpy(file->cont_filename, cont_filename, sizeof(file->cont_filename)-1);
1088 file->cont_filename[sizeof(file->cont_filename)-1] = '\0';
1091 /* Send an Amanda dump header to the output file.
1092 * returns true if an error occured, false on success
1095 int write_tapeheader(outfd, file)
1099 char buffer[DISK_BLOCK_BYTES];
1102 build_header(buffer, file, sizeof(buffer));
1104 written = fullwrite(outfd, buffer, sizeof(buffer));
1105 if(written == sizeof(buffer)) return 0;
1106 if(written < 0) return written;
1112 int do_dump(mesgfd, datafd, indexfd, outfd)
1113 int mesgfd, datafd, indexfd, outfd;
1115 int maxfd, nfound, size1, size2, eof1, eof2;
1117 fd_set readset, selectset;
1118 struct timeval timeout;
1120 int header_done; /* flag - header has been written */
1121 char *indexfile_tmp = NULL;
1122 char *indexfile_real = NULL;
1123 char level_str[NUM_STR_SIZE];
1124 char kb_str[NUM_STR_SIZE];
1125 char kps_str[NUM_STR_SIZE];
1126 char orig_kb_str[NUM_STR_SIZE];
1130 double dumptime; /* Time dump took in secs */
1131 int compresspid = -1, indexpid = -1, killerr;
1132 char *errfname = NULL;
1134 #ifndef DUMPER_SOCKET_BUFFERING
1135 #define DUMPER_SOCKET_BUFFERING 0
1138 #if !defined(SO_RCVBUF) || !defined(SO_RCVLOWAT)
1139 #undef DUMPER_SOCKET_BUFFERING
1140 #define DUMPER_SOCKET_BUFFERING 0
1143 #if DUMPER_SOCKET_BUFFERING
1144 int lowat = NETWORK_BLOCK_BYTES;
1146 int sizeof_recbuf = sizeof(recbuf);
1148 int lowwatset_count = 0;
1153 datain = dataout = databuf;
1154 datalimit = databuf + sizeof(databuf);
1155 dumpsize = dumpbytes = origsize = filesize = dump_result = 0;
1156 nb_header_block = 0;
1157 got_info_endline = got_sizeline = got_endline = 0;
1159 amfree(backup_name);
1160 amfree(recover_cmd);
1161 amfree(compress_suffix);
1163 ap_snprintf(level_str, sizeof(level_str), "%d", level);
1164 fn = sanitise_filename(diskname);
1165 errfname = newvstralloc(errfname,
1173 if((errf = fopen(errfname, "w+")) == NULL) {
1174 errstr = newvstralloc(errstr,
1175 "errfile open \"", errfname, "\": ",
1182 unlink(errfname); /* so it goes away on close */
1185 /* insert pipe in the *READ* side, if server-side compression is desired */
1191 pipe(outpipe); /* outpipe[0] is pipe's stdin, outpipe[1] is stdout. */
1192 datafd = outpipe[0];
1193 if(datafd < 0 || datafd >= FD_SETSIZE) {
1196 errstr = newstralloc(errstr, "descriptor out of range");
1201 switch(compresspid=fork()) {
1203 errstr = newstralloc2(errstr, "couldn't fork: ", strerror(errno));
1212 /* child acts on stdin/stdout */
1213 if (dup2(outpipe[1],1) == -1)
1214 fprintf(stderr, "err dup2 out: %s\n", strerror(errno));
1215 if (dup2(tmpfd, 0) == -1)
1216 fprintf(stderr, "err dup2 in: %s\n", strerror(errno));
1217 for(tmpfd = 3; tmpfd <= FD_SETSIZE; ++tmpfd) {
1220 /* now spawn gzip -1 to take care of the rest */
1221 execlp(COMPRESS_PATH, COMPRESS_PATH,
1222 (srvcompress == srvcomp_best ? COMPRESS_BEST_OPT
1223 : COMPRESS_FAST_OPT),
1225 error("error: couldn't exec %s.\n", COMPRESS_PATH);
1227 /* Now the pipe has been inserted. */
1231 if (indexfd != -1) {
1234 indexfile_real = getindexfname(hostname, diskname, datestamp, level),
1235 indexfile_tmp = stralloc2(indexfile_real, ".tmp");
1237 if (mkpdir(indexfile_tmp, 02755, (uid_t)-1, (gid_t)-1) == -1) {
1238 errstr = newvstralloc(errstr,
1244 amfree(indexfile_real);
1245 amfree(indexfile_tmp);
1250 switch(indexpid=fork()) {
1252 errstr = newstralloc2(errstr, "couldn't fork: ", strerror(errno));
1257 indexfd = -1; /* redundant */
1260 if (dup2(indexfd, 0) == -1) {
1261 error("err dup2 in: %s", strerror(errno));
1263 indexfd = open(indexfile_tmp, O_WRONLY | O_CREAT | O_TRUNC, 0600);
1265 error("err open %s: %s", indexfile_tmp, strerror(errno));
1266 if (dup2(indexfd,1) == -1)
1267 error("err dup2 out: %s", strerror(errno));
1268 for(tmpfd = 3; tmpfd <= FD_SETSIZE; ++tmpfd) {
1271 execlp(COMPRESS_PATH, COMPRESS_PATH, COMPRESS_BEST_OPT, (char *)0);
1272 error("error: couldn't exec %s.", COMPRESS_PATH);
1276 NAUGHTY_BITS_INITIALIZE;
1278 maxfd = max(mesgfd, datafd) + 1;
1283 /* Just process messages for now. Once we have done the header
1284 ** we will start processing data too.
1286 FD_SET(mesgfd, &readset);
1288 if(datafd == -1) eof1 = 1; /* fake eof on data */
1290 #if DUMPER_SOCKET_BUFFERING
1292 #ifndef EST_PACKET_SIZE
1293 #define EST_PACKET_SIZE 512
1295 #ifndef EST_MIN_WINDOW
1296 #define EST_MIN_WINDOW EST_PACKET_SIZE*4 /* leave room for 2k in transit */
1300 recbuf = STREAM_BUFSIZE;
1301 if (setsockopt(datafd, SOL_SOCKET, SO_RCVBUF,
1302 (void *) &recbuf, sizeof_recbuf)) {
1303 const int errornumber = errno;
1304 fprintf(stderr, "%s: pid %ld setsockopt(SO_RCVBUF): %s\n",
1305 get_pname(), (long) getpid(), strerror(errornumber));
1307 if (getsockopt(datafd, SOL_SOCKET, SO_RCVBUF,
1308 (void *) &recbuf, (void *)&sizeof_recbuf)) {
1309 const int errornumber = errno;
1310 fprintf(stderr, "%s: pid %ld getsockopt(SO_RCVBUF): %s\n",
1311 get_pname(), (long) getpid(), strerror(errornumber));
1315 /* leave at least EST_MIN_WINDOW between lowwat and recbuf */
1316 if (recbuf-lowat < EST_MIN_WINDOW)
1317 lowat = recbuf-EST_MIN_WINDOW;
1319 /* if lowwat < ~512, don't bother */
1320 if (lowat < EST_PACKET_SIZE)
1322 fprintf(stderr, "%s: pid %ld receive size is %d, low water is %d\n",
1323 get_pname(), (long) getpid(), recbuf, lowat);
1327 while(!(eof1 && eof2)) {
1329 #if DUMPER_SOCKET_BUFFERING
1330 /* Set socket buffering */
1331 if (recbuf>0 && !lowwatset) {
1332 if (setsockopt(datafd, SOL_SOCKET, SO_RCVLOWAT,
1333 (void *) &lowat, sizeof(lowat))) {
1334 const int errornumber = errno;
1336 "%s: pid %ld setsockopt(SO_RCVLOWAT): %s\n",
1337 get_pname(), (long) getpid(), strerror(errornumber));
1344 timeout.tv_sec = conf_dtimeout;
1345 timeout.tv_usec = 0;
1346 memcpy(&selectset, &readset, sizeof(fd_set));
1348 nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
1350 /* check for errors or timeout */
1352 #if DUMPER_SOCKET_BUFFERING
1353 if (nfound==0 && lowwatset) {
1355 /* Disable socket buffering and ... */
1356 if (setsockopt(datafd, SOL_SOCKET, SO_RCVLOWAT,
1357 (void *) &zero, sizeof(zero))) {
1358 const int errornumber = errno;
1360 "%s: pid %ld setsockopt(SO_RCVLOWAT): %s\n",
1361 get_pname(), (long) getpid(), strerror(errornumber));
1365 /* ... try once more */
1366 timeout.tv_sec = conf_dtimeout;
1367 timeout.tv_usec = 0;
1368 memcpy(&selectset, &readset, sizeof(fd_set));
1369 nfound = select(maxfd, (SELECT_ARG_TYPE *)(&selectset), NULL, NULL, &timeout);
1374 errstr = newstralloc(errstr, "data timeout");
1379 errstr = newstralloc2(errstr, "select: ", strerror(errno));
1384 /* read/write any data */
1386 if(datafd >= 0 && FD_ISSET(datafd, &selectset)) {
1387 size1 = read(datafd, datain, datalimit - datain);
1389 errstr = newstralloc2(errstr, "data read: ", strerror(errno));
1393 if(update_dataptr(&outfd, size1)) {
1399 FD_CLR(datafd, &readset);
1404 if(mesgfd >= 0 && FD_ISSET(mesgfd, &selectset)) {
1405 size2 = read(mesgfd, mesgbuf, sizeof(mesgbuf)-1);
1408 errstr = newstralloc2(errstr, "mesg read: ", strerror(errno));
1414 FD_CLR(mesgfd, &readset);
1418 mesgbuf[size2] = '\0';
1419 add_msg_data(mesgbuf, size2);
1422 if (got_info_endline && !header_done) { /* time to do the header */
1423 make_tapeheader(&file, F_DUMPFILE);
1424 if (write_tapeheader(outfd, &file)) {
1425 int save_errno = errno;
1426 errstr = newstralloc2(errstr, "write_tapeheader: ",
1428 if(save_errno == ENOSPC) {
1429 putresult(NO_ROOM, "%s %lu\n", handle,
1430 use+split_size-dumpsize);
1431 use = 0; /* force RQ_MORE_DISK */
1432 split_size = dumpsize;
1440 dumpsize += DISK_BLOCK_KB;
1441 filesize += DISK_BLOCK_KB;
1444 strncat(cont_filename,filename,sizeof(cont_filename));
1445 cont_filename[sizeof(cont_filename)-1] = '\0';
1448 FD_SET(datafd, &readset); /* now we can read the data */
1453 #if DUMPER_SOCKET_BUFFERING
1454 if(lowwatset_count > 1) {
1455 fprintf(stderr, "%s: pid %ld low water set %d times\n",
1456 get_pname(), (long) getpid(), lowwatset_count);
1460 if(dump_result > 1) {
1465 runtime = stopclock();
1466 dumptime = runtime.r.tv_sec + runtime.r.tv_usec/1000000.0;
1468 dumpsize -= (nb_header_block * DISK_BLOCK_KB);/* don't count the header */
1469 if (dumpsize < 0) dumpsize = 0; /* XXX - maybe this should be fatal? */
1471 ap_snprintf(kb_str, sizeof(kb_str), "%ld", dumpsize);
1472 ap_snprintf(kps_str, sizeof(kps_str),
1474 dumptime ? dumpsize / dumptime : 0.0);
1475 ap_snprintf(orig_kb_str, sizeof(orig_kb_str), "%ld", origsize);
1476 errstr = newvstralloc(errstr,
1477 "sec ", walltime_str(runtime),
1479 " ", "kps ", kps_str,
1480 " ", "orig-kb ", orig_kb_str,
1482 q = squotef("[%s]", errstr);
1483 putresult(DONE, "%s %ld %ld %ld %s\n", handle, origsize, dumpsize,
1484 (long)(dumptime+0.5), q);
1487 switch(dump_result) {
1489 log_add(L_SUCCESS, "%s %s %s %d [%s]", hostname, diskname, datestamp, level, errstr);
1494 log_start_multiline();
1495 log_add(L_STRANGE, "%s %s %d [%s]", hostname, diskname, level, errstr);
1496 log_msgout(L_STRANGE);
1497 log_end_multiline();
1502 if(errf) afclose(errf);
1504 if (indexfile_tmp) {
1505 waitpid(indexpid,NULL,0);
1506 if(rename(indexfile_tmp, indexfile_real) != 0) {
1507 log_add(L_WARNING, "could not rename \"%s\" to \"%s\": %s",
1508 indexfile_tmp, indexfile_real, strerror(errno));
1510 amfree(indexfile_tmp);
1511 amfree(indexfile_real);
1518 #if DUMPER_SOCKET_BUFFERING
1519 if(lowwatset_count > 1) {
1520 fprintf(stderr, "%s: pid %ld low water set %d times\n",
1521 get_pname(), (long) getpid(), lowwatset_count);
1525 if(!abort_pending) {
1526 q = squotef("[%s]", errstr);
1528 putresult(FAILED, "%s %s\n", handle, q);
1530 putresult(TRYAGAIN, "%s %s\n", handle, q);
1534 /* kill all child process */
1535 if(compresspid != -1) {
1536 killerr = kill(compresspid,SIGTERM);
1538 fprintf(stderr,"%s: kill compress command\n",get_pname());
1540 else if ( killerr == -1 ) {
1542 fprintf(stderr,"%s: can't kill compress command: %s\n",
1543 get_pname(), strerror(errno));
1547 if(indexpid != -1) {
1548 killerr = kill(indexpid,SIGTERM);
1550 fprintf(stderr,"%s: kill index command\n",get_pname());
1552 else if ( killerr == -1 ) {
1554 fprintf(stderr,"%s: can't kill index command: %s\n",
1555 get_pname(),strerror(errno));
1559 if(!abort_pending) {
1560 log_start_multiline();
1561 log_add(L_FAIL, "%s %s %s %d [%s]", hostname, diskname,
1562 datestamp, level, errstr);
1566 log_end_multiline();
1569 if(errf) afclose(errf);
1571 if (indexfile_tmp) {
1572 unlink(indexfile_tmp);
1573 amfree(indexfile_tmp);
1574 amfree(indexfile_real);
1580 /* -------------------- */
1582 char *hostname, *disk;
1585 void sendbackup_response(p, pkt)
1591 int index_port = -1;
1599 am_release_feature_set(their_features);
1600 their_features = NULL;
1602 if(p->state == S_FAILED) {
1604 if(p->prevstate == S_REPWAIT) {
1605 errstr = newstralloc(errstr, "[reply timeout]");
1608 errstr = newstralloc(errstr, "[request timeout]");
1616 fprintf(stderr, "got %sresponse:\n----\n%s----\n\n",
1617 (p->state == S_FAILED) ? "NAK " : "", pkt->body);
1620 #ifdef KRB4_SECURITY
1621 if(krb4_auth && !check_mutual_authenticator(&cred.session, pkt, p)) {
1622 errstr = newstralloc(errstr, "mutual-authentication failed");
1633 if (s[-2] == '\n') {
1637 #define sc "OPTIONS "
1638 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1641 #define sc "features="
1642 t = strstr(line, sc);
1643 if(t != NULL && (isspace((int)t[-1]) || t[-1] == ';')) {
1646 am_release_feature_set(their_features);
1647 if((their_features = am_string_to_feature(t)) == NULL) {
1648 errstr = newvstralloc(errstr,
1649 "bad features value: ",
1659 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1660 t = line + sizeof(sc)-1;
1665 skip_whitespace(t, tch);
1666 errstr = newvstralloc(errstr,
1667 (p->state == S_FAILED) ? "nak error: " : "",
1670 response_error = ((p->state == S_FAILED) ? 1 : 2);
1674 #define sc "CONNECT "
1675 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1679 t = strstr(line, sc);
1680 if(t != NULL && isspace((int)t[-1])) {
1683 data_port = atoi(t);
1687 t = strstr(line, sc);
1688 if(t != NULL && isspace((int)t[-1])) {
1691 mesg_port = atoi(t);
1695 t = strstr(line, sc);
1696 if(t != NULL && isspace((int)t[-1])) {
1699 index_port = atoi(t);
1704 errstr = newvstralloc(errstr,
1705 "unknown response: ",
1712 if (data_port == -1 || mesg_port == -1) {
1713 errstr = newvstralloc(errstr, "bad CONNECT response", NULL);
1718 datafd = stream_client(hostname, data_port, -1, -1, NULL);
1720 errstr = newvstralloc(errstr,
1721 "could not connect to data port: ",
1727 mesgfd = stream_client(hostname, mesg_port, -1, -1, NULL);
1729 errstr = newvstralloc(errstr,
1730 "could not connect to mesg port: ",
1734 datafd = -1; /* redundant */
1739 if (index_port != -1) {
1740 indexfd = stream_client(hostname, index_port, -1, -1, NULL);
1741 if (indexfd == -1) {
1742 errstr = newvstralloc(errstr,
1743 "could not connect to index port: ",
1748 datafd = mesgfd = -1; /* redundant */
1754 /* everything worked */
1756 #ifdef KRB4_SECURITY
1757 if(krb4_auth && kerberos_handshake(datafd, cred.session) == 0) {
1758 errstr = newstralloc(errstr,
1759 "mutual authentication in data stream failed");
1767 if(krb4_auth && kerberos_handshake(mesgfd, cred.session) == 0) {
1768 errstr = newstralloc(errstr,
1769 "mutual authentication in mesg stream failed");
1782 int startup_dump(hostname, disk, device, level, dumpdate, progname, options)
1783 char *hostname, *disk, *device, *dumpdate, *progname, *options;
1786 char level_string[NUM_STR_SIZE];
1790 int has_features = am_has_feature(their_features, fe_req_options_features);
1791 int has_hostname = am_has_feature(their_features, fe_req_options_hostname);
1792 int has_device = am_has_feature(their_features, fe_sendbackup_req_device);
1794 ap_snprintf(level_string, sizeof(level_string), "%d", level);
1795 req = vstralloc("SERVICE sendbackup\n",
1797 has_features ? "features=" : "",
1798 has_features ? our_feature_string : "",
1799 has_features ? ";" : "",
1800 has_hostname ? "hostname=" : "",
1801 has_hostname ? hostname : "",
1802 has_hostname ? ";" : "",
1806 " ", device && has_device ? device : "",
1809 " ", "OPTIONS ", options,
1813 datafd = mesgfd = indexfd = -1;
1815 #ifdef KRB4_SECURITY
1817 char rc_str[NUM_STR_SIZE];
1819 rc = make_krb_request(hostname, kamanda_port, req, NULL,
1820 STARTUP_TIMEOUT, sendbackup_response);
1822 char inst[256], realm[256];
1823 #define HOSTNAME_INSTANCE inst
1825 * This repeats a lot of work with make_krb_request, but it's
1826 * ultimately the kerberos library's fault: krb_mk_req calls
1827 * krb_get_cred, but doesn't make the session key available!
1828 * XXX: But admittedly, we could restructure a bit here and
1829 * at least eliminate the duplicate gethostbyname().
1831 if(host2krbname(hostname, inst, realm) == 0)
1834 rc = krb_get_cred(CLIENT_HOST_PRINCIPLE, CLIENT_HOST_INSTANCE,
1837 ap_snprintf(rc_str, sizeof(rc_str), "%d", rc);
1838 errstr = newvstralloc(errstr,
1840 ": krb4 error (krb_get_cred) ",
1842 ": ", krb_err_txt[rc],
1849 ap_snprintf(rc_str, sizeof(rc_str), "%d", rc);
1850 errstr = newvstralloc(errstr,
1852 ": krb4 error (make_krb_req) ",
1854 ": ", krb_err_txt[rc],
1861 rc = make_request(hostname, amanda_port, req, NULL,
1862 STARTUP_TIMEOUT, sendbackup_response);
1864 req = NULL; /* do not own this any more */
1867 errstr = newvstralloc(errstr,
1868 "[could not resolve name \"", hostname, "\"]",
1873 return response_error;