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: amidxtaped.c,v 1.25.2.3.4.1.2.11 2004/01/29 19:26:51 martinea Exp $
28 * This daemon extracts a dump image off a tape for amrecover and
29 * returns it over the network. It basically, reads a number of
30 * arguments from stdin (it is invoked via inet), one per line,
31 * preceeded by the number of them, and forms them into an argv
32 * structure, then execs amrestore
44 static char *pgm = "amidxtaped"; /* in case argv[0] is not set */
46 char *conf_logdir = NULL;
47 char *conf_logfile = NULL;
49 char *found_device = NULL;
51 static char *get_client_line P((void));
53 /* get a line from client - line terminated by \r\n */
57 static char *line = NULL;
63 if((part = agets(stdin)) == NULL) {
65 dbprintf(("%s: read error: %s\n",
66 debug_prefix_time(NULL), strerror(errno)));
68 dbprintf(("%s: EOF reached\n", debug_prefix_time(NULL)));
71 dbprintf(("%s: unprocessed input:\n", debug_prefix_time(NULL)));
72 dbprintf(("-----\n"));
73 dbprintf(("%s\n", line));
74 dbprintf(("-----\n"));
86 strappend(line, part);
92 if((len = strlen(line)) > 0 && line[len-1] == '\r') {
93 line[len-1] = '\0'; /* zap the '\r' */
97 * Hmmm. We got a "line" from agets(), which means it saw
98 * a '\n' (or EOF, etc), but there was not a '\r' before it.
99 * Put a '\n' back in the buffer and loop for more.
101 strappend(line, "\n");
103 dbprintf(("%s: > %s\n", debug_prefix_time(NULL), line));
108 char *searchlabel = NULL;
109 int nslots, backwards;
111 int scan_init(rc, ns, bk)
116 unlink(conf_logfile);
118 error("could not get changer info: %s", changer_resultstr);
127 int taperscan_slot(rc, slotstr, device)
133 char *datestamp = NULL, *label = NULL;
136 dbprintf(("%s: fatal slot %s: %s\n", debug_prefix_time(NULL),
137 slotstr, changer_resultstr));
141 dbprintf(("%s: slot %s: %s\n", debug_prefix_time(NULL),
142 slotstr, changer_resultstr));
146 if((errstr = tape_rdlabel(device, &datestamp, &label)) != NULL) {
147 dbprintf(("%s: slot %s: %s\n", debug_prefix_time(NULL),
150 /* got an amanda tape */
151 dbprintf(("%s: slot %s: date %-8s label %s",
152 debug_prefix_time(NULL), slotstr, datestamp, label));
153 if(strcmp(label, FAKE_LABEL) == 0 ||
154 strcmp(label, searchlabel) == 0) {
155 /* it's the one we are looking for, stop here */
156 found_device = newstralloc(found_device,device);
157 dbprintf((" (exact label match)\n"));
162 dbprintf((" (no match)\n"));
172 conf_logdir = getconf_str(CNF_LOGDIR);
173 if (*conf_logdir == '/') {
174 conf_logdir = stralloc(conf_logdir);
176 conf_logdir = stralloc2(config_dir, conf_logdir);
178 conf_logfile = vstralloc(conf_logdir, "/log", NULL);
179 if (access(conf_logfile, F_OK) == 0) {
180 error("%s exists: amdump or amflush is already running, or you must run amcleanup", conf_logfile);
182 log_add(L_INFO, "amidxtaped");
192 char **amrestore_args;
195 char *amrestore_path;
198 struct stat stat_tape;
199 char *tapename = NULL;
204 struct sockaddr_in addr;
209 char *re_label = NULL;
211 char *re_device = NULL;
212 char *re_host = NULL;
213 char *re_disk = NULL;
214 char *re_datestamp = NULL;
215 char *re_config = NULL;
217 for(fd = 3; fd < FD_SETSIZE; fd++) {
219 * Make sure nobody spoofs us with a lot of extra open files
220 * that would cause an open we do to get a very high file
221 * descriptor, which in turn might be used as an index into
222 * an array (e.g. an fd_set).
230 * When called via inetd, it is not uncommon to forget to put the
231 * argv[0] value on the config line. On some systems (e.g. Solaris)
232 * this causes argv and/or argv[0] to be NULL, so we have to be
233 * careful getting our name.
235 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
236 if((pgm = strrchr(argv[0], '/')) != NULL) {
247 /* we'd rather not run as root */
250 if(client_uid == (uid_t) -1) {
251 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
254 initgroups(CLIENT_LOGIN, client_gid);
259 #endif /* FORCE_USERID */
262 /* close stderr first so that debug file becomes it - amrestore
263 chats to stderr, which we don't want going to client */
264 /* if no debug file, ship to bit bucket */
265 (void)close(STDERR_FILENO);
268 dbprintf(("%s: version %s\n", pgm, version()));
270 if(dbfd() != -1 && dbfd() != STDERR_FILENO)
272 if(dup2(dbfd(),STDERR_FILENO) != STDERR_FILENO)
274 perror("amidxtaped can't redirect stderr to the debug file");
275 dbprintf(("%s: can't redirect stderr to the debug file\n",
276 debug_prefix_time(NULL)));
281 if ((i = open("/dev/null", O_WRONLY)) == -1 ||
282 (i != STDERR_FILENO &&
283 (dup2(i, STDERR_FILENO) != STDERR_FILENO ||
285 perror("amidxtaped can't redirect stderr");
290 if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) {
291 dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
292 debug_prefix_time(NULL)));
296 if (getpeername(0, (struct sockaddr *)&addr, &i) == -1)
297 error("getpeername: %s", strerror(errno));
298 if (addr.sin_family != AF_INET || ntohs(addr.sin_port) == 20) {
299 error("connection rejected from %s family %d port %d",
300 inet_ntoa(addr.sin_addr), addr.sin_family, htons(addr.sin_port));
303 /* do the security thing */
305 buf = stralloc(get_client_line());
309 skip_whitespace(s, ch);
312 error("cannot parse SECURITY line");
315 skip_non_whitespace(s, ch);
317 if (strcmp(fp, "SECURITY") != 0)
319 error("cannot parse SECURITY line");
321 skip_whitespace(s, ch);
322 if (!security_ok(&addr, s-1, 0, &errstr)) {
323 error("security check failed: %s", errstr);
326 /* get the number of arguments */
330 buf = stralloc(get_client_line());
331 if(strncmp(buf, "LABEL=", 6) == 0) {
332 re_label = stralloc(buf+6);
334 else if(strncmp(buf, "FSF=", 4) == 0) {
335 int fsf = atoi(buf+4);
337 re_fsf = stralloc(buf+4);
340 else if(strncmp(buf, "HEADER", 6) == 0) {
343 else if(strncmp(buf, "DEVICE=", 7) == 0) {
344 re_device = stralloc(buf+7);
346 else if(strncmp(buf, "HOST=", 5) == 0) {
347 re_host = stralloc(buf+5);
349 else if(strncmp(buf, "DISK=", 5) == 0) {
350 re_disk = stralloc(buf+5);
352 else if(strncmp(buf, "DATESTAMP=", 10) == 0) {
353 re_datestamp = stralloc(buf+10);
355 else if(strncmp(buf, "END", 3) == 0) {
358 else if(strncmp(buf, "CONFIG=", 7) == 0) {
359 re_config = stralloc(buf+7);
361 else if(buf[0] != '\0' && buf[0] >= '0' && buf[0] <= '9') {
362 amrestore_nargs = atoi(buf);
367 } while (re_end == 0);
371 config_dir = vstralloc(CONFIG_DIR, "/", re_config, "/", NULL);
372 conffile = stralloc2(config_dir, CONFFILE_NAME);
373 if (read_conffile(conffile)) {
374 dbprintf(("%s: config '%s' not found\n",
375 debug_prefix_time(NULL), re_config));
386 if(re_device && re_config &&
387 strcmp(re_device, getconf_str(CNF_TAPEDEV)) == 0) {
388 get_lock = lock_logfile();
391 if(re_label && re_config &&
392 strcmp(re_device, getconf_str(CNF_AMRECOVER_CHANGER)) == 0) {
394 if(changer_init() == 0) {
395 dbprintf(("%s: No changer available\n",
396 debug_prefix_time(NULL)));
399 searchlabel = stralloc(re_label);
400 changer_find(scan_init, taperscan_slot, searchlabel);
402 dbprintf(("%s: Can't find label \"%s\"\n",
403 debug_prefix_time(NULL), searchlabel));
405 unlink(conf_logfile);
411 re_device=stralloc(found_device);
412 dbprintf(("%s: label \"%s\" found\n",
413 debug_prefix_time(NULL), searchlabel));
418 if(re_fsf && re_config && getconf_int(CNF_AMRECOVER_DO_FSF) == 0) {
421 if(re_label && re_config && getconf_int(CNF_AMRECOVER_CHECK_LABEL) == 0) {
425 dbprintf(("%s: amrestore_nargs=%d\n",
426 debug_prefix_time(NULL),
429 amrestore_args = (char **)alloc((amrestore_nargs+12)*sizeof(char *));
431 amrestore_args[i++] = "amrestore";
432 if(re_header || re_device || re_host || re_disk || re_datestamp ||
433 re_label || re_fsf) {
435 amrestore_args[i++] = "-p";
436 if(re_header) amrestore_args[i++] = "-h";
438 amrestore_args[i++] = "-l";
439 amrestore_args[i++] = re_label;
442 amrestore_args[i++] = "-f";
443 amrestore_args[i++] = re_fsf;
445 if(re_device) amrestore_args[i++] = re_device;
446 if(re_host) amrestore_args[i++] = re_host;
447 if(re_disk) amrestore_args[i++] = re_disk;
448 if(re_datestamp) amrestore_args[i++] = re_datestamp;
450 else { /* fe_amidxtaped_nargs */
451 while (i <= amrestore_nargs) {
452 amrestore_args[i++] = stralloc(get_client_line());
455 amrestore_args[i] = NULL;
457 amrestore_path = vstralloc(sbindir, "/", "amrestore", NULL);
459 /* so got all the arguments, now ready to execv */
460 dbprintf(("%s: Ready to execv amrestore with:\n", debug_prefix_time(NULL)));
461 dbprintf(("path = %s\n", amrestore_path));
462 for (i = 0; amrestore_args[i] != NULL; i++)
464 dbprintf(("argv[%d] = \"%s\"\n", i, amrestore_args[i]));
467 if ((pid = fork()) == 0)
470 (void)execv(amrestore_path, amrestore_args);
472 /* only get here if exec failed */
473 dbprintf(("%s: child could not exec %s: %s\n",
474 debug_prefix_time(NULL),
481 /* this is the parent */
484 dbprintf(("%s: error forking amrestore child: %s\n",
485 debug_prefix_time(NULL), strerror(errno)));
487 unlink(conf_logfile);
493 /* wait for the child to do the restore */
494 if (waitpid(pid, &status, 0) == -1)
496 dbprintf(("%s: error waiting for amrestore child: %s\n",
497 debug_prefix_time(NULL), strerror(errno)));
499 unlink(conf_logfile);
504 /* amrestore often sees the pipe reader (ie restore) quit in the middle
505 of the file because it has extracted all of the files needed. This
506 results in an exit status of 2. This unfortunately is the exit
507 status returned by many errors. Only when the exit status is 1 is it
508 guaranteed that an error existed. In all cases we should rewind the
509 tape if we can so that a retry starts from the correct place */
510 if (WIFEXITED(status) != 0)
513 dbprintf(("%s: amrestore terminated normally with status: %d\n",
514 debug_prefix_time(NULL), WEXITSTATUS(status)));
518 dbprintf(("%s: amrestore terminated abnormally.\n",
519 debug_prefix_time(NULL)));
524 tapename = re_device;
527 /* the first non-option argument is the tape device */
528 for (i = 1; i <= amrestore_nargs; i++)
529 if (amrestore_args[i][0] != '-')
531 if (i > amrestore_nargs) {
532 dbprintf(("%s: Couldn't find tape in arguments\n",
533 debug_prefix_time(NULL)));
535 unlink(conf_logfile);
541 tapename = stralloc(amrestore_args[i]);
543 if (tape_stat(tapename, &stat_tape) != 0) {
544 error("could not stat %s: %s", tapename, strerror(errno));
546 isafile = S_ISREG((stat_tape.st_mode));
550 dbprintf(("%s: rewinding tape ...\n", debug_prefix_time(NULL)));
551 errstr = tape_rewind(tapename);
553 if (errstr != NULL) {
554 dbprintf(("%s: %s\n", debug_prefix_time(NULL), errstr));
557 dbprintf(("%s: done\n", debug_prefix_time(NULL)));
562 unlink(conf_logfile);