Imported Upstream version 2.5.2p1
[debian/amanda] / recover-src / set_commands.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998, 2000 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 /*
27  * $Id: set_commands.c,v 1.26 2006/07/05 13:14:58 martinea Exp $
28  *
29  * implements the "set" commands in amrecover
30  */
31
32 #include "amanda.h"
33 #include "util.h"
34 #include "amrecover.h"
35
36 #ifdef SAMBA_CLIENT
37 extern unsigned short samba_extract_method;
38 #endif /* SAMBA_CLIENT */
39
40 /* sets a date, mapping given date into standard form if needed */
41 int
42 set_date(
43     char *      date)
44 {
45     char *cmd = NULL;
46     char *qdisk_path;
47
48     clear_dir_list();
49
50     cmd = stralloc2("DATE ", date);
51     if (converse(cmd) == -1)
52         exit(1);
53
54     /* if a host/disk/directory is set, then check if that directory
55        is still valid at the new date, and if not set directory to
56        mount_point */
57     if (disk_path != NULL) {
58         qdisk_path = quote_string(disk_path);
59         cmd = newstralloc2(cmd, "OISD ", qdisk_path);
60         amfree(qdisk_path);
61         if (exchange(cmd) == -1)
62             exit(1);
63         if (server_happy())
64         {
65             suck_dir_list_from_server();
66         }
67         else
68         {
69             printf("No index records for cwd on new date\n");
70             printf("Setting cwd to mount point\n");
71             disk_path = newstralloc(disk_path, "/");    /* fake it */
72             clear_dir_list();
73         }
74     }
75     amfree(cmd);
76     return 0;
77 }
78
79
80 void
81 set_host(
82     const char *host)
83 {
84     char *cmd = NULL;
85     struct hostent *hp = NULL;
86     char **hostp;
87     int found_host = 0;
88     char *uqhost = unquote_string(host);
89
90     if (is_extract_list_nonempty())
91     {
92         printf("Must clear extract list before changing host\n");
93         return;
94     }
95
96     /*
97      * The idea here is to try as many permutations of the hostname
98      * as we can imagine.  The server will reject anything it doesn't
99      * recognize.
100      */
101
102     cmd = stralloc2("HOST ", uqhost);
103     if (converse(cmd) == -1)
104         exit(1);
105     if (server_happy())
106         found_host = 1;
107
108     /*
109      * Try converting the given host to a fully qualified, canonical
110      * name.
111      */
112     if (!found_host) {
113         if ((hp = gethostbyname(uqhost)) != NULL) {
114             host = hp->h_name;
115             printf("Trying host %s ...\n", host);
116             cmd = newstralloc2(cmd, "HOST ", host);
117             if (converse(cmd) == -1)
118                 exit(1);
119             if(server_happy())
120                 found_host = 1;
121         }
122     }
123
124     /*
125      * Since we have them, try any CNAMEs that were traversed from uqhost
126      * to the canonical name (this assumes gethostbyname was called above)
127      */
128     if (!found_host) {
129         if (hp) {
130             for (hostp = hp->h_aliases; (host = *hostp) != NULL; hostp++)
131             {
132                 printf(_("Trying host %s ...\n"), host);
133                 cmd = newstralloc2(cmd, "HOST ", host);
134                 if (converse(cmd) == -1)
135                     exit(1);
136                 if(server_happy())
137                 {
138                     found_host = 1;
139                     break;
140                 }
141             }
142         }
143     }
144
145     /*
146      * gethostbyname() will not return a canonical name for a host with no
147      * IPv4 addresses, so use getaddrinfo() (if supported)
148      */
149 #ifdef WORKING_IPV6
150     if (!found_host) {
151         struct addrinfo hints;
152         struct addrinfo *gaires = NULL;
153         int res;
154
155         hints.ai_flags = AI_CANONNAME;
156         hints.ai_family = AF_UNSPEC;
157         hints.ai_socktype = 0;
158         hints.ai_protocol = 0;
159         hints.ai_addrlen = 0;
160         hints.ai_addr = NULL;
161         hints.ai_canonname = NULL;
162         hints.ai_next = NULL;
163         if ((res = getaddrinfo(uqhost, NULL, &hints, &gaires)) == 0) {
164             if (gaires && (host = gaires->ai_canonname)) {
165                 printf(_("Trying host %s ...\n"), host);
166                 cmd = newstralloc2(cmd, "HOST ", host);
167                 if (converse(cmd) == -1)
168                     exit(1);
169                 if(server_happy())
170                     found_host = 1;
171             }
172         }
173
174         if (gaires) freeaddrinfo(gaires);
175     }
176 #endif
177
178     if(found_host) {
179         dump_hostname = newstralloc(dump_hostname, host);
180         amfree(disk_name);
181         amfree(mount_point);
182         amfree(disk_path);
183         clear_dir_list();
184     }
185     amfree(cmd);
186     amfree(uqhost);
187 }
188
189 void
190 list_host(void)
191 {
192     char *cmd = NULL;
193
194     cmd = stralloc("LISTHOST");
195     if (converse(cmd) == -1)
196         exit(1);
197     amfree(cmd);
198 }
199
200 void
201 set_disk(
202     char *      dsk,
203     char *      mtpt)
204 {
205     char *cmd = NULL;
206     char *qdsk;
207     char *uqdsk;
208     char *uqmtpt = NULL;
209
210     if (is_extract_list_nonempty())
211     {
212         printf("Must clear extract list before changing disk\n");
213         return;
214     }
215
216     /* if mount point specified, check it is valid */
217     if (mtpt != NULL) {
218         uqmtpt = unquote_string(mtpt);
219         if (*mtpt != '/') {
220             printf("Mount point \"%s\" invalid - must start with /\n", uqmtpt);
221             amfree(uqmtpt);
222             return;
223         }
224     }
225
226     clear_dir_list();
227     uqdsk = unquote_string(dsk);
228     qdsk = quote_string(uqdsk);
229     cmd = stralloc2("DISK ", qdsk);
230     amfree(qdsk);
231     if (converse(cmd) == -1)
232         exit(1);
233     amfree(cmd);
234
235     if (!server_happy())
236         return;
237
238     disk_name = newstralloc(disk_name, uqdsk);
239     if (mtpt == NULL)
240     {
241         /* mount point not specified */
242         if (*uqdsk == '/')
243         {
244             /* disk specified by mount point, hence use it */
245             mount_point = newstralloc(mount_point, uqdsk);
246         }
247         else
248         {
249             /* device name given, use '/' because nothing better */
250             mount_point = newstralloc(mount_point, "/");
251         }
252     }
253     else
254     {
255         /* mount point specified */
256         mount_point = newstralloc(mount_point, uqmtpt);
257     }
258
259     /* set the working directory to the mount point */
260     /* there is the possibility that there are no index records for the
261        disk for the given date, hence setting the directory to the
262        mount point will fail. Preempt this by checking first so we can write
263        a more informative message. */
264     if (exchange("OISD /") == -1)
265         exit(1);
266     if (server_happy())
267     {
268         disk_path = newstralloc(disk_path, "/");
269         suck_dir_list_from_server();    /* get list of directory contents */
270     }
271     else
272     {
273         printf("No index records for disk for specified date\n");
274         printf("If date correct, notify system administrator\n");
275         disk_path = newstralloc(disk_path, "/");        /* fake it */
276         clear_dir_list();
277     }
278     amfree(uqmtpt);
279     amfree(uqdsk);
280 }
281
282 void
283 list_disk(
284     char *      amdevice)
285 {
286     char *cmd = NULL;
287     char *qamdevice, *uqamdevice;
288
289     if(amdevice) {
290         uqamdevice = unquote_string(amdevice);
291         qamdevice = quote_string(uqamdevice);
292         cmd = stralloc2("LISTDISK ", qamdevice);
293         amfree(uqamdevice);
294         amfree(qamdevice);
295         if (converse(cmd) == -1)
296             exit(1);
297         amfree(cmd);
298     }
299     else {
300         cmd = stralloc("LISTDISK");
301         if (converse(cmd) == -1)
302             exit(1);
303         amfree(cmd);
304     }
305 }
306
307 void
308 local_cd(
309     char *dir)
310 {
311     char *uqdir = unquote_string(dir);
312     if (chdir(uqdir) == -1) {
313         perror(uqdir);
314     }
315     amfree(uqdir);
316 }
317
318 void
319 cd_glob(
320     char *      glob)
321 {
322     char *regex;
323     char *regex_path;
324     char *s;
325     char *uqglob;
326
327     char *path_on_disk = NULL;
328
329     if (disk_name == NULL) {
330         printf("Must select disk before changing directory\n");
331         return;
332     }
333
334     uqglob = unquote_string(glob);
335     regex = glob_to_regex(uqglob);
336     dbprintf(("cd_glob (%s) -> %s\n", uqglob, regex));
337     if ((s = validate_regexp(regex)) != NULL) {
338         printf("\"%s\" is not a valid shell wildcard pattern: ", glob);
339         puts(s);
340         amfree(regex);
341         return;
342     }
343     /*
344      * glob_to_regex() anchors the beginning of the pattern with ^,
345      * but we will be tacking it onto the end of the current directory
346      * in add_file, so strip that off.  Also, it anchors the end with
347      * $, but we need to match a trailing /, add it if it is not there
348      */
349     regex_path = stralloc(regex + 1);
350     amfree(regex);
351     if(regex_path[strlen(regex_path) - 2] != '/' ) {
352         regex_path[strlen(regex_path) - 1] = '\0';
353         strappend(regex_path, "/$");
354     }
355
356     /* convert path (assumed in cwd) to one on disk */
357     if (strcmp(disk_path, "/") == 0)
358         path_on_disk = stralloc2("/", regex_path);
359     else {
360         char *clean_disk_path = clean_regex(disk_path);
361         path_on_disk = vstralloc(clean_disk_path, "/", regex_path, NULL);
362         amfree(clean_disk_path);
363     }
364
365     cd_dir(path_on_disk, uqglob);
366
367     amfree(regex_path);
368     amfree(path_on_disk);
369     amfree(uqglob);
370 }
371
372 void
373 cd_regex(
374     char *      regex)
375 {
376     char *s;
377     char *uqregex;
378
379     char *path_on_disk = NULL;
380
381     if (disk_name == NULL) {
382         printf("Must select disk before changing directory\n");
383         return;
384     }
385
386     uqregex = unquote_string(regex);
387     if ((s = validate_regexp(uqregex)) != NULL) {
388         printf("\"%s\" is not a valid regular expression: ", uqregex);
389         amfree(uqregex);
390         puts(s);
391         return;
392     }
393
394     /* convert path (assumed in cwd) to one on disk */
395     if (strcmp(disk_path, "/") == 0)
396         path_on_disk = stralloc2("/", regex);
397     else {
398         char *clean_disk_path = clean_regex(disk_path);
399         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
400         amfree(clean_disk_path);
401     }
402
403     cd_dir(path_on_disk, uqregex);
404
405     amfree(path_on_disk);
406     amfree(uqregex);
407 }
408
409 void
410 cd_dir(
411     char *      path_on_disk,
412     char *      default_dir)
413 {
414     char *path_on_disk_slash = NULL;
415     char *dir = NULL;
416     char *s;
417
418     int nb_found;
419     size_t i;
420
421     DIR_ITEM *ditem;
422
423     path_on_disk_slash = stralloc2(path_on_disk, "/");
424     if ((s = validate_regexp(path_on_disk_slash)) != NULL) {
425         amfree(path_on_disk_slash);
426     }
427
428     if ((s = validate_regexp(path_on_disk)) != NULL) {
429         path_on_disk = NULL;
430     }
431
432     nb_found = 0;
433
434     for (ditem=get_dir_list(); ditem!=NULL && nb_found <= 1; 
435                                ditem=get_next_dir_item(ditem))
436     {
437         if ((path_on_disk && match(path_on_disk, ditem->path))
438             || (path_on_disk_slash && match(path_on_disk_slash, ditem->path)))
439         {
440             i = strlen(ditem->path);
441             if((i > 0 && ditem->path[i-1] == '/')
442                || (i > 1 && ditem->path[i-2] == '/' && ditem->path[i-1] == '.'))
443             {   /* It is a directory */
444                 char *dir1, *dir2;
445                 nb_found++;
446                 dir = newstralloc(dir,ditem->path);
447                 if(dir[strlen(dir)-1] == '/')
448                     dir[strlen(dir)-1] = '\0'; /* remove last / */
449                 /* remove everything before the last / */
450                 dir1 = rindex(dir,'/');
451                 if (dir1) {
452                     dir1++;
453                     dir2 = stralloc(dir1);
454                     amfree(dir);
455                     dir = dir2;
456                 }
457             }
458         }
459     }
460     amfree(path_on_disk_slash);
461
462     if(nb_found==0) {
463         set_directory(default_dir);
464     }
465     else if(nb_found==1) {
466         set_directory(dir);
467     }
468     else {
469         printf("Too many directory\n");
470     }
471     amfree(dir);
472 }
473
474 void
475 set_directory(
476     char *      dir)
477 {
478     char *cmd = NULL;
479     char *new_dir = NULL;
480     char *qnew_dir;
481     char *dp, *de;
482     char *ldir = NULL;
483
484     /* do nothing if "." */
485     if(strcmp(dir,".")==0) {
486         show_directory();               /* say where we are */
487         return;
488         /*NOTREACHED*/
489     }
490
491     if (disk_name == NULL) {
492         printf("Must select disk before setting directory\n");
493         return;
494         /*NOTREACHED*/
495     }
496
497     ldir = stralloc(dir);
498     clean_pathname(ldir);
499
500     /* convert directory into absolute path relative to disk mount point */
501     if (ldir[0] == '/')
502     {
503         /* absolute path specified, must start with mount point */
504         if (strcmp(mount_point, "/") == 0)
505         {
506             new_dir = stralloc(ldir);
507         }
508         else
509         {
510             if (strncmp(mount_point, ldir, strlen(mount_point)) != 0)
511             {
512                 printf("Invalid directory - Can't cd outside mount point \"%s\"\n",
513                        mount_point);
514                 amfree(ldir);
515                 return;
516                 /*NOTREACHED*/
517             }
518             new_dir = stralloc(ldir+strlen(mount_point));
519             if (strlen(new_dir) == 0) {
520                 new_dir = newstralloc(new_dir, "/");
521                                         /* i.e. ldir == mount_point */
522             }
523         }
524     }
525     else
526     {
527         new_dir = stralloc(disk_path);
528         dp = ldir;
529         /* strip any leading ..s */
530         while (strncmp(dp, "../", 3) == 0)
531         {
532             de = strrchr(new_dir, '/'); /* always at least 1 */
533             if (de == new_dir)
534             {
535                 /* at top of disk */
536                 *(de + 1) = '\0';
537                 dp = dp + 3;
538             }
539             else
540             {
541                 *de = '\0';
542                 dp = dp + 3;
543             }
544         }
545         if (strcmp(dp, "..") == 0) {
546             if (strcmp(new_dir, "/") == 0) {
547                 /* at top of disk */
548                 printf("Invalid directory - Can't cd outside mount point \"%s\"\n",
549                        mount_point);
550                 /*@ignore@*/
551                 amfree(new_dir);
552                 /*@end@*/
553                 amfree(ldir);
554                 return;
555                 /*NOTREACHED*/
556             }
557             de = strrchr(new_dir, '/'); /* always at least 1 */
558             if (de == new_dir)
559             {
560                 /* at top of disk */
561                 *(de+1) = '\0';
562             }
563             else
564             {
565                 *de = '\0';
566             }
567         } else {
568             /*@ignore@*/
569             if (strcmp(new_dir, "/") != 0) {
570                 strappend(new_dir, "/");
571             }
572             strappend(new_dir, ldir);
573             /*@end@*/
574         }
575     }
576
577     qnew_dir = quote_string(new_dir);
578     cmd = stralloc2("OISD ", qnew_dir);
579     amfree(qnew_dir);
580     if (exchange(cmd) == -1) {
581         exit(1);
582         /*NOTREACHED*/
583     }
584     amfree(cmd);
585
586     if (server_happy())
587     {
588         disk_path = newstralloc(disk_path, new_dir);
589         suck_dir_list_from_server();    /* get list of directory contents */
590         show_directory();               /* say where we moved to */
591     }
592     else
593     {
594         printf("Invalid directory - %s\n", dir);
595     }
596
597     /*@ignore@*/
598     amfree(new_dir);
599     amfree(ldir);
600     /*@end@*/
601 }
602
603
604 /* prints the current working directory */
605 void
606 show_directory(void)
607 {
608     if (mount_point == NULL || disk_path == NULL)
609         printf("Must select disk first\n");
610     else if (strcmp(mount_point, "/") == 0)
611         printf("%s\n", disk_path);
612     else if (strcmp(disk_path, "/") == 0)
613         printf("%s\n", mount_point);
614     else
615         printf("%s%s\n", mount_point, disk_path);
616 }
617
618
619 /* set the tape server and device */
620 void
621 set_tape(
622     char *      tape)
623 {
624     char *uqtape = unquote_string(tape);
625     char *tapedev = strchr(uqtape, ':');
626
627     if (tapedev)
628     {
629         if (tapedev != uqtape) {
630             if((strchr(tapedev+1, ':') == NULL) &&
631                (strncmp(uqtape, "null:", 5) == 0 ||
632                 strncmp(uqtape, "rait:", 5) == 0 ||
633                 strncmp(uqtape, "file:", 5) == 0 ||
634                 strncmp(uqtape, "tape:", 5) == 0)) {
635                 tapedev = uqtape;
636             }
637             else {
638                 *tapedev = '\0';
639                 tape_server_name = newstralloc(tape_server_name, uqtape);
640                 ++tapedev;
641             }
642         } else { /* reset server_name if start with : */
643             amfree(tape_server_name);
644             ++tapedev;
645         }
646     } else
647         tapedev = uqtape;
648     
649     if (tapedev[0])
650     {
651         if (strcmp(tapedev, "default") == 0)
652             amfree(tape_device_name);
653         else
654             tape_device_name = newstralloc(tape_device_name, tapedev);
655     }
656
657     if (tape_device_name)
658         printf ("Using tape \"%s\"", tape_device_name);
659     else
660         printf ("Using default tape");
661
662     if (tape_server_name)
663         printf (" from server %s.\n", tape_server_name);
664     else
665         printf (".\nTape server unspecified, assumed to be %s.\n",
666                 server_name);
667 }
668
669 void
670 set_mode(
671     int         mode)
672 {
673 #ifdef SAMBA_CLIENT
674   if (mode == SAMBA_SMBCLIENT) {
675     printf ("SAMBA dumps will be extracted using smbclient\n");
676     samba_extract_method = SAMBA_SMBCLIENT;
677   } else {
678     if (mode == SAMBA_TAR) {
679       printf ("SAMBA dumps will be extracted as TAR dumps\n");
680       samba_extract_method = SAMBA_TAR;
681     }
682   }
683 #else
684   (void)mode;   /* Quiet unused parameter warning */
685 #endif /* SAMBA_CLIENT */
686 }
687
688 void
689 show_mode(void) 
690 {
691 #ifdef SAMBA_CLIENT
692   printf ("SAMBA dumps are extracted ");
693
694   if (samba_extract_method == SAMBA_TAR) {
695     printf (" as TAR dumps\n");
696   } else {
697     printf ("using smbclient\n");
698   }
699 #endif /* SAMBA_CLIENT */
700 }