9f9e62ad139655560d67d8f0e2f6f97ca4ebf022
[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.25.2.3.4.1.2.11 2004/01/29 19:26:51 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 "clock.h"
37 #include "version.h"
38
39 #include "changer.h"
40 #include "tapeio.h"
41 #include "conffile.h"
42 #include "logfile.h"
43
44 static char *pgm = "amidxtaped";        /* in case argv[0] is not set */
45
46 char *conf_logdir = NULL;
47 char *conf_logfile = NULL;
48 int get_lock = 0;
49 char *found_device = NULL;
50
51 static char *get_client_line P((void));
52
53 /* get a line from client - line terminated by \r\n */
54 static char *
55 get_client_line()
56 {
57     static char *line = NULL;
58     char *part = NULL;
59     int len;
60
61     amfree(line);
62     while(1) {
63         if((part = agets(stdin)) == NULL) {
64             if(errno != 0) {
65                 dbprintf(("%s: read error: %s\n",
66                           debug_prefix_time(NULL), strerror(errno)));
67             } else {
68                 dbprintf(("%s: EOF reached\n", debug_prefix_time(NULL)));
69             }
70             if(line) {
71                 dbprintf(("%s: unprocessed input:\n", debug_prefix_time(NULL)));
72                 dbprintf(("-----\n"));
73                 dbprintf(("%s\n", line));
74                 dbprintf(("-----\n"));
75             }
76             amfree(line);
77             amfree(part);
78             if(get_lock) {
79                 unlink(conf_logfile);
80             }
81             dbclose();
82             exit(1);
83             /* NOTREACHED */
84         }
85         if(line) {
86             strappend(line, part);
87             amfree(part);
88         } else {
89             line = part;
90             part = NULL;
91         }
92         if((len = strlen(line)) > 0 && line[len-1] == '\r') {
93             line[len-1] = '\0';         /* zap the '\r' */
94             break;
95         }
96         /*
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.
100          */
101         strappend(line, "\n");
102     }
103     dbprintf(("%s: > %s\n", debug_prefix_time(NULL), line));
104     return line;
105 }
106
107 int found = 0;
108 char *searchlabel = NULL;
109 int nslots, backwards;
110
111 int scan_init(rc, ns, bk)
112 int rc, ns, bk;
113 {
114     if(rc) {
115         if(get_lock) {
116             unlink(conf_logfile);
117         }
118         error("could not get changer info: %s", changer_resultstr);
119     }
120     nslots = ns;
121     backwards = bk;
122
123     return 0;
124 }
125
126
127 int taperscan_slot(rc, slotstr, device)
128 int rc;
129 char *slotstr;
130 char *device;
131 {
132     char *errstr;
133     char *datestamp = NULL, *label = NULL;
134
135     if(rc == 2) {
136         dbprintf(("%s: fatal slot %s: %s\n", debug_prefix_time(NULL),
137                   slotstr, changer_resultstr));
138         return 1;
139     }
140     else if(rc == 1) {
141         dbprintf(("%s: slot %s: %s\n", debug_prefix_time(NULL),
142                   slotstr, changer_resultstr));
143         return 0;
144     }
145     else {
146         if((errstr = tape_rdlabel(device, &datestamp, &label)) != NULL) {
147             dbprintf(("%s: slot %s: %s\n", debug_prefix_time(NULL),
148                       slotstr, errstr));
149         } else {
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"));
158                 found = 1;
159                 return 1;
160             }
161             else {
162                 dbprintf((" (no match)\n"));
163             }
164         }
165     }
166     return 0;
167 }
168
169
170 int lock_logfile()
171 {
172     conf_logdir = getconf_str(CNF_LOGDIR);
173     if (*conf_logdir == '/') {
174         conf_logdir = stralloc(conf_logdir);
175     } else {
176         conf_logdir = stralloc2(config_dir, conf_logdir);
177     }
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);
181     }
182     log_add(L_INFO, "amidxtaped");
183     return 1;
184 }
185
186
187 int main(argc, argv)
188 int argc;
189 char **argv;
190 {
191     int amrestore_nargs;
192     char **amrestore_args;
193     char *buf = NULL;
194     int i;
195     char *amrestore_path;
196     pid_t pid;
197     int isafile;
198     struct stat stat_tape;
199     char *tapename = NULL;
200     int fd;
201     char *s, *fp;
202     int ch;
203     char *errstr = NULL;
204     struct sockaddr_in addr;
205     amwait_t status;
206
207     int re_header = 0;
208     int re_end = 0;
209     char *re_label = NULL;
210     char *re_fsf = 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;
216
217     for(fd = 3; fd < FD_SETSIZE; fd++) {
218         /*
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).
223          */
224         close(fd);
225     }
226
227     safe_cd();
228
229     /*
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.
234      */
235     if (argc >= 1 && argv != NULL && argv[0] != NULL) {
236         if((pgm = strrchr(argv[0], '/')) != NULL) {
237             pgm++;
238         } else {
239             pgm = argv[0];
240         }
241     }
242
243     set_pname(pgm);
244
245 #ifdef FORCE_USERID
246
247     /* we'd rather not run as root */
248
249     if(geteuid() == 0) {
250         if(client_uid == (uid_t) -1) {
251             error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
252         }
253
254         initgroups(CLIENT_LOGIN, client_gid);
255         setgid(client_gid);
256         setuid(client_uid);
257     }
258
259 #endif  /* FORCE_USERID */
260
261     /* initialize */
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);
266     dbopen();
267     startclock();
268     dbprintf(("%s: version %s\n", pgm, version()));
269 #ifdef DEBUG_CODE
270     if(dbfd() != -1 && dbfd() != STDERR_FILENO)
271     {
272         if(dup2(dbfd(),STDERR_FILENO) != STDERR_FILENO)
273         {
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)));
277             return 1;
278         }
279     }
280 #else
281     if ((i = open("/dev/null", O_WRONLY)) == -1 ||
282         (i != STDERR_FILENO &&
283          (dup2(i, STDERR_FILENO) != STDERR_FILENO ||
284           close(i) != 0))) {
285         perror("amidxtaped can't redirect stderr");
286         return 1;
287     }
288 #endif
289
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)));
293     }
294
295     i = sizeof (addr);
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));
301     }
302
303     /* do the security thing */
304     amfree(buf);
305     buf = stralloc(get_client_line());
306     s = buf;
307     ch = *s++;
308
309     skip_whitespace(s, ch);
310     if (ch == '\0')
311     {
312         error("cannot parse SECURITY line");
313     }
314     fp = s-1;
315     skip_non_whitespace(s, ch);
316     s[-1] = '\0';
317     if (strcmp(fp, "SECURITY") != 0)
318     {
319         error("cannot parse SECURITY line");
320     }
321     skip_whitespace(s, ch);
322     if (!security_ok(&addr, s-1, 0, &errstr)) {
323         error("security check failed: %s", errstr);
324     }
325
326     /* get the number of arguments */
327     amrestore_nargs = 0;
328     do {
329         amfree(buf);
330         buf = stralloc(get_client_line());
331         if(strncmp(buf, "LABEL=", 6) == 0) {
332             re_label = stralloc(buf+6);
333         }
334         else if(strncmp(buf, "FSF=", 4) == 0) {
335             int fsf = atoi(buf+4);
336             if(fsf > 0) {
337                 re_fsf = stralloc(buf+4);
338             }
339         }
340         else if(strncmp(buf, "HEADER", 6) == 0) {
341             re_header = 1;
342         }
343         else if(strncmp(buf, "DEVICE=", 7) == 0) {
344             re_device = stralloc(buf+7);
345         }
346         else if(strncmp(buf, "HOST=", 5) == 0) {
347             re_host = stralloc(buf+5);
348         }
349         else if(strncmp(buf, "DISK=", 5) == 0) {
350             re_disk = stralloc(buf+5);
351         }
352         else if(strncmp(buf, "DATESTAMP=", 10) == 0) {
353             re_datestamp = stralloc(buf+10);
354         }
355         else if(strncmp(buf, "END", 3) == 0) {
356             re_end = 1;
357         }
358         else if(strncmp(buf, "CONFIG=", 7) == 0) {
359             re_config = stralloc(buf+7);
360         }
361         else if(buf[0] != '\0' && buf[0] >= '0' && buf[0] <= '9') {
362             amrestore_nargs = atoi(buf);
363             re_end = 1;
364         }
365         else {
366         }
367     } while (re_end == 0);
368
369     if(re_config) {
370         char *conffile;
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));
376             amfree(re_fsf);
377             amfree(re_label);
378             amfree(re_config);
379         }
380     }
381     else {
382         amfree(re_fsf);
383         amfree(re_label);
384     }
385
386     if(re_device && re_config &&
387        strcmp(re_device, getconf_str(CNF_TAPEDEV)) == 0) {
388         get_lock = lock_logfile();
389     }
390
391     if(re_label && re_config &&
392        strcmp(re_device, getconf_str(CNF_AMRECOVER_CHANGER)) == 0) {
393
394         if(changer_init() == 0) {
395             dbprintf(("%s: No changer available\n",
396                        debug_prefix_time(NULL)));
397         }
398         else {
399             searchlabel = stralloc(re_label);
400             changer_find(scan_init, taperscan_slot, searchlabel);
401             if(found == 0) {
402                 dbprintf(("%s: Can't find label \"%s\"\n",
403                           debug_prefix_time(NULL), searchlabel));
404                 if(get_lock) {
405                     unlink(conf_logfile);
406                 }
407                 dbclose();
408                 exit(1);
409             }
410             else {
411                      re_device=stralloc(found_device);
412                 dbprintf(("%s: label \"%s\" found\n",
413                           debug_prefix_time(NULL), searchlabel));
414             }
415         }
416     }
417
418     if(re_fsf && re_config && getconf_int(CNF_AMRECOVER_DO_FSF) == 0) {
419         amfree(re_fsf);
420     }
421     if(re_label && re_config && getconf_int(CNF_AMRECOVER_CHECK_LABEL) == 0) {
422         amfree(re_label);
423     }
424
425     dbprintf(("%s: amrestore_nargs=%d\n",
426               debug_prefix_time(NULL),
427               amrestore_nargs));
428
429     amrestore_args = (char **)alloc((amrestore_nargs+12)*sizeof(char *));
430     i = 0;
431     amrestore_args[i++] = "amrestore";
432     if(re_header || re_device || re_host || re_disk || re_datestamp ||
433        re_label || re_fsf) {
434
435         amrestore_args[i++] = "-p";
436         if(re_header) amrestore_args[i++] = "-h";
437         if(re_label) {
438             amrestore_args[i++] = "-l";
439             amrestore_args[i++] = re_label;
440         }
441         if(re_fsf) {
442             amrestore_args[i++] = "-f";
443             amrestore_args[i++] = re_fsf;
444         }
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;
449     }
450     else { /* fe_amidxtaped_nargs */
451         while (i <= amrestore_nargs) {
452             amrestore_args[i++] = stralloc(get_client_line());
453         }
454     }
455     amrestore_args[i] = NULL;
456
457     amrestore_path = vstralloc(sbindir, "/", "amrestore", NULL);
458
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++)
463     {
464         dbprintf(("argv[%d] = \"%s\"\n", i, amrestore_args[i]));
465     }
466
467     if ((pid = fork()) == 0)
468     {
469         /* child */
470         (void)execv(amrestore_path, amrestore_args);
471
472         /* only get here if exec failed */
473         dbprintf(("%s: child could not exec %s: %s\n",
474                   debug_prefix_time(NULL),
475                   amrestore_path,
476                   strerror(errno)));
477         return 1;
478         /*NOT REACHED*/
479     }
480
481     /* this is the parent */
482     if (pid == -1)
483     {
484         dbprintf(("%s: error forking amrestore child: %s\n",
485                   debug_prefix_time(NULL), strerror(errno)));
486         if(get_lock) {
487             unlink(conf_logfile);
488         }
489         dbclose();
490         return 1;
491     }
492
493     /* wait for the child to do the restore */
494     if (waitpid(pid, &status, 0) == -1)
495     {
496         dbprintf(("%s: error waiting for amrestore child: %s\n",
497                   debug_prefix_time(NULL), strerror(errno)));
498         if(get_lock) {
499             unlink(conf_logfile);
500         }
501         dbclose();
502         return 1;
503     }
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)
511     {
512
513         dbprintf(("%s: amrestore terminated normally with status: %d\n",
514                   debug_prefix_time(NULL), WEXITSTATUS(status)));
515     }
516     else
517     {
518         dbprintf(("%s: amrestore terminated abnormally.\n",
519                   debug_prefix_time(NULL)));
520     }
521
522     /* rewind tape */
523     if(re_device) {
524         tapename = re_device;
525     }
526     else {
527         /* the first non-option argument is the tape device */
528         for (i = 1; i <= amrestore_nargs; i++)
529             if (amrestore_args[i][0] != '-')
530                 break;
531         if (i > amrestore_nargs) {
532             dbprintf(("%s: Couldn't find tape in arguments\n",
533                       debug_prefix_time(NULL)));
534             if(get_lock) {
535                 unlink(conf_logfile);
536             }
537             dbclose();
538             return 1;
539         }
540
541         tapename = stralloc(amrestore_args[i]);
542     }
543     if (tape_stat(tapename, &stat_tape) != 0) {
544         error("could not stat %s: %s", tapename, strerror(errno));
545     }
546     isafile = S_ISREG((stat_tape.st_mode));
547     if (!isafile) {
548         char *errstr = NULL;
549
550         dbprintf(("%s: rewinding tape ...\n", debug_prefix_time(NULL)));
551         errstr = tape_rewind(tapename);
552
553         if (errstr != NULL) {
554             dbprintf(("%s: %s\n", debug_prefix_time(NULL), errstr));
555             amfree(errstr);
556         } else {
557             dbprintf(("%s: done\n", debug_prefix_time(NULL)));
558         }
559     }
560
561     if(get_lock) {
562         unlink(conf_logfile);
563     }
564
565     amfree(tapename);
566     dbclose();
567     return 0;
568 }