4c672721be956aba82360b31e0a908b77c19f54b
[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.2.2 2005/10/02 13:48:42 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     socklen_t socklen;
196     char *amrestore_path;
197     pid_t pid;
198     int isafile;
199     struct stat stat_tape;
200     char *tapename = NULL;
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     safe_fd(-1, 0);
218     safe_cd();
219
220     /*
221      * When called via inetd, it is not uncommon to forget to put the
222      * argv[0] value on the config line.  On some systems (e.g. Solaris)
223      * this causes argv and/or argv[0] to be NULL, so we have to be
224      * careful getting our name.
225      */
226     if (argc >= 1 && argv != NULL && argv[0] != NULL) {
227         if((pgm = strrchr(argv[0], '/')) != NULL) {
228             pgm++;
229         } else {
230             pgm = argv[0];
231         }
232     }
233
234     set_pname(pgm);
235
236 #ifdef FORCE_USERID
237
238     /* we'd rather not run as root */
239
240     if(geteuid() == 0) {
241         if(client_uid == (uid_t) -1) {
242             error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
243         }
244
245         initgroups(CLIENT_LOGIN, client_gid);
246         setgid(client_gid);
247         setuid(client_uid);
248     }
249
250 #endif  /* FORCE_USERID */
251
252     /* initialize */
253     /* close stderr first so that debug file becomes it - amrestore
254        chats to stderr, which we don't want going to client */
255     /* if no debug file, ship to bit bucket */
256     (void)close(STDERR_FILENO);
257     dbopen();
258     startclock();
259     dbprintf(("%s: version %s\n", pgm, version()));
260 #ifdef DEBUG_CODE
261     if(dbfd() != -1 && dbfd() != STDERR_FILENO)
262     {
263         if(dup2(dbfd(),STDERR_FILENO) != STDERR_FILENO)
264         {
265             perror("amidxtaped can't redirect stderr to the debug file");
266             dbprintf(("%s: can't redirect stderr to the debug file\n",
267                       debug_prefix_time(NULL)));
268             return 1;
269         }
270     }
271 #else
272     if ((i = open("/dev/null", O_WRONLY)) == -1 ||
273         (i != STDERR_FILENO &&
274          (dup2(i, STDERR_FILENO) != STDERR_FILENO ||
275           close(i) != 0))) {
276         perror("amidxtaped can't redirect stderr");
277         return 1;
278     }
279 #endif
280
281     if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) {
282         dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
283                   debug_prefix_time(NULL)));
284     }
285
286     socklen = sizeof (addr);
287     if (getpeername(0, (struct sockaddr *)&addr, &socklen) == -1)
288         error("getpeername: %s", strerror(errno));
289     if (addr.sin_family != AF_INET || ntohs(addr.sin_port) == 20) {
290         error("connection rejected from %s family %d port %d",
291               inet_ntoa(addr.sin_addr), addr.sin_family, htons(addr.sin_port));
292     }
293
294     /* do the security thing */
295     amfree(buf);
296     buf = stralloc(get_client_line());
297     s = buf;
298     ch = *s++;
299
300     skip_whitespace(s, ch);
301     if (ch == '\0')
302     {
303         error("cannot parse SECURITY line");
304     }
305     fp = s-1;
306     skip_non_whitespace(s, ch);
307     s[-1] = '\0';
308     if (strcmp(fp, "SECURITY") != 0)
309     {
310         error("cannot parse SECURITY line");
311     }
312     skip_whitespace(s, ch);
313     if (!security_ok(&addr, s-1, 0, &errstr)) {
314         error("security check failed: %s", errstr);
315     }
316
317     /* get the number of arguments */
318     amrestore_nargs = 0;
319     do {
320         amfree(buf);
321         buf = stralloc(get_client_line());
322         if(strncmp(buf, "LABEL=", 6) == 0) {
323             re_label = stralloc(buf+6);
324         }
325         else if(strncmp(buf, "FSF=", 4) == 0) {
326             int fsf = atoi(buf+4);
327             if(fsf > 0) {
328                 re_fsf = stralloc(buf+4);
329             }
330         }
331         else if(strncmp(buf, "HEADER", 6) == 0) {
332             re_header = 1;
333         }
334         else if(strncmp(buf, "DEVICE=", 7) == 0) {
335             re_device = stralloc(buf+7);
336         }
337         else if(strncmp(buf, "HOST=", 5) == 0) {
338             re_host = stralloc(buf+5);
339         }
340         else if(strncmp(buf, "DISK=", 5) == 0) {
341             re_disk = stralloc(buf+5);
342         }
343         else if(strncmp(buf, "DATESTAMP=", 10) == 0) {
344             re_datestamp = stralloc(buf+10);
345         }
346         else if(strncmp(buf, "END", 3) == 0) {
347             re_end = 1;
348         }
349         else if(strncmp(buf, "CONFIG=", 7) == 0) {
350             re_config = stralloc(buf+7);
351         }
352         else if(buf[0] != '\0' && buf[0] >= '0' && buf[0] <= '9') {
353             amrestore_nargs = atoi(buf);
354             re_end = 1;
355         }
356         else {
357         }
358     } while (re_end == 0);
359
360     if(re_config) {
361         char *conffile;
362         config_dir = vstralloc(CONFIG_DIR, "/", re_config, "/", NULL);
363         conffile = stralloc2(config_dir, CONFFILE_NAME);
364         if (read_conffile(conffile)) {
365             dbprintf(("%s: config '%s' not found\n",
366                       debug_prefix_time(NULL), re_config));
367             amfree(re_fsf);
368             amfree(re_label);
369             amfree(re_config);
370         }
371     }
372     else {
373         amfree(re_fsf);
374         amfree(re_label);
375     }
376
377     if(re_device && re_config &&
378        strcmp(re_device, getconf_str(CNF_TAPEDEV)) == 0) {
379         get_lock = lock_logfile();
380     }
381
382     if(re_label && re_config &&
383        strcmp(re_device, getconf_str(CNF_AMRECOVER_CHANGER)) == 0) {
384
385         if(changer_init() == 0) {
386             dbprintf(("%s: No changer available\n",
387                        debug_prefix_time(NULL)));
388         }
389         else {
390             searchlabel = stralloc(re_label);
391             changer_find(scan_init, taperscan_slot, searchlabel);
392             if(found == 0) {
393                 dbprintf(("%s: Can't find label \"%s\"\n",
394                           debug_prefix_time(NULL), searchlabel));
395                 if(get_lock) {
396                     unlink(conf_logfile);
397                 }
398                 dbclose();
399                 exit(1);
400             }
401             else {
402                      re_device=stralloc(found_device);
403                 dbprintf(("%s: label \"%s\" found\n",
404                           debug_prefix_time(NULL), searchlabel));
405             }
406         }
407     }
408
409     if(re_fsf && re_config && getconf_int(CNF_AMRECOVER_DO_FSF) == 0) {
410         amfree(re_fsf);
411     }
412     if(re_label && re_config && getconf_int(CNF_AMRECOVER_CHECK_LABEL) == 0) {
413         amfree(re_label);
414     }
415
416     dbprintf(("%s: amrestore_nargs=%d\n",
417               debug_prefix_time(NULL),
418               amrestore_nargs));
419
420     amrestore_args = (char **)alloc((amrestore_nargs+12)*sizeof(char *));
421     i = 0;
422     amrestore_args[i++] = "amrestore";
423     if(re_header || re_device || re_host || re_disk || re_datestamp ||
424        re_label || re_fsf) {
425
426         amrestore_args[i++] = "-p";
427         if(re_header) amrestore_args[i++] = "-h";
428         if(re_label) {
429             amrestore_args[i++] = "-l";
430             amrestore_args[i++] = re_label;
431         }
432         if(re_fsf) {
433             amrestore_args[i++] = "-f";
434             amrestore_args[i++] = re_fsf;
435         }
436         if(re_device) amrestore_args[i++] = re_device;
437         if(re_host) amrestore_args[i++] = re_host;
438         if(re_disk) amrestore_args[i++] = re_disk;
439         if(re_datestamp) amrestore_args[i++] = re_datestamp;
440     }
441     else { /* fe_amidxtaped_nargs */
442         while (i <= amrestore_nargs) {
443             amrestore_args[i++] = stralloc(get_client_line());
444         }
445     }
446     amrestore_args[i] = NULL;
447
448     amrestore_path = vstralloc(sbindir, "/", "amrestore", NULL);
449
450     /* so got all the arguments, now ready to execv */
451     dbprintf(("%s: Ready to execv amrestore with:\n", debug_prefix_time(NULL)));
452     dbprintf(("path = %s\n", amrestore_path));
453     for (i = 0; amrestore_args[i] != NULL; i++)
454     {
455         dbprintf(("argv[%d] = \"%s\"\n", i, amrestore_args[i]));
456     }
457
458     if ((pid = fork()) == 0)
459     {
460         /* child */
461         (void)execv(amrestore_path, amrestore_args);
462
463         /* only get here if exec failed */
464         dbprintf(("%s: child could not exec %s: %s\n",
465                   debug_prefix_time(NULL),
466                   amrestore_path,
467                   strerror(errno)));
468         return 1;
469         /*NOT REACHED*/
470     }
471
472     /* this is the parent */
473     if (pid == -1)
474     {
475         dbprintf(("%s: error forking amrestore child: %s\n",
476                   debug_prefix_time(NULL), strerror(errno)));
477         if(get_lock) {
478             unlink(conf_logfile);
479         }
480         dbclose();
481         return 1;
482     }
483
484     /* wait for the child to do the restore */
485     if (waitpid(pid, &status, 0) == -1)
486     {
487         dbprintf(("%s: error waiting for amrestore child: %s\n",
488                   debug_prefix_time(NULL), strerror(errno)));
489         if(get_lock) {
490             unlink(conf_logfile);
491         }
492         dbclose();
493         return 1;
494     }
495     /* amrestore often sees the pipe reader (ie restore) quit in the middle
496        of the file because it has extracted all of the files needed. This
497        results in an exit status of 2. This unfortunately is the exit
498        status returned by many errors. Only when the exit status is 1 is it
499        guaranteed that an error existed. In all cases we should rewind the
500        tape if we can so that a retry starts from the correct place */
501     if (WIFEXITED(status) != 0)
502     {
503
504         dbprintf(("%s: amrestore terminated normally with status: %d\n",
505                   debug_prefix_time(NULL), WEXITSTATUS(status)));
506     }
507     else
508     {
509         dbprintf(("%s: amrestore terminated abnormally.\n",
510                   debug_prefix_time(NULL)));
511     }
512
513     /* rewind tape */
514     if(re_device) {
515         tapename = re_device;
516     }
517     else {
518         /* the first non-option argument is the tape device */
519         for (i = 1; i <= amrestore_nargs; i++)
520             if (amrestore_args[i][0] != '-')
521                 break;
522         if (i > amrestore_nargs) {
523             dbprintf(("%s: Couldn't find tape in arguments\n",
524                       debug_prefix_time(NULL)));
525             if(get_lock) {
526                 unlink(conf_logfile);
527             }
528             dbclose();
529             return 1;
530         }
531
532         tapename = stralloc(amrestore_args[i]);
533     }
534     if (tape_stat(tapename, &stat_tape) != 0) {
535         error("could not stat %s: %s", tapename, strerror(errno));
536     }
537     isafile = S_ISREG((stat_tape.st_mode));
538     if (!isafile) {
539         char *errstr = NULL;
540
541         dbprintf(("%s: rewinding tape ...\n", debug_prefix_time(NULL)));
542         errstr = tape_rewind(tapename);
543
544         if (errstr != NULL) {
545             dbprintf(("%s: %s\n", debug_prefix_time(NULL), errstr));
546             amfree(errstr);
547         } else {
548             dbprintf(("%s: done\n", debug_prefix_time(NULL)));
549         }
550     }
551
552     if(get_lock) {
553         unlink(conf_logfile);
554     }
555
556     amfree(tapename);
557     dbclose();
558     return 0;
559 }