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