Imported Upstream version 2.5.1
[debian/amanda] / restore-src / amidxtaped.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * All Rights Reserved.
5  *
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.
15  *
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.
22  *
23  * Authors: the Amanda Development Team.  Its members are listed in a
24  * file named AUTHORS, in the root directory of this distribution.
25  */
26 /* $Id: amidxtaped.c,v 1.73 2006/07/25 19:06:46 martinea Exp $
27  *
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
33  */
34
35 #include "amanda.h"
36 #include "version.h"
37 #include "clock.h"
38 #include "restore.h"
39
40 #include "changer.h"
41 #include "tapeio.h"
42 #include "conffile.h"
43 #include "logfile.h"
44 #include "amfeatures.h"
45 #include "stream.h"
46 #include "amandad.h"
47
48 #define TIMEOUT 30
49
50 static char *pgm = "amidxtaped";        /* in case argv[0] is not set */
51
52 extern char *rst_conf_logfile;
53 extern char *config_dir;
54
55 static int get_lock = 0;
56 static int from_amandad;
57
58 static am_feature_t *our_features = NULL;
59 static am_feature_t *their_features = NULL;
60 static g_option_t *g_options = NULL;
61 static int ctlfdin, ctlfdout, datafdout;
62 static char *amandad_auth = NULL;
63
64 static char *get_client_line(void);
65 static void check_security_buffer(char *);
66 static char *get_client_line_fd(int);
67
68 /* exit routine */
69 static pid_t parent_pid = -1;
70 static void cleanup(void);
71
72 int main(int argc, char **argv);
73
74 /* get a line from client - line terminated by \r\n */
75 static char *
76 get_client_line(void)
77 {
78     static char *line = NULL;
79     char *part = NULL;
80     size_t len;
81
82     amfree(line);
83     while(1) {
84         if((part = agets(stdin)) == NULL) {
85             if(errno != 0) {
86                 dbprintf(("%s: read error: %s\n",
87                           debug_prefix_time(NULL), strerror(errno)));
88             } else {
89                 dbprintf(("%s: EOF reached\n", debug_prefix_time(NULL)));
90             }
91             if(line) {
92                 dbprintf(("%s: unprocessed input:\n", debug_prefix_time(NULL)));
93                 dbprintf(("-----\n"));
94                 dbprintf(("%s\n", line));
95                 dbprintf(("-----\n"));
96             }
97             amfree(line);
98             amfree(part);
99             dbclose();
100             exit(1);
101             /*NOTREACHED*/
102         }
103         if(line) {
104             strappend(line, part);
105             amfree(part);
106         } else {
107             line = part;
108             part = NULL;
109         }
110         if((len = strlen(line)) > 0 && line[len-1] == '\r') {
111             line[len-1] = '\0';         /* zap the '\r' */
112             break;
113         }
114         /*
115          * Hmmm.  We got a "line" from agets(), which means it saw
116          * a '\n' (or EOF, etc), but there was not a '\r' before it.
117          * Put a '\n' back in the buffer and loop for more.
118          */
119         strappend(line, "\n");
120     }
121     dbprintf(("%s: > %s\n", debug_prefix_time(NULL), line));
122     return line;
123 }
124
125 /* get a line from client - line terminated by \r\n */
126 static char *
127 get_client_line_fd(
128     int         fd)
129 {
130     static char *line = NULL;
131     static size_t line_size = 0;
132     char *s = line;
133     size_t len = 0;
134     char c;
135     ssize_t nb;
136
137     if(line == NULL) { /* first time only, allocate initial buffer */
138         s = line = alloc(128);
139         line_size = 128;
140     }
141     while(1) {
142         nb = read(fd, &c, 1);
143         if (nb <= 0) {
144             /* EOF or error */
145             if ((nb <= 0) && ((errno == EINTR) || (errno == EAGAIN))) {
146                 /* Keep looping if failure is temporary */
147                 continue;
148             }
149             dbprintf(("%s: Control pipe read error - %s\n",
150                       pgm, strerror(errno)));
151             break;
152         }
153
154         if(len >= line_size-1) { /* increase buffer size */
155             line_size *= 2;
156             line = realloc(line, line_size);
157             if (line == NULL) {
158                 error("Memory reallocation failure");
159                 /*NOTREACHED*/
160             }
161             s = &line[len];
162         }
163         *s = c;
164         if(c == '\n') {
165             if(len > 0 && *(s-1) == '\r') { /* remove '\r' */
166                 s--;
167                 len--;
168             }
169             *s = '\0';
170             return line;
171         }
172         s++;
173         len++;
174     }
175     line[len] = '\0';
176     return line;
177 }
178
179
180 void
181 check_security_buffer(
182     char *      buffer)
183 {
184     socklen_t i;
185     struct sockaddr_in addr;
186     char *s, *fp, ch;
187     char *errstr = NULL;
188
189     dbprintf(("%s: check_security_buffer(buffer='%s')\n",
190                 debug_prefix(NULL), buffer));
191
192     i = SIZEOF(addr);
193     if (getpeername(0, (struct sockaddr *)&addr, &i) == -1) {
194         error("getpeername: %s", strerror(errno));
195         /*NOTREACHED*/
196     }
197     if ((addr.sin_family != (sa_family_t)AF_INET)
198                 || (ntohs(addr.sin_port) == 20)) {
199         error("connection rejected from %s family %d port %d",
200              inet_ntoa(addr.sin_addr), addr.sin_family, htons(addr.sin_port));
201         /*NOTREACHED*/
202     }
203
204     /* do the security thing */
205     s = buffer;
206     ch = *s++;
207
208     skip_whitespace(s, ch);
209     if (ch == '\0') {
210         error("cannot parse SECURITY line");
211         /*NOTREACHED*/
212     }
213     fp = s-1;
214     skip_non_whitespace(s, ch);
215     s[-1] = '\0';
216     if (strcmp(fp, "SECURITY") != 0) {
217         error("cannot parse SECURITY line");
218         /*NOTREACHED*/
219     }
220     skip_whitespace(s, ch);
221     if (!check_security(&addr, s-1, 0, &errstr)) {
222         error("security check failed: %s", errstr);
223         /*NOTREACHED*/
224     }
225 }
226
227 int
228 main(
229     int         argc,
230     char **     argv)
231 {
232     char *buf = NULL;
233     int data_sock = -1;
234     in_port_t data_port = (in_port_t)-1;
235     socklen_t socklen;
236     struct sockaddr_in addr;
237     match_list_t *match_list;
238     tapelist_t *tapes = NULL;
239     char *their_feature_string = NULL;
240     rst_flags_t *rst_flags;
241     int use_changer = 0;
242     FILE *prompt_stream = NULL;
243     int re_end;
244     char *re_config = NULL;
245     char *conf_tapetype;
246     tapetype_t *tape;
247     char *line;
248
249     safe_fd(DATA_FD_OFFSET, 4);
250     safe_cd();
251
252     /* Don't die when child closes pipe */
253     signal(SIGPIPE, SIG_IGN);
254
255     rst_flags = new_rst_flags();
256     rst_flags->mask_splits = 1; /* for older clients */
257     rst_flags->amidxtaped = 1;
258     our_features = am_init_feature_set();
259     their_features = am_set_default_feature_set();
260
261     /*
262      * When called via inetd, it is not uncommon to forget to put the
263      * argv[0] value on the config line.  On some systems (e.g. Solaris)
264      * this causes argv and/or argv[0] to be NULL, so we have to be
265      * careful getting our name.
266      */
267     if (argc >= 1 && argv != NULL && argv[0] != NULL) {
268         if((pgm = strrchr(argv[0], '/')) != NULL) {
269             pgm++;
270         } else {
271             pgm = argv[0];
272         }
273     }
274
275     set_pname(pgm);
276
277     if(argv && argv[1] && strcmp(argv[1], "amandad") == 0) {
278         from_amandad = 1;
279         if(argv[2])
280             amandad_auth = argv[2];
281     }
282     else {
283         from_amandad = 0;
284         safe_fd(-1, 0);
285     }
286
287 #ifdef FORCE_USERID
288
289     /* we'd rather not run as root */
290
291     if(geteuid() == 0) {
292         if(client_uid == (uid_t) -1) {
293             error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
294             /*NOTREACHED*/
295         }
296
297         /*@ignore@*/
298         initgroups(CLIENT_LOGIN, client_gid);
299         /*@end@*/
300         setgid(client_gid);
301         setuid(client_uid);
302     }
303
304 #endif  /* FORCE_USERID */
305
306     /* initialize */
307     /* close stderr first so that debug file becomes it - amrestore
308        chats to stderr, which we don't want going to client */
309     /* if no debug file, ship to bit bucket */
310     (void)close(STDERR_FILENO);
311     dbopen(DBG_SUBDIR_SERVER);
312     startclock();
313     dbprintf(("%s: version %s\n", pgm, version()));
314 #ifdef DEBUG_CODE
315     if(dbfd() != -1 && dbfd() != STDERR_FILENO)
316     {
317         if(dup2(dbfd(),STDERR_FILENO) != STDERR_FILENO)
318         {
319             perror("amidxtaped can't redirect stderr to the debug file");
320             dbprintf(("%s: can't redirect stderr to the debug file\n",
321                       debug_prefix_time(NULL)));
322             return 1;
323         }
324     }
325 #else
326     if ((i = open("/dev/null", O_WRONLY)) == -1 ||
327         (i != STDERR_FILENO &&
328          (dup2(i, STDERR_FILENO) != STDERR_FILENO ||
329           close(i) != 0))) {
330         perror("amidxtaped can't redirect stderr");
331         return 1;
332     }
333 #endif
334
335     if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) {
336         dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
337                   debug_prefix_time(NULL)));
338     }
339
340     if(from_amandad == 0) {
341         socklen = SIZEOF(addr);
342         if (getpeername(0, (struct sockaddr *)&addr, &socklen) == -1) {
343             error("getpeername: %s", strerror(errno));
344             /*NOTREACHED*/
345         }
346         if ((addr.sin_family != (sa_family_t)AF_INET)
347                 || (ntohs(addr.sin_port) == 20)) {
348             error("connection rejected from %s family %d port %d",
349                   inet_ntoa(addr.sin_addr), addr.sin_family,
350                   htons(addr.sin_port));
351             /*NOTREACHED*/
352         }
353
354         /* do the security thing */
355         amfree(buf);
356         buf = stralloc(get_client_line());
357         check_security_buffer(buf);
358     }
359     else {
360         ctlfdout  = DATA_FD_OFFSET + 0;
361         ctlfdin   = DATA_FD_OFFSET + 1;
362         datafdout = DATA_FD_OFFSET + 2;
363         close(DATA_FD_OFFSET +3);
364
365         /* read the REQ packet */
366         for(; (line = agets(stdin)) != NULL; free(line)) {
367 #define sc "OPTIONS "
368             if(strncmp(line, sc, sizeof(sc)-1) == 0) {
369 #undef sc
370                 g_options = parse_g_options(line+8, 1);
371                 if(!g_options->hostname) {
372                     g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
373                     gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
374                     g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
375                 }
376             }
377         }
378         amfree(line);
379
380         if(amandad_auth && g_options->auth) {
381             if(strcasecmp(amandad_auth, g_options->auth) != 0) {
382                 printf("ERROR recover program ask for auth=%s while amidxtaped is configured for '%s'\n",
383                        g_options->auth, amandad_auth);
384                 error("ERROR recover program ask for auth=%s while amidxtaped is configured for '%s'",
385                       g_options->auth, amandad_auth);
386                 /*NOTREACHED*/
387             }
388         }
389         /* send the REP packet */
390         printf("CONNECT CTL %d DATA %d\n", DATA_FD_OFFSET, DATA_FD_OFFSET+1);
391         printf("\n");
392         fflush(stdout);
393         fflush(stdin);
394         if ((dup2(ctlfdout, fileno(stdout)) < 0)
395                  || (dup2(ctlfdin, fileno(stdin)) < 0)) {
396             error("amandad: Failed to setup stdin or stdout");
397             /*NOTREACHED*/
398         }
399     }
400
401     /* get the number of arguments */
402     match_list = alloc(SIZEOF(match_list_t));
403     match_list->next = NULL;
404     match_list->hostname = "";
405     match_list->datestamp = "";
406     match_list->level = "";
407     match_list->diskname = "";
408
409     for (re_end = 0; re_end == 0; ) {
410         amfree(buf);
411         buf = stralloc(get_client_line());
412         if(strncmp(buf, "LABEL=", 6) == 0) {
413             tapes = unmarshal_tapelist_str(buf+6);
414         }
415         else if(strncmp(buf, "FSF=", 4) == 0) {
416             rst_flags->fsf = OFF_T_ATOI(buf + 4);
417         }
418         else if(strncmp(buf, "HEADER", 6) == 0) {
419             rst_flags->headers = 1;
420         }
421         else if(strncmp(buf, "FEATURES=", 9) == 0) {
422             char *our_feature_string = NULL;
423             their_feature_string = stralloc(buf+9);
424             am_release_feature_set(their_features);
425             their_features = am_string_to_feature(their_feature_string);
426             amfree(their_feature_string);
427             our_feature_string = am_feature_to_string(our_features);
428             if(from_amandad == 1) 
429                 printf("FEATURES=%s\r\n", our_feature_string);
430             else
431                 printf("%s", our_feature_string);
432             fflush(stdout);
433             amfree(our_feature_string);
434         }
435         else if(strncmp(buf, "DEVICE=", 7) == 0) {
436             rst_flags->alt_tapedev= stralloc(buf+7);
437         }
438         else if(strncmp(buf, "HOST=", 5) == 0) {
439             match_list->hostname = stralloc(buf+5);
440         }
441         else if(strncmp(buf, "DISK=", 5) == 0) {
442             match_list->diskname = stralloc(buf+5);
443         }
444         else if(strncmp(buf, "DATESTAMP=", 10) == 0) {
445             match_list->datestamp = stralloc(buf+10);
446         }
447         else if(strncmp(buf, "END", 3) == 0) {
448             re_end = 1;
449         }
450         else if(strncmp(buf, "CONFIG=", 7) == 0) {
451             re_config = stralloc(buf+7);
452         }
453         else if(buf[0] != '\0' && buf[0] >= '0' && buf[0] <= '9') {
454 /* XXX does nothing?     amrestore_nargs = atoi(buf); */
455             re_end = 1;
456         }
457     }
458     amfree(buf);
459
460     if(!tapes && rst_flags->alt_tapedev){
461         dbprintf(("%s: Looks like we're restoring from a holding file...\n", debug_prefix_time(NULL)));
462         tapes = unmarshal_tapelist_str(rst_flags->alt_tapedev);
463         tapes->isafile = 1;
464         amfree(rst_flags->alt_tapedev);
465         rst_flags->alt_tapedev = NULL;
466     }
467
468     if(re_config) {
469         char *conffile;
470         config_dir = vstralloc(CONFIG_DIR, "/", re_config, "/", NULL);
471         conffile = stralloc2(config_dir, CONFFILE_NAME);
472         if (read_conffile(conffile)) {
473             dbprintf(("%s: config '%s' not found\n",
474                       debug_prefix_time(NULL), re_config));
475             amfree(re_config);
476             re_config = NULL;
477         }
478         amfree(conffile);
479
480         dbrename(config_name, DBG_SUBDIR_SERVER);
481     }
482
483     if(tapes &&
484        (!rst_flags->alt_tapedev  ||
485         (re_config && ( strcmp(rst_flags->alt_tapedev,
486                                getconf_str(CNF_AMRECOVER_CHANGER)) == 0 ||
487                         strcmp(rst_flags->alt_tapedev,
488                                getconf_str(CNF_TPCHANGER)) == 0 ) ) ) ) {
489         /* We need certain options, if restoring from more than one tape */
490         if(tapes->next && !am_has_feature(their_features, fe_recover_splits)) {
491             error("%s: Client must support split dumps to restore requested data.",  get_pname());
492             /*NOTREACHED*/
493         }
494         dbprintf(("%s: Restoring from changer, checking labels\n", get_pname()));
495         rst_flags->check_labels = 1;
496         use_changer = 1;
497     }
498
499     /* If we'll be stepping on the tape server's devices, lock them. */
500     if(re_config &&
501        (use_changer || (rst_flags->alt_tapedev &&
502                         strcmp(rst_flags->alt_tapedev,
503                                getconf_str(CNF_TAPEDEV)) == 0) ) ) {
504         dbprintf(("%s: Locking devices\n", get_pname()));
505         parent_pid = getpid();
506         atexit(cleanup);
507         get_lock = lock_logfile();
508     }
509
510     /* Init the tape changer */
511     if(tapes && use_changer && changer_init() == 0) {
512         dbprintf(("%s: No changer available\n", debug_prefix_time(NULL)));
513     }
514
515     /* Read the default block size from the tape type */
516     if(re_config && (conf_tapetype = getconf_str(CNF_TAPETYPE)) != NULL) {
517         tape = lookup_tapetype(conf_tapetype);
518         rst_flags->blocksize = tapetype_get_blocksize(tape) * 1024;
519     }
520
521     if(rst_flags->fsf && re_config &&
522        getconf_boolean(CNF_AMRECOVER_DO_FSF) == 0) {
523         rst_flags->fsf = (off_t)0;
524     }
525
526     if (!use_changer && re_config &&
527         getconf_boolean(CNF_AMRECOVER_CHECK_LABEL) == 0) {
528         rst_flags->check_labels = 0;
529     }
530
531     /* establish a distinct data connection for dumpfile data */
532     if(am_has_feature(their_features, fe_recover_splits)) {
533         if(from_amandad == 1) {
534             rst_flags->pipe_to_fd = datafdout;
535             prompt_stream = stdout;
536         }
537         else {
538             int data_fd;
539             char *buf;
540
541             dbprintf(("%s: Client understands split dumpfiles\n",get_pname()));
542
543             if((data_sock = stream_server(&data_port, STREAM_BUFSIZE, 
544                  STREAM_BUFSIZE, 0)) < 0){
545                 error("%s: could not create data socket: %s", get_pname(),
546                       strerror(errno));
547                 /*NOTREACHED*/
548             }
549             dbprintf(("%s: Local port %d set aside for data\n", get_pname(),                         data_port));
550
551             /* tell client where to connect */
552             printf("CONNECT %hu\n", (unsigned short)data_port);
553             fflush(stdout);
554
555             if((data_fd = stream_accept(data_sock, TIMEOUT, STREAM_BUFSIZE, 
556                  STREAM_BUFSIZE)) < 0){
557                 error("stream_accept failed for client data connection: %s\n",
558                       strerror(errno));
559                 /*NOTREACHED*/
560             }
561
562             buf = get_client_line_fd(data_fd);
563
564             check_security_buffer(buf);
565             rst_flags->pipe_to_fd = data_fd;
566             prompt_stream = stdout;
567         }
568     }
569     else {
570         rst_flags->pipe_to_fd = fileno(stdout);
571         prompt_stream = stderr;
572     }
573     dbprintf(("%s: Sending output to file descriptor %d\n",
574               get_pname(), rst_flags->pipe_to_fd));
575
576
577     if(get_lock == 0 &&
578        re_config && 
579        (use_changer || (rst_flags->alt_tapedev &&
580                         strcmp(rst_flags->alt_tapedev,
581                                getconf_str(CNF_TAPEDEV)) == 0) ) ) {
582         send_message(prompt_stream, rst_flags, their_features,
583                      "%s exists: amdump or amflush is already running, "
584                      "or you must run amcleanup", 
585                      rst_conf_logfile);
586         error("%s exists: amdump or amflush is already running, "
587               "or you must run amcleanup",
588               rst_conf_logfile);
589     }
590
591     /* make sure our restore flags aren't crazy */
592     if (check_rst_flags(rst_flags) == -1) {
593         if (rst_flags->pipe_to_fd != -1)
594             aclose(rst_flags->pipe_to_fd);
595         send_message(prompt_stream, rst_flags, their_features,
596                      "restore flags are crazy");
597         exit(1);
598     }
599
600     /* actual restoration */
601     search_tapes(prompt_stream, use_changer, tapes, match_list, rst_flags,
602                  their_features);
603     dbprintf(("%s: Restoration finished\n", debug_prefix_time(NULL)));
604
605     /* cleanup */
606     if(rst_flags->pipe_to_fd != -1) aclose(rst_flags->pipe_to_fd);
607     free_tapelist(tapes);
608
609     am_release_feature_set(their_features);
610
611     amfree(rst_flags->alt_tapedev);
612     amfree(rst_flags);
613     amfree(match_list->hostname);
614     amfree(match_list->diskname);
615     amfree(match_list->datestamp);
616     amfree(match_list);
617     amfree(config_dir);
618     amfree(re_config);
619     dbclose();
620     return 0;
621 }
622
623 static void
624 cleanup(void)
625 {
626     if(parent_pid == getpid()) {
627         if(get_lock) unlink(rst_conf_logfile);
628     }
629 }