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