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.58 2006/03/14 13:12:01 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 #include "amfeatures.h"
49 static char *pgm = "amidxtaped"; /* in case argv[0] is not set */
51 extern char *rst_conf_logdir;
52 extern char *rst_conf_logfile;
53 extern char *config_dir;
55 static int get_lock = 0;
57 static am_feature_t *our_features = NULL;
58 static am_feature_t *their_features = NULL;
60 static char *get_client_line P((void));
61 static char *get_client_line_fd P((int));
62 static void check_security_buffer P((char*));
65 static int parent_pid = -1;
66 static void cleanup P((void));
68 /* get a line from client - line terminated by \r\n */
72 static char *line = NULL;
78 if((part = agets(stdin)) == NULL) {
80 dbprintf(("%s: read error: %s\n",
81 debug_prefix_time(NULL), strerror(errno)));
83 dbprintf(("%s: EOF reached\n", debug_prefix_time(NULL)));
86 dbprintf(("%s: unprocessed input:\n", debug_prefix_time(NULL)));
87 dbprintf(("-----\n"));
88 dbprintf(("%s\n", line));
89 dbprintf(("-----\n"));
98 strappend(line, part);
104 if((len = strlen(line)) > 0 && line[len-1] == '\r') {
105 line[len-1] = '\0'; /* zap the '\r' */
109 * Hmmm. We got a "line" from agets(), which means it saw
110 * a '\n' (or EOF, etc), but there was not a '\r' before it.
111 * Put a '\n' back in the buffer and loop for more.
113 strappend(line, "\n");
115 dbprintf(("%s: > %s\n", debug_prefix_time(NULL), line));
119 /* get a line from client - line terminated by \r\n */
121 get_client_line_fd(fd)
124 static char *line = NULL;
125 static int line_size = 0;
131 if(line == NULL) { /* first time only, allocate initial buffer */
132 s = line = malloc(128);
136 nb = read(fd, &c, 1);
139 if ((nb <= 0) && ((errno == EINTR) || (errno == EAGAIN))) {
140 /* Keep looping if failure is temporary */
143 dbprintf(("%s: Control pipe read error - %s\n",
144 pgm, strerror(errno)));
148 if(len >= line_size-1) { /* increase buffer size */
150 line = realloc(line, line_size);
152 error("Memory reallocation failure");
159 if(len > 0 && *(s-1) == '\r') { /* remove '\r' */
174 void check_security_buffer(buffer)
178 struct sockaddr_in addr;
183 if (getpeername(0, (struct sockaddr *)&addr, &i) == -1)
184 error("getpeername: %s", strerror(errno));
185 if (addr.sin_family != AF_INET || ntohs(addr.sin_port) == 20) {
186 error("connection rejected from %s family %d port %d",
187 inet_ntoa(addr.sin_addr), addr.sin_family, htons(addr.sin_port));
190 /* do the security thing */
194 skip_whitespace(s, ch);
196 error("cannot parse SECURITY line");
199 skip_non_whitespace(s, ch);
201 if (strcmp(fp, "SECURITY") != 0) {
202 error("cannot parse SECURITY line");
204 skip_whitespace(s, ch);
205 if (!check_security(&addr, s-1, 0, &errstr)) {
206 error("security check failed: %s", errstr);
215 int data_sock = -1, data_port = -1;
217 struct sockaddr_in addr;
218 match_list_t *match_list;
219 tapelist_t *tapes = NULL;
220 char *their_feature_string = NULL;
221 rst_flags_t *rst_flags;
223 FILE *prompt_stream = NULL;
225 char *re_config = NULL;
232 /* Don't die when child closes pipe */
233 signal(SIGPIPE, SIG_IGN);
235 rst_flags = new_rst_flags();
236 rst_flags->mask_splits = 1; /* for older clients */
237 rst_flags->amidxtaped = 1;
238 our_features = am_init_feature_set();
239 their_features = am_set_default_feature_set();
242 * When called via inetd, it is not uncommon to forget to put the
243 * argv[0] value on the config line. On some systems (e.g. Solaris)
244 * this causes argv and/or argv[0] to be NULL, so we have to be
245 * careful getting our name.
247 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
248 if((pgm = strrchr(argv[0], '/')) != NULL) {
259 /* we'd rather not run as root */
262 if(client_uid == (uid_t) -1) {
263 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
266 initgroups(CLIENT_LOGIN, client_gid);
271 #endif /* FORCE_USERID */
274 /* close stderr first so that debug file becomes it - amrestore
275 chats to stderr, which we don't want going to client */
276 /* if no debug file, ship to bit bucket */
277 (void)close(STDERR_FILENO);
280 dbprintf(("%s: version %s\n", pgm, version()));
282 if(dbfd() != -1 && dbfd() != STDERR_FILENO)
284 if(dup2(dbfd(),STDERR_FILENO) != STDERR_FILENO)
286 perror("amidxtaped can't redirect stderr to the debug file");
287 dbprintf(("%s: can't redirect stderr to the debug file\n",
288 debug_prefix_time(NULL)));
293 if ((i = open("/dev/null", O_WRONLY)) == -1 ||
294 (i != STDERR_FILENO &&
295 (dup2(i, STDERR_FILENO) != STDERR_FILENO ||
297 perror("amidxtaped can't redirect stderr");
302 if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) {
303 dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
304 debug_prefix_time(NULL)));
307 socklen = sizeof (addr);
308 if (getpeername(0, (struct sockaddr *)&addr, &socklen) == -1)
309 error("getpeername: %s", strerror(errno));
310 if (addr.sin_family != AF_INET || ntohs(addr.sin_port) == 20) {
311 error("connection rejected from %s family %d port %d",
312 inet_ntoa(addr.sin_addr), addr.sin_family, htons(addr.sin_port));
315 /* do the security thing */
317 buf = stralloc(get_client_line());
318 check_security_buffer(buf);
320 /* get the number of arguments */
321 match_list = alloc(sizeof(match_list_t));
322 match_list->next = NULL;
323 match_list->hostname = "";
324 match_list->datestamp = "";
325 match_list->level = "";
326 match_list->diskname = "";
330 buf = stralloc(get_client_line());
331 if(strncmp(buf, "LABEL=", 6) == 0) {
332 tapes = unmarshal_tapelist_str(buf+6);
334 else if(strncmp(buf, "FSF=", 4) == 0) {
335 rst_flags->fsf = atoi(buf + 4);
337 else if(strncmp(buf, "HEADER", 6) == 0) {
338 rst_flags->headers = 1;
340 else if(strncmp(buf, "FEATURES=", 9) == 0) {
341 char *our_feature_string = NULL;
342 their_feature_string = stralloc(buf+9);
343 am_release_feature_set(their_features);
344 their_features = am_string_to_feature(their_feature_string);
345 amfree(their_feature_string);
346 our_feature_string = am_feature_to_string(our_features);
347 printf("%s", our_feature_string);
349 amfree(our_feature_string);
351 else if(strncmp(buf, "DEVICE=", 7) == 0) {
352 rst_flags->alt_tapedev= stralloc(buf+7);
354 else if(strncmp(buf, "HOST=", 5) == 0) {
355 match_list->hostname = stralloc(buf+5);
357 else if(strncmp(buf, "DISK=", 5) == 0) {
358 match_list->diskname = stralloc(buf+5);
360 else if(strncmp(buf, "DATESTAMP=", 10) == 0) {
361 match_list->datestamp = stralloc(buf+10);
363 else if(strncmp(buf, "END", 3) == 0) {
366 else if(strncmp(buf, "CONFIG=", 7) == 0) {
367 re_config = stralloc(buf+7);
369 else if(buf[0] != '\0' && buf[0] >= '0' && buf[0] <= '9') {
370 /* XXX does nothing? amrestore_nargs = atoi(buf); */
375 } while (re_end == 0);
378 if(!tapes && rst_flags->alt_tapedev){
379 dbprintf(("%s: Looks like we're restoring from a holding file...\n", debug_prefix_time(NULL)));
380 tapes = unmarshal_tapelist_str(rst_flags->alt_tapedev);
382 amfree(rst_flags->alt_tapedev);
383 rst_flags->alt_tapedev = NULL;
388 config_dir = vstralloc(CONFIG_DIR, "/", re_config, "/", NULL);
389 conffile = stralloc2(config_dir, CONFFILE_NAME);
390 if (read_conffile(conffile)) {
391 dbprintf(("%s: config '%s' not found\n",
392 debug_prefix_time(NULL), re_config));
400 (!rst_flags->alt_tapedev ||
401 (re_config && ( strcmp(rst_flags->alt_tapedev,
402 getconf_str(CNF_AMRECOVER_CHANGER)) == 0 ||
403 strcmp(rst_flags->alt_tapedev,
404 getconf_str(CNF_TPCHANGER)) == 0 ) ) ) ) {
405 /* We need certain options, if restoring from more than one tape */
406 if(tapes->next && !am_has_feature(their_features, fe_recover_splits)) {
407 error("%s: Client must support split dumps to restore requested data.", get_pname());
410 dbprintf(("%s: Restoring from changer, checking labels\n", get_pname()));
411 rst_flags->check_labels = 1;
415 /* If we'll be stepping on the tape server's devices, lock them. */
417 (use_changer || (rst_flags->alt_tapedev &&
418 strcmp(rst_flags->alt_tapedev,
419 getconf_str(CNF_TAPEDEV)) == 0) ) ) {
420 dbprintf(("%s: Locking devices\n", get_pname()));
421 parent_pid = getpid();
423 get_lock = lock_logfile();
426 /* Init the tape changer */
427 if(tapes && use_changer && changer_init() == 0) {
428 dbprintf(("%s: No changer available\n", debug_prefix_time(NULL)));
431 /* Read the default block size from the tape type */
432 if(re_config && (conf_tapetype = getconf_str(CNF_TAPETYPE)) != NULL) {
433 tape = lookup_tapetype(conf_tapetype);
434 rst_flags->blocksize = tape->blocksize * 1024;
437 if(rst_flags->fsf && re_config &&
438 getconf_int(CNF_AMRECOVER_DO_FSF) == 0) {
442 if(re_config && getconf_int(CNF_AMRECOVER_CHECK_LABEL) == 0) {
443 rst_flags->check_labels = 0;
446 /* establish a distinct data connection for dumpfile data */
447 if(am_has_feature(their_features, fe_recover_splits)){
451 dbprintf(("%s: Client understands split dumpfiles\n", get_pname()));
453 if((data_sock = stream_server(&data_port, STREAM_BUFSIZE, -1)) < 0){
454 error("%s: could not create data socket: %s", get_pname(),
457 dbprintf(("%s: Local port %d set aside for data\n", get_pname(),
460 printf("CONNECT %d\n", data_port); /* tell client where to connect */
463 if((data_fd = stream_accept(data_sock, TIMEOUT, -1, -1)) < 0){
464 error("stream_accept failed for client data connection: %s\n",
468 buf = get_client_line_fd(data_fd);
470 check_security_buffer(buf);
471 rst_flags->pipe_to_fd = data_fd;
472 prompt_stream = stdout;
475 rst_flags->pipe_to_fd = fileno(stdout);
476 prompt_stream = stderr;
478 dbprintf(("%s: Sending output to file descriptor %d\n",
479 get_pname(), rst_flags->pipe_to_fd));
482 /* make sure our restore flags aren't crazy */
483 if(check_rst_flags(rst_flags) == -1){
484 if(rst_flags->pipe_to_fd != -1) aclose(rst_flags->pipe_to_fd);
488 /* actual restoration */
489 search_tapes(prompt_stream, use_changer, tapes, match_list, rst_flags,
491 dbprintf(("%s: Restoration finished\n", debug_prefix_time(NULL)));
494 if(rst_flags->pipe_to_fd != -1) aclose(rst_flags->pipe_to_fd);
495 free_tapelist(tapes);
497 am_release_feature_set(their_features);
499 amfree(rst_flags->alt_tapedev);
501 amfree(match_list->hostname);
502 amfree(match_list->diskname);
503 amfree(match_list->datestamp);
514 if(parent_pid == getpid()) {
515 if(get_lock) unlink(rst_conf_logfile);