2f659aec791bb9938c0a4489924554612a9522c7
[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             g_printf(_("No index records for cwd on new date\n"));
70             g_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         g_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             g_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                 g_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     /* Try looking up the canonical name of the host */
146     if (!found_host) {
147         char *canonname;
148         int result;
149
150         result = resolve_hostname(uqhost, 0, NULL, &canonname);
151         if (result == 0 && canonname) {
152             host = canonname;
153             g_printf(_("Trying host %s ...\n"), host);
154             cmd = newstralloc2(cmd, "HOST ", host);
155             if (converse(cmd) == -1)
156                 exit(1);
157             if(server_happy())
158                 found_host = 1;
159         }
160     }
161
162     if(found_host) {
163         dump_hostname = newstralloc(dump_hostname, host);
164         amfree(disk_name);
165         amfree(mount_point);
166         amfree(disk_path);
167         clear_dir_list();
168     }
169     amfree(cmd);
170     amfree(uqhost);
171 }
172
173 void
174 list_host(void)
175 {
176     char *cmd = NULL;
177
178     cmd = stralloc("LISTHOST");
179     if (converse(cmd) == -1)
180         exit(1);
181     amfree(cmd);
182 }
183
184 void
185 set_disk(
186     char *      dsk,
187     char *      mtpt)
188 {
189     char *cmd = NULL;
190     char *qdsk;
191     char *uqdsk;
192     char *uqmtpt = NULL;
193
194     if (is_extract_list_nonempty())
195     {
196         g_printf(_("Must clear extract list before changing disk\n"));
197         return;
198     }
199
200     /* if mount point specified, check it is valid */
201     if (mtpt != NULL) {
202         uqmtpt = unquote_string(mtpt);
203         if (*mtpt != '/') {
204             g_printf(_("Mount point \"%s\" invalid - must start with /\n"), uqmtpt);
205             amfree(uqmtpt);
206             return;
207         }
208     }
209
210     clear_dir_list();
211     uqdsk = unquote_string(dsk);
212     qdsk = quote_string(uqdsk);
213     cmd = stralloc2("DISK ", qdsk);
214     amfree(qdsk);
215     if (converse(cmd) == -1)
216         exit(1);
217     amfree(cmd);
218
219     if (!server_happy())
220         return;
221
222     disk_name = newstralloc(disk_name, uqdsk);
223     if (mtpt == NULL)
224     {
225         /* mount point not specified */
226         if (*uqdsk == '/')
227         {
228             /* disk specified by mount point, hence use it */
229             mount_point = newstralloc(mount_point, uqdsk);
230         }
231         else
232         {
233             /* device name given, use '/' because nothing better */
234             mount_point = newstralloc(mount_point, "/");
235         }
236     }
237     else
238     {
239         /* mount point specified */
240         mount_point = newstralloc(mount_point, uqmtpt);
241     }
242
243     /* set the working directory to the mount point */
244     /* there is the possibility that there are no index records for the
245        disk for the given date, hence setting the directory to the
246        mount point will fail. Preempt this by checking first so we can write
247        a more informative message. */
248     if (exchange("OISD /") == -1)
249         exit(1);
250     if (server_happy())
251     {
252         disk_path = newstralloc(disk_path, "/");
253         suck_dir_list_from_server();    /* get list of directory contents */
254     }
255     else
256     {
257         g_printf(_("No index records for disk for specified date\n"));
258         g_printf(_("If date correct, notify system administrator\n"));
259         disk_path = newstralloc(disk_path, "/");        /* fake it */
260         clear_dir_list();
261     }
262     amfree(uqmtpt);
263     amfree(uqdsk);
264 }
265
266 void
267 list_disk(
268     char *      amdevice)
269 {
270     char *cmd = NULL;
271     char *qamdevice, *uqamdevice;
272
273     if(amdevice) {
274         uqamdevice = unquote_string(amdevice);
275         qamdevice = quote_string(uqamdevice);
276         cmd = stralloc2("LISTDISK ", qamdevice);
277         amfree(uqamdevice);
278         amfree(qamdevice);
279         if (converse(cmd) == -1)
280             exit(1);
281         amfree(cmd);
282     }
283     else {
284         cmd = stralloc("LISTDISK");
285         if (converse(cmd) == -1)
286             exit(1);
287         amfree(cmd);
288     }
289 }
290
291 void
292 local_cd(
293     char *dir)
294 {
295     char *uqdir = unquote_string(dir);
296     if (chdir(uqdir) == -1) {
297         perror(uqdir);
298     }
299     amfree(uqdir);
300 }
301
302 void
303 cd_glob(
304     char *      glob)
305 {
306     char *regex;
307     char *regex_path;
308     char *s;
309     char *uqglob;
310
311     char *path_on_disk = NULL;
312
313     if (disk_name == NULL) {
314         g_printf(_("Must select disk before changing directory\n"));
315         return;
316     }
317
318     uqglob = unquote_string(glob);
319     regex = glob_to_regex(uqglob);
320     dbprintf(_("cd_glob (%s) -> %s\n"), uqglob, regex);
321     if ((s = validate_regexp(regex)) != NULL) {
322         g_printf(_("\"%s\" is not a valid shell wildcard pattern: "), glob);
323         puts(s);
324         amfree(regex);
325         return;
326     }
327     /*
328      * glob_to_regex() anchors the beginning of the pattern with ^,
329      * but we will be tacking it onto the end of the current directory
330      * in add_file, so strip that off.  Also, it anchors the end with
331      * $, but we need to match a trailing /, add it if it is not there
332      */
333     regex_path = stralloc(regex + 1);
334     amfree(regex);
335     if(regex_path[strlen(regex_path) - 2] != '/' ) {
336         regex_path[strlen(regex_path) - 1] = '\0';
337         strappend(regex_path, "/$");
338     }
339
340     /* convert path (assumed in cwd) to one on disk */
341     if (strcmp(disk_path, "/") == 0)
342         path_on_disk = stralloc2("/", regex_path);
343     else {
344         char *clean_disk_path = clean_regex(disk_path);
345         path_on_disk = vstralloc(clean_disk_path, "/", regex_path, NULL);
346         amfree(clean_disk_path);
347     }
348
349     cd_dir(path_on_disk, uqglob);
350
351     amfree(regex_path);
352     amfree(path_on_disk);
353     amfree(uqglob);
354 }
355
356 void
357 cd_regex(
358     char *      regex)
359 {
360     char *s;
361     char *uq_orig_regex;
362     char *uqregex;
363     int  len_uqregex;
364
365     char *path_on_disk = NULL;
366
367     if (disk_name == NULL) {
368         g_printf(_("Must select disk before changing directory\n"));
369         return;
370     }
371
372     uq_orig_regex = unquote_string(regex);
373     uqregex = stralloc(uq_orig_regex);
374
375     /* Add a terminating '/' if it is not there, maybe before a '$' */
376     len_uqregex = strlen(uqregex);
377     if (uqregex[len_uqregex-1] == '$') {
378         if (uqregex[len_uqregex-2] != '/') {
379             uqregex[len_uqregex-1] = '\0';
380             strappend(uqregex, "/$");
381         }
382     } else if (uqregex[len_uqregex-1] != '/') {
383         //uqregex[len_uqregex-1] = '\0';
384         strappend(uqregex, "/");
385     }
386     if ((s = validate_regexp(uqregex)) != NULL) {
387         g_printf(_("\"%s\" is not a valid regular expression: "), uq_orig_regex);
388         amfree(uqregex);
389         puts(s);
390         return;
391     }
392
393     /* convert path (assumed in cwd) to one on disk */
394     if (strcmp(disk_path, "/") == 0)
395         path_on_disk = stralloc2("/", uqregex);
396     else {
397         char *clean_disk_path = clean_regex(disk_path);
398         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
399         amfree(clean_disk_path);
400     }
401
402     cd_dir(path_on_disk, uq_orig_regex);
403
404     amfree(path_on_disk);
405     amfree(uqregex);
406     amfree(uq_orig_regex);
407 }
408
409 void
410 cd_dir(
411     char *      path_on_disk,
412     char *      default_dir)
413 {
414     char *dir = NULL;
415     char *s;
416     int nb_found;
417     size_t i;
418
419     DIR_ITEM *ditem;
420
421     if ((s = validate_regexp(path_on_disk)) != NULL) {
422         set_directory(default_dir);
423         return;
424     }
425
426     nb_found = 0;
427
428     for (ditem=get_dir_list(); ditem!=NULL && nb_found <= 1; 
429                                ditem=get_next_dir_item(ditem))
430     {
431         if (match(path_on_disk, ditem->path))
432         {
433             i = strlen(ditem->path);
434             if((i > 0 && ditem->path[i-1] == '/')
435                || (i > 1 && ditem->path[i-2] == '/' && ditem->path[i-1] == '.'))
436             {   /* It is a directory */
437                 char *dir1, *dir2;
438                 nb_found++;
439                 dir = newstralloc(dir,ditem->path);
440                 if(dir[strlen(dir)-1] == '/')
441                     dir[strlen(dir)-1] = '\0'; /* remove last / */
442                 /* remove everything before the last / */
443                 dir1 = rindex(dir,'/');
444                 if (dir1) {
445                     dir1++;
446                     dir2 = stralloc(dir1);
447                     amfree(dir);
448                     dir = dir2;
449                 }
450             }
451         }
452     }
453
454     if(nb_found==0) {
455         set_directory(default_dir);
456     }
457     else if(nb_found==1) {
458         set_directory(dir);
459     }
460     else {
461         g_printf(_("Too many directories\n"));
462     }
463     amfree(dir);
464 }
465
466 void
467 set_directory(
468     char *      dir)
469 {
470     char *cmd = NULL;
471     char *new_dir = NULL;
472     char *qnew_dir;
473     char *dp, *de;
474     char *ldir = NULL;
475
476     /* do nothing if "." */
477     if(strcmp(dir,".")==0) {
478         show_directory();               /* say where we are */
479         return;
480         /*NOTREACHED*/
481     }
482
483     if (disk_name == NULL) {
484         g_printf(_("Must select disk before setting directory\n"));
485         return;
486         /*NOTREACHED*/
487     }
488
489     ldir = stralloc(dir);
490     clean_pathname(ldir);
491
492     /* convert directory into absolute path relative to disk mount point */
493     if (ldir[0] == '/')
494     {
495         /* absolute path specified, must start with mount point */
496         if (strcmp(mount_point, "/") == 0)
497         {
498             new_dir = stralloc(ldir);
499         }
500         else
501         {
502             if (strncmp(mount_point, ldir, strlen(mount_point)) != 0)
503             {
504                 g_printf(_("Invalid directory - Can't cd outside mount point \"%s\"\n"),
505                        mount_point);
506                 amfree(ldir);
507                 return;
508                 /*NOTREACHED*/
509             }
510             new_dir = stralloc(ldir+strlen(mount_point));
511             if (strlen(new_dir) == 0) {
512                 new_dir = newstralloc(new_dir, "/");
513                                         /* i.e. ldir == mount_point */
514             }
515         }
516     }
517     else
518     {
519         new_dir = stralloc(disk_path);
520         dp = ldir;
521         /* strip any leading ..s */
522         while (strncmp(dp, "../", 3) == 0)
523         {
524             de = strrchr(new_dir, '/'); /* always at least 1 */
525             if (de == new_dir)
526             {
527                 /* at top of disk */
528                 *(de + 1) = '\0';
529                 dp = dp + 3;
530             }
531             else
532             {
533                 *de = '\0';
534                 dp = dp + 3;
535             }
536         }
537         if (strcmp(dp, "..") == 0) {
538             if (strcmp(new_dir, "/") == 0) {
539                 /* at top of disk */
540                 g_printf(_("Invalid directory - Can't cd outside mount point \"%s\"\n"),
541                        mount_point);
542                 /*@ignore@*/
543                 amfree(new_dir);
544                 /*@end@*/
545                 amfree(ldir);
546                 return;
547                 /*NOTREACHED*/
548             }
549             de = strrchr(new_dir, '/'); /* always at least 1 */
550             if (de == new_dir)
551             {
552                 /* at top of disk */
553                 *(de+1) = '\0';
554             }
555             else
556             {
557                 *de = '\0';
558             }
559         } else {
560             /*@ignore@*/
561             if (strcmp(new_dir, "/") != 0) {
562                 strappend(new_dir, "/");
563             }
564             strappend(new_dir, ldir);
565             /*@end@*/
566         }
567     }
568
569     qnew_dir = quote_string(new_dir);
570     cmd = stralloc2("OISD ", qnew_dir);
571     amfree(qnew_dir);
572     if (exchange(cmd) == -1) {
573         exit(1);
574         /*NOTREACHED*/
575     }
576     amfree(cmd);
577
578     if (server_happy())
579     {
580         disk_path = newstralloc(disk_path, new_dir);
581         suck_dir_list_from_server();    /* get list of directory contents */
582         show_directory();               /* say where we moved to */
583     }
584     else
585     {
586         g_printf(_("Invalid directory - %s\n"), dir);
587     }
588
589     /*@ignore@*/
590     amfree(new_dir);
591     amfree(ldir);
592     /*@end@*/
593 }
594
595
596 /* prints the current working directory */
597 void
598 show_directory(void)
599 {
600     if (mount_point == NULL || disk_path == NULL)
601         g_printf(_("Must select disk first\n"));
602     else if (strcmp(mount_point, "/") == 0)
603         g_printf("%s\n", disk_path);
604     else if (strcmp(disk_path, "/") == 0)
605         g_printf("%s\n", mount_point);
606     else
607         g_printf("%s%s\n", mount_point, disk_path);
608 }
609
610
611 /* set the tape server and device (deprecated version) */
612 void
613 set_tape(
614     char *      tape)
615 {
616     char *uqtape = unquote_string(tape);
617     char *tapedev = strchr(uqtape, ':');
618     char *host = NULL;
619
620     g_printf(_("NOTE: 'settape' is deprecated; use setdevice instead.\n"));
621
622     if (tapedev)
623     {
624         /* This command is deprecated because this parsing is going to fall 
625          * behind the list of available device names at some point, or to shadow
626          * an interesting hostname (wouldn't 'tape' be a good name for a 
627          * tape server?) */
628         if (tapedev != uqtape) {
629             if((strchr(tapedev+1, ':') == NULL) &&
630                (strncmp_const(uqtape, "null:") == 0 ||
631                 strncmp_const(uqtape, "rait:") == 0 ||
632                 strncmp_const(uqtape, "file:") == 0 ||
633                 strncmp_const(uqtape, "s3:") == 0 ||
634                 strncmp_const(uqtape, "tape:") == 0)) {
635                 tapedev = uqtape;
636             }
637             else {
638                 *tapedev = '\0';
639                 host = stralloc(uqtape);
640                 ++tapedev;
641             }
642         } else {
643             ++tapedev;
644         }
645     } else
646         tapedev = uqtape;
647     
648     if (tapedev[0])
649     {
650         if (strcmp(tapedev, "default") == 0)
651             tapedev = NULL;
652     }
653
654     /* call out to the new version */
655     set_device(host, tapedev);
656
657     amfree(host);
658     amfree(uqtape);
659 }
660
661 /* set the tape server and device, for real */
662 void
663 set_device(
664     char *      host,
665     char *      device)
666 {
667     if (host)
668         tape_server_name = newstralloc(tape_server_name, host);
669     else
670         amfree(tape_server_name);
671
672     if (device)
673         tape_device_name = newstralloc(tape_device_name, device);
674     else
675         amfree(tape_device_name);
676
677     /* print the current status */
678     if (tape_device_name)
679         g_printf (_("Using tape \"%s\""), tape_device_name);
680     else
681         g_printf (_("Using default tape"));
682
683     if (tape_server_name)
684         g_printf (_(" from server %s.\n"), tape_server_name);
685     else
686         g_printf (_(".\nTape server unspecified, assumed to be %s.\n"),
687                 server_name);
688 }
689
690 void
691 set_mode(
692     int         mode)
693 {
694 #ifdef SAMBA_CLIENT
695   if (mode == SAMBA_SMBCLIENT) {
696     g_printf (_("SAMBA dumps will be extracted using smbclient\n"));
697     samba_extract_method = SAMBA_SMBCLIENT;
698   } else {
699     if (mode == SAMBA_TAR) {
700       g_printf (_("SAMBA dumps will be extracted as TAR dumps\n"));
701       samba_extract_method = SAMBA_TAR;
702     }
703   }
704 #else
705   (void)mode;   /* Quiet unused parameter warning */
706 #endif /* SAMBA_CLIENT */
707 }
708
709 void
710 show_mode(void) 
711 {
712 #ifdef SAMBA_CLIENT
713   g_printf (_("SAMBA dumps are extracted "));
714
715   if (samba_extract_method == SAMBA_TAR) {
716     g_printf (_(" as TAR dumps\n"));
717   } else {
718     g_printf (_("using smbclient\n"));
719   }
720 #endif /* SAMBA_CLIENT */
721 }