f741ce951cfad37ef2826566e5e3c71bd6fc1cb8
[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 #include "amxml.h"
36
37 extern unsigned short samba_extract_method;
38
39 /* sets a date, mapping given date into standard form if needed */
40 int
41 set_date(
42     char *      date)
43 {
44     char *cmd = NULL;
45     char *qdisk_path;
46
47     clear_dir_list();
48
49     cmd = stralloc2("DATE ", date);
50     if (converse(cmd) == -1)
51         exit(1);
52
53     /* if a host/disk/directory is set, then check if that directory
54        is still valid at the new date, and if not set directory to
55        mount_point */
56     if (disk_path != NULL) {
57         qdisk_path = quote_string(disk_path);
58         cmd = newstralloc2(cmd, "OISD ", qdisk_path);
59         amfree(qdisk_path);
60         if (exchange(cmd) == -1)
61             exit(1);
62         if (server_happy())
63         {
64             suck_dir_list_from_server();
65         }
66         else
67         {
68             g_printf(_("No index records for cwd on new date\n"));
69             g_printf(_("Setting cwd to mount point\n"));
70             disk_path = newstralloc(disk_path, "/");    /* fake it */
71             clear_dir_list();
72         }
73     }
74     amfree(cmd);
75     return 0;
76 }
77
78
79 void
80 set_host(
81     const char *host)
82 {
83     char *cmd = NULL;
84     struct hostent *hp = NULL;
85     char **hostp;
86     int found_host = 0;
87     char *uqhost = unquote_string(host);
88
89     if (is_extract_list_nonempty())
90     {
91         g_printf(_("Must clear extract list before changing host\n"));
92         amfree(uqhost);
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         amfree(uqmtpt);
221         amfree(uqdsk);
222         return;
223     }
224
225     disk_name = newstralloc(disk_name, uqdsk);
226     if (mtpt == NULL)
227     {
228         /* mount point not specified */
229         if (*uqdsk == '/')
230         {
231             /* disk specified by mount point, hence use it */
232             mount_point = newstralloc(mount_point, uqdsk);
233         }
234         else
235         {
236             /* device name given, use '/' because nothing better */
237             mount_point = newstralloc(mount_point, "/");
238         }
239     }
240     else
241     {
242         /* mount point specified */
243         mount_point = newstralloc(mount_point, uqmtpt);
244     }
245
246     /* set the working directory to the mount point */
247     /* there is the possibility that there are no index records for the
248        disk for the given date, hence setting the directory to the
249        mount point will fail. Preempt this by checking first so we can write
250        a more informative message. */
251     if (exchange("OISD /") == -1)
252         exit(1);
253     if (server_happy())
254     {
255         disk_path = newstralloc(disk_path, "/");
256         suck_dir_list_from_server();    /* get list of directory contents */
257     }
258     else
259     {
260         g_printf(_("No index records for disk for specified date\n"));
261         g_printf(_("If date correct, notify system administrator\n"));
262         disk_path = newstralloc(disk_path, "/");        /* fake it */
263         clear_dir_list();
264     }
265     amfree(uqmtpt);
266     amfree(uqdsk);
267
268     if (am_has_feature(indexsrv_features, fe_amindexd_DLE)) {
269         char *dle_str;
270         char *errmsg = NULL;
271
272         cmd = stralloc("DLE");
273         if (exchange(cmd) == -1)
274             exit(1);
275         amfree(cmd);
276
277         if (!server_happy())
278             return;
279
280         dle_str = reply_line();
281         if (BSTRNCMP(dle_str+4, "NODLE") == 0) {
282             dump_dle = NULL;
283         } else {
284             dle_str = unquote_string(dle_str+4);
285             dump_dle = amxml_parse_node_CHAR(dle_str, &errmsg);
286         }
287     }
288 }
289
290 void
291 list_disk(
292     char *      amdevice)
293 {
294     char *cmd = NULL;
295     char *qamdevice, *uqamdevice;
296
297     if(amdevice) {
298         uqamdevice = unquote_string(amdevice);
299         qamdevice = quote_string(uqamdevice);
300         cmd = stralloc2("LISTDISK ", qamdevice);
301         amfree(uqamdevice);
302         amfree(qamdevice);
303         if (converse(cmd) == -1)
304             exit(1);
305         amfree(cmd);
306     }
307     else {
308         cmd = stralloc("LISTDISK");
309         if (converse(cmd) == -1)
310             exit(1);
311         amfree(cmd);
312     }
313 }
314
315 void
316 local_cd(
317     char *dir)
318 {
319     char *uqdir = unquote_string(dir);
320     if (chdir(uqdir) == -1) {
321         perror(uqdir);
322     }
323     amfree(uqdir);
324 }
325
326 void
327 cd_glob(
328     char *      glob)
329 {
330     char *regex;
331     char *regex_path;
332     char *s;
333     char *uqglob;
334
335     char *path_on_disk = NULL;
336
337     if (disk_name == NULL) {
338         g_printf(_("Must select disk before changing directory\n"));
339         return;
340     }
341
342     uqglob = unquote_string(glob);
343     regex = glob_to_regex(uqglob);
344     dbprintf(_("cd_glob (%s) -> %s\n"), uqglob, regex);
345     if ((s = validate_regexp(regex)) != NULL) {
346         g_printf(_("\"%s\" is not a valid shell wildcard pattern: "), glob);
347         puts(s);
348         amfree(regex);
349         amfree(uqglob);
350         return;
351     }
352     /*
353      * glob_to_regex() anchors the beginning of the pattern with ^,
354      * but we will be tacking it onto the end of the current directory
355      * in add_file, so strip that off.  Also, it anchors the end with
356      * $, but we need to match a trailing /, add it if it is not there
357      */
358     regex_path = stralloc(regex + 1);
359     amfree(regex);
360     if(regex_path[strlen(regex_path) - 2] != '/' ) {
361         regex_path[strlen(regex_path) - 1] = '\0';
362         strappend(regex_path, "/$");
363     }
364
365     /* convert path (assumed in cwd) to one on disk */
366     if (strcmp(disk_path, "/") == 0)
367         path_on_disk = stralloc2("/", regex_path);
368     else {
369         char *clean_disk_path = clean_regex(disk_path);
370         path_on_disk = vstralloc(clean_disk_path, "/", regex_path, NULL);
371         amfree(clean_disk_path);
372     }
373
374     cd_dir(path_on_disk, uqglob);
375
376     amfree(regex_path);
377     amfree(path_on_disk);
378     amfree(uqglob);
379 }
380
381 void
382 cd_regex(
383     char *      regex)
384 {
385     char *s;
386     char *uq_orig_regex;
387     char *uqregex;
388     int  len_uqregex;
389
390     char *path_on_disk = NULL;
391
392     if (disk_name == NULL) {
393         g_printf(_("Must select disk before changing directory\n"));
394         return;
395     }
396
397     uq_orig_regex = unquote_string(regex);
398     uqregex = stralloc(uq_orig_regex);
399
400     /* Add a terminating '/' if it is not there, maybe before a '$' */
401     len_uqregex = strlen(uqregex);
402     if (uqregex[len_uqregex-1] == '$') {
403         if (uqregex[len_uqregex-2] != '/') {
404             uqregex[len_uqregex-1] = '\0';
405             strappend(uqregex, "/$");
406         }
407     } else if (uqregex[len_uqregex-1] != '/') {
408         //uqregex[len_uqregex-1] = '\0';
409         strappend(uqregex, "/");
410     }
411     if ((s = validate_regexp(uqregex)) != NULL) {
412         g_printf(_("\"%s\" is not a valid regular expression: "), uq_orig_regex);
413         amfree(uqregex);
414         amfree(uq_orig_regex);
415         puts(s);
416         return;
417     }
418
419     /* convert path (assumed in cwd) to one on disk */
420     if (strcmp(disk_path, "/") == 0)
421         path_on_disk = stralloc2("/", uqregex);
422     else {
423         char *clean_disk_path = clean_regex(disk_path);
424         path_on_disk = vstralloc(clean_disk_path, "/", regex, NULL);
425         amfree(clean_disk_path);
426     }
427
428     cd_dir(path_on_disk, uq_orig_regex);
429
430     amfree(path_on_disk);
431     amfree(uqregex);
432     amfree(uq_orig_regex);
433 }
434
435 void
436 cd_dir(
437     char *      path_on_disk,
438     char *      default_dir)
439 {
440     char *dir = NULL;
441     char *s;
442     int nb_found;
443     size_t i;
444
445     DIR_ITEM *ditem;
446
447     if ((s = validate_regexp(path_on_disk)) != NULL) {
448         set_directory(default_dir);
449         return;
450     }
451
452     nb_found = 0;
453
454     for (ditem=get_dir_list(); ditem!=NULL && nb_found <= 1; 
455                                ditem=get_next_dir_item(ditem))
456     {
457         if (match(path_on_disk, ditem->path))
458         {
459             i = strlen(ditem->path);
460             if((i > 0 && ditem->path[i-1] == '/')
461                || (i > 1 && ditem->path[i-2] == '/' && ditem->path[i-1] == '.'))
462             {   /* It is a directory */
463                 char *dir1, *dir2;
464                 nb_found++;
465                 dir = newstralloc(dir,ditem->path);
466                 if(dir[strlen(dir)-1] == '/')
467                     dir[strlen(dir)-1] = '\0'; /* remove last / */
468                 /* remove everything before the last / */
469                 dir1 = strrchr(dir,'/');
470                 if (dir1) {
471                     dir1++;
472                     dir2 = stralloc(dir1);
473                     amfree(dir);
474                     dir = dir2;
475                 }
476             }
477         }
478     }
479
480     if(nb_found==0) {
481         set_directory(default_dir);
482     }
483     else if(nb_found==1) {
484         set_directory(dir);
485     }
486     else {
487         g_printf(_("Too many directories\n"));
488     }
489     amfree(dir);
490 }
491
492 void
493 set_directory(
494     char *      dir)
495 {
496     char *cmd = NULL;
497     char *new_dir = NULL;
498     char *qnew_dir;
499     char *dp, *de;
500     char *ldir = NULL;
501
502     /* do nothing if "." */
503     if(strcmp(dir,".")==0) {
504         show_directory();               /* say where we are */
505         return;
506         /*NOTREACHED*/
507     }
508
509     if (disk_name == NULL) {
510         g_printf(_("Must select disk before setting directory\n"));
511         return;
512         /*NOTREACHED*/
513     }
514
515     ldir = stralloc(dir);
516     clean_pathname(ldir);
517
518     /* convert directory into absolute path relative to disk mount point */
519     if (ldir[0] == '/')
520     {
521         /* absolute path specified, must start with mount point */
522         if (strcmp(mount_point, "/") == 0)
523         {
524             new_dir = stralloc(ldir);
525         }
526         else
527         {
528             if (strncmp(mount_point, ldir, strlen(mount_point)) != 0)
529             {
530                 g_printf(_("Invalid directory - Can't cd outside mount point \"%s\"\n"),
531                        mount_point);
532                 amfree(ldir);
533                 return;
534                 /*NOTREACHED*/
535             }
536             new_dir = stralloc(ldir+strlen(mount_point));
537             if (strlen(new_dir) == 0) {
538                 new_dir = newstralloc(new_dir, "/");
539                                         /* i.e. ldir == mount_point */
540             }
541         }
542     }
543     else
544     {
545         new_dir = stralloc(disk_path);
546         dp = ldir;
547         /* strip any leading ..s */
548         while (strncmp(dp, "../", 3) == 0)
549         {
550             de = strrchr(new_dir, '/'); /* always at least 1 */
551             if (de == new_dir)
552             {
553                 /* at top of disk */
554                 *(de + 1) = '\0';
555                 dp = dp + 3;
556             }
557             else
558             {
559                 *de = '\0';
560                 dp = dp + 3;
561             }
562         }
563         if (strcmp(dp, "..") == 0) {
564             if (strcmp(new_dir, "/") == 0) {
565                 /* at top of disk */
566                 g_printf(_("Invalid directory - Can't cd outside mount point \"%s\"\n"),
567                        mount_point);
568                 /*@ignore@*/
569                 amfree(new_dir);
570                 /*@end@*/
571                 amfree(ldir);
572                 return;
573                 /*NOTREACHED*/
574             }
575             de = strrchr(new_dir, '/'); /* always at least 1 */
576             if (de == new_dir)
577             {
578                 /* at top of disk */
579                 *(de+1) = '\0';
580             }
581             else
582             {
583                 *de = '\0';
584             }
585         } else {
586             /*@ignore@*/
587             if (strcmp(new_dir, "/") != 0) {
588                 strappend(new_dir, "/");
589             }
590             strappend(new_dir, ldir);
591             /*@end@*/
592         }
593     }
594
595     qnew_dir = quote_string(new_dir);
596     cmd = stralloc2("OISD ", qnew_dir);
597     amfree(qnew_dir);
598     if (exchange(cmd) == -1) {
599         exit(1);
600         /*NOTREACHED*/
601     }
602     amfree(cmd);
603
604     if (server_happy())
605     {
606         disk_path = newstralloc(disk_path, new_dir);
607         suck_dir_list_from_server();    /* get list of directory contents */
608         show_directory();               /* say where we moved to */
609     }
610     else
611     {
612         g_printf(_("Invalid directory - %s\n"), dir);
613     }
614
615     /*@ignore@*/
616     amfree(new_dir);
617     amfree(ldir);
618     /*@end@*/
619 }
620
621
622 /* prints the current working directory */
623 void
624 show_directory(void)
625 {
626     if (mount_point == NULL || disk_path == NULL)
627         g_printf(_("Must select disk first\n"));
628     else if (strcmp(mount_point, "/") == 0)
629         g_printf("%s\n", disk_path);
630     else if (strcmp(disk_path, "/") == 0)
631         g_printf("%s\n", mount_point);
632     else
633         g_printf("%s%s\n", mount_point, disk_path);
634 }
635
636
637 /* set the tape server and device (deprecated version) */
638 void
639 set_tape(
640     char *      tape)
641 {
642     char *uqtape = unquote_string(tape);
643     char *tapedev = strchr(uqtape, ':');
644     char *host = NULL;
645
646     g_printf(_("NOTE: 'settape' is deprecated; use setdevice instead.\n"));
647
648     if (tapedev)
649     {
650         /* This command is deprecated because this parsing is going to fall 
651          * behind the list of available device names at some point, or to shadow
652          * an interesting hostname (wouldn't 'tape' be a good name for a 
653          * tape server?) */
654         if (tapedev != uqtape) {
655             if((strchr(tapedev+1, ':') == NULL) &&
656                (strncmp_const(uqtape, "null:") == 0 ||
657                 strncmp_const(uqtape, "rait:") == 0 ||
658                 strncmp_const(uqtape, "file:") == 0 ||
659                 strncmp_const(uqtape, "s3:") == 0 ||
660                 strncmp_const(uqtape, "tape:") == 0)) {
661                 tapedev = uqtape;
662             }
663             else {
664                 *tapedev = '\0';
665                 host = stralloc(uqtape);
666                 ++tapedev;
667             }
668         } else {
669             ++tapedev;
670         }
671     } else
672         tapedev = uqtape;
673     
674     if (tapedev[0])
675     {
676         if (strcmp(tapedev, "default") == 0)
677             tapedev = NULL;
678     }
679
680     /* call out to the new version */
681     set_device(host, tapedev);
682
683     amfree(host);
684     amfree(uqtape);
685 }
686
687 /* set the tape server and device, for real */
688 void
689 set_device(
690     char *      host,
691     char *      device)
692 {
693     if (host)
694         tape_server_name = newstralloc(tape_server_name, host);
695     else
696         amfree(tape_server_name);
697
698     if (device)
699         tape_device_name = newstralloc(tape_device_name, device);
700     else
701         amfree(tape_device_name);
702
703     /* print the current status */
704     if (tape_device_name)
705         g_printf (_("Using tape \"%s\""), tape_device_name);
706     else
707         g_printf (_("Using default tape"));
708
709     if (tape_server_name)
710         g_printf (_(" from server %s.\n"), tape_server_name);
711     else
712         g_printf (_(".\nTape server unspecified, assumed to be %s.\n"),
713                 server_name);
714 }
715
716 void
717 set_mode(
718     int         mode)
719 {
720   if (mode == SAMBA_SMBCLIENT) {
721     g_printf (_("SAMBA dumps will be extracted using smbclient\n"));
722     samba_extract_method = SAMBA_SMBCLIENT;
723   } else {
724     if (mode == SAMBA_TAR) {
725       g_printf (_("SAMBA dumps will be extracted as TAR dumps\n"));
726       samba_extract_method = SAMBA_TAR;
727     }
728   }
729 }
730
731 void
732 show_mode(void) 
733 {
734 #ifdef SAMBA_CLIENT
735   g_printf (_("SAMBA dumps are extracted "));
736
737   if (samba_extract_method == SAMBA_TAR) {
738     g_printf (_(" as TAR dumps\n"));
739   } else {
740     g_printf (_("using smbclient\n"));
741   }
742 #endif /* SAMBA_CLIENT */
743 }