2 * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3 * Copyright (c) 1991-1998 University of Maryland at College Park
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.
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.
23 * Authors: the Amanda Development Team. Its members are listed in a
24 * file named AUTHORS, in the root directory of this distribution.
27 * $Id: amindexd.c,v 1.39.2.11.4.4.2.13.2.3 2005/10/02 13:48:42 martinea Exp $
29 * This is the server daemon part of the index client/server system.
30 * It is assumed that this is launched from inetd instead of being
31 * started as a daemon because it is not often used
36 ** - this server will do very little until it knows what Amanda config it
37 ** is to use. Setting the config has the side effect of changing to the
39 ** - XXX - I'm pretty sure the config directory name should have '/'s stripped
40 ** from it. It is given to us by an unknown person via the network.
52 #include "disk_history.h"
59 #ifdef HAVE_NETINET_IN_SYSTM_H
60 #include <netinet/in_systm.h>
63 #ifdef HAVE_NETINET_IP_H
64 #include <netinet/ip.h>
69 typedef struct REMOVE_ITEM
72 struct REMOVE_ITEM *next;
76 char local_hostname[MAX_HOSTNAME_LENGTH+1]; /* me! */
77 char *remote_hostname = NULL; /* the client */
78 char *dump_hostname = NULL; /* machine we are restoring */
79 char *disk_name; /* disk we are restoring */
80 char *target_date = NULL;
81 disklist_t *disk_list; /* all disks in cur config */
82 find_result_t *output_find = NULL;
84 static int amindexd_debug = 0;
86 static REMOVE_ITEM *uncompress_remove = NULL;
87 /* uncompressed files to remove */
89 static am_feature_t *our_features = NULL;
90 static am_feature_t *their_features = NULL;
92 static void reply P((int n, char * fmt, ...))
93 __attribute__ ((format (printf, 2, 3)));
94 static void lreply P((int n, char * fmt, ...))
95 __attribute__ ((format (printf, 2, 3)));
96 static void fast_lreply P((int n, char * fmt, ...))
97 __attribute__ ((format (printf, 2, 3)));
99 REMOVE_ITEM *remove_files(remove)
105 dbprintf(("%s: removing index file: %s\n",
106 debug_prefix_time(NULL), remove->filename));
107 unlink(remove->filename);
108 amfree(remove->filename);
110 remove = remove->next;
116 char *uncompress_file(filename_gz, emsg)
121 char *filename = NULL;
122 struct stat stat_filename;
126 filename = stralloc(filename_gz);
127 len = strlen(filename);
128 if(len > 3 && strcmp(&(filename[len-3]),".gz")==0) {
129 filename[len-3]='\0';
130 } else if(len > 2 && strcmp(&(filename[len-2]),".Z")==0) {
131 filename[len-2]='\0';
134 /* uncompress the file */
135 result=stat(filename,&stat_filename);
136 if(result==-1 && errno==ENOENT) { /* file does not exist */
137 REMOVE_ITEM *remove_file;
138 cmd = vstralloc(UNCOMPRESS_PATH,
139 #ifdef UNCOMPRESS_OPT
142 " \'", filename_gz, "\'",
145 " > ", "\'", filename, "\'",
147 dbprintf(("%s: uncompress command: %s\n",
148 debug_prefix_time(NULL), cmd));
149 if (system(cmd)!=0) {
151 *emsg = vstralloc("\"", cmd, "\" failed", NULL);
159 /* add at beginning */
160 remove_file = (REMOVE_ITEM *)alloc(sizeof(REMOVE_ITEM));
161 remove_file->filename = stralloc(filename);
162 remove_file->next = uncompress_remove;
163 uncompress_remove = remove_file;
164 } else if(!S_ISREG((stat_filename.st_mode))) {
166 *emsg = vstralloc("\"", filename, "\" is not a regular file", NULL);
172 /* already uncompressed */
178 /* find all matching entries in a dump listing */
179 /* return -1 if error */
180 static int process_ls_dump(dir, dump_item, recursive, emsg)
182 DUMP_ITEM *dump_item;
187 char *old_line = NULL;
188 char *filename = NULL;
190 char *dir_slash = NULL;
196 if (strcmp(dir, "/") == 0) {
197 dir_slash = stralloc(dir);
199 dir_slash = stralloc2(dir, "/");
202 filename_gz = getindexfname(dump_hostname, disk_name, dump_item->date,
204 if((filename = uncompress_file(filename_gz, emsg)) == NULL) {
211 if((fp = fopen(filename,"r"))==0) {
213 *emsg = stralloc(strerror(errno));
218 len_dir_slash=strlen(dir_slash);
220 for(; (line = agets(fp)) != NULL; free(line)) {
221 if(strncmp(dir_slash, line, len_dir_slash) == 0) {
223 s = line + len_dir_slash;
225 while(ch && ch != '/') ch = *s++;/* find end of the file name */
231 if(old_line == NULL || strcmp(line, old_line) != 0) {
232 add_dir_list_item(dump_item, line);
247 /* send a 1 line reply to the client */
248 printf_arglist_function1(static void reply, int, n, char *, fmt)
253 arglist_start(args, fmt);
254 ap_snprintf(buf, sizeof(buf), "%03d ", n);
255 ap_vsnprintf(buf+4, sizeof(buf)-4, fmt, args);
258 if (printf("%s\r\n", buf) < 0)
260 dbprintf(("%s: ! error %d (%s) in printf\n",
261 debug_prefix_time(NULL), errno, strerror(errno)));
262 uncompress_remove = remove_files(uncompress_remove);
265 if (fflush(stdout) != 0)
267 dbprintf(("%s: ! error %d (%s) in fflush\n",
268 debug_prefix_time(NULL), errno, strerror(errno)));
269 uncompress_remove = remove_files(uncompress_remove);
272 dbprintf(("%s: < %s\n", debug_prefix_time(NULL), buf));
275 /* send one line of a multi-line response */
276 printf_arglist_function1(static void lreply, int, n, char *, fmt)
281 arglist_start(args, fmt);
282 ap_snprintf(buf, sizeof(buf), "%03d-", n);
283 ap_vsnprintf(buf+4, sizeof(buf)-4, fmt, args);
286 if (printf("%s\r\n", buf) < 0)
288 dbprintf(("%s: ! error %d (%s) in printf\n",
289 debug_prefix_time(NULL), errno, strerror(errno)));
290 uncompress_remove = remove_files(uncompress_remove);
293 if (fflush(stdout) != 0)
295 dbprintf(("%s: ! error %d (%s) in fflush\n",
296 debug_prefix_time(NULL), errno, strerror(errno)));
297 uncompress_remove = remove_files(uncompress_remove);
301 dbprintf(("%s: < %s\n", debug_prefix_time(NULL), buf));
304 /* send one line of a multi-line response */
305 printf_arglist_function1(static void fast_lreply, int, n, char *, fmt)
310 arglist_start(args, fmt);
311 ap_snprintf(buf, sizeof(buf), "%03d-", n);
312 ap_vsnprintf(buf+4, sizeof(buf)-4, fmt, args);
315 if (printf("%s\r\n", buf) < 0)
317 dbprintf(("%s: ! error %d (%s) in printf\n",
318 debug_prefix_time(NULL), errno, strerror(errno)));
319 uncompress_remove = remove_files(uncompress_remove);
324 /* see if hostname is valid */
325 /* valid is defined to be that there is an index directory for it */
326 /* also do a security check on the requested dump hostname */
327 /* to restrict access to index records if required */
328 /* return -1 if not okay */
329 int is_dump_host_valid(host)
332 struct stat dir_stat;
336 if (config_name == NULL) {
337 reply(501, "Must set config before setting host.");
342 /* only let a client restore itself for now unless it is the server */
343 if (strcasecmp(remote_hostname, local_hostname) == 0)
345 if (strcasecmp(remote_hostname, host) != 0)
348 "You don't have the necessary permissions to set dump host to %s.",
354 /* check that the config actually handles that host */
355 ihost = lookup_host(host);
357 reply(501, "Host %s is not in your disklist.", host);
361 /* assume an index dir already */
362 fn = getindexfname(host, NULL, NULL, 0);
363 if (stat (fn, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
364 reply(501, "No index records for host: %s. Have you enabled indexing?", host);
374 int is_disk_valid(disk)
378 struct stat dir_stat;
381 if (config_name == NULL || dump_hostname == NULL) {
382 reply(501, "Must set config,host before setting disk.");
386 /* check that the config actually handles that disk */
387 idisk = lookup_disk(dump_hostname, disk);
389 reply(501, "Disk %s:%s is not in your disklist.", dump_hostname, disk);
393 /* assume an index dir already */
394 fn = getindexfname(dump_hostname, disk, NULL, 0);
395 if (stat (fn, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
396 reply(501, "No index records for disk: %s. Invalid?", disk);
406 int is_config_valid(config)
413 struct stat dir_stat;
415 /* check that the config actually exists */
416 if (config == NULL) {
417 reply(501, "Must set config first.");
422 conffile = stralloc2(config_dir, CONFFILE_NAME);
423 if (read_conffile(conffile)) {
424 reply(501, "Could not read config file %s!", conffile);
429 conf_diskfile = getconf_str(CNF_DISKFILE);
430 if (*conf_diskfile == '/') {
431 conf_diskfile = stralloc(conf_diskfile);
433 conf_diskfile = stralloc2(config_dir, conf_diskfile);
435 if ((disk_list = read_diskfile(conf_diskfile)) == NULL) {
436 reply(501, "Could not read disk file %s!", conf_diskfile);
437 amfree(conf_diskfile);
440 amfree(conf_diskfile);
441 conf_tapelist = getconf_str(CNF_TAPELIST);
442 if (*conf_tapelist == '/') {
443 conf_tapelist = stralloc(conf_tapelist);
445 conf_tapelist = stralloc2(config_dir, conf_tapelist);
447 if(read_tapelist(conf_tapelist)) {
448 reply(501, "Could not read tapelist file %s!", conf_tapelist);
449 amfree(conf_tapelist);
452 amfree(conf_tapelist);
454 output_find = find_dump(1, disk_list);
455 sort_find_result("DLKHB", &output_find);
457 /* okay, now look for the index directory */
458 conf_indexdir = getconf_str(CNF_INDEXDIR);
459 if(*conf_indexdir == '/') {
460 conf_indexdir = stralloc(conf_indexdir);
462 conf_indexdir = stralloc2(config_dir, conf_indexdir);
464 if (stat (conf_indexdir, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
465 reply(501, "Index directory %s does not exist", conf_indexdir);
466 amfree(conf_indexdir);
469 amfree(conf_indexdir);
475 int build_disk_table P((void))
477 char date[3 * NUM_STR_SIZE + 2 + 1];
481 find_result_t *find_output;
483 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
484 reply(590, "Must set config,host,disk before building disk table");
492 for(find_output = output_find;
494 find_output = find_output->next) {
495 if(strcasecmp(dump_hostname, find_output->hostname) == 0 &&
496 strcmp(disk_name , find_output->diskname) == 0 &&
497 strcmp("OK" , find_output->status) == 0) {
499 * The sort order puts holding disk entries first. We want to
500 * use them if at all possible, so ignore any other entries
501 * for the same datestamp after we see a holding disk entry
502 * (as indicated by a filenum of zero).
504 if(find_output->datestamp == last_datestamp &&
505 find_output->level == last_level && last_filenum == 0) {
508 last_datestamp = find_output->datestamp;
509 last_filenum = find_output->filenum;
510 last_level = find_output->level;
511 ap_snprintf(date, sizeof(date), "%04d-%02d-%02d",
512 find_output->datestamp/10000,
513 (find_output->datestamp/100) %100,
514 find_output->datestamp %100);
515 add_dump(date, find_output->level, find_output->label,
516 find_output->filenum);
517 dbprintf(("%s: - %s %d %s %d\n",
518 debug_prefix_time(NULL), date, find_output->level,
519 find_output->label, find_output->filenum));
526 int disk_history_list P((void))
530 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
531 reply(502, "Must set config,host,disk before listing history");
535 lreply(200, " Dump history for config \"%s\" host \"%s\" disk \"%s\"",
536 config_name, dump_hostname, disk_name);
538 for (item=first_dump(); item!=NULL; item=next_dump(item))
539 lreply(201, " %s %d %s %d", item->date, item->level, item->tape,
542 reply(200, "Dump history for config \"%s\" host \"%s\" disk \"%s\"",
543 config_name, dump_hostname, disk_name);
549 /* is the directory dir backed up - dir assumed complete relative to
551 /* opaque version of command */
552 int is_dir_valid_opaque(dir)
560 char *filename_gz = NULL;
561 char *filename = NULL;
563 static char *emsg = NULL;
565 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
566 reply(502, "Must set config,host,disk before asking about directories");
569 if (target_date == NULL) {
570 reply(502, "Must set date before asking about directories");
574 /* scan through till we find first dump on or before date */
575 for (item=first_dump(); item!=NULL; item=next_dump(item))
576 if (strcmp(item->date, target_date) <= 0)
581 /* no dump for given date */
582 reply(500, "No dumps available on or before date \"%s\"", target_date);
586 if(strcmp(dir, "/") == 0) {
587 ldir = stralloc(dir);
589 ldir = stralloc2(dir, "/");
591 ldir_len = strlen(ldir);
593 /* go back till we hit a level 0 dump */
597 filename_gz = getindexfname(dump_hostname, disk_name,
598 item->date, item->level);
599 if((filename = uncompress_file(filename_gz, &emsg)) == NULL) {
600 reply(599, "System error %s", emsg);
607 dbprintf(("%s: f %s\n", debug_prefix_time(NULL), filename));
608 if ((fp = fopen(filename, "r")) == NULL) {
609 reply(599, "System error %s", strerror(errno));
614 for(; (line = agets(fp)) != NULL; free(line)) {
615 if (strncmp(line, ldir, ldir_len) != 0) {
616 continue; /* not found yet */
626 last_level = item->level;
629 item=next_dump(item);
630 } while ((item != NULL) && (item->level >= last_level));
631 } while (item != NULL);
635 reply(500, "\"%s\" is an invalid directory", dir);
639 int opaque_ls(dir,recursive)
643 DUMP_ITEM *dump_item;
646 static char *emsg = NULL;
650 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
651 reply(502, "Must set config,host,disk before listing a directory");
654 if (target_date == NULL) {
655 reply(502, "Must set date before listing a directory");
659 /* scan through till we find first dump on or before date */
660 for (dump_item=first_dump(); dump_item!=NULL; dump_item=next_dump(dump_item))
661 if (strcmp(dump_item->date, target_date) <= 0)
664 if (dump_item == NULL)
666 /* no dump for given date */
667 reply(500, "No dumps available on or before date \"%s\"", target_date);
671 /* get data from that dump */
672 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
673 reply(599, "System error %s", emsg);
678 /* go back processing higher level dumps till we hit a level 0 dump */
679 last_level = dump_item->level;
680 while ((last_level != 0) && ((dump_item=next_dump(dump_item)) != NULL))
682 if (dump_item->level < last_level)
684 last_level = dump_item->level;
685 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
686 reply(599, "System error %s", emsg);
693 /* return the information to the caller */
696 lreply(200, " Opaque recursive list of %s", dir);
697 for (dir_item = get_dir_list(); dir_item != NULL;
698 dir_item = dir_item->next) {
699 if(am_has_feature(their_features, fe_amindexd_fileno_in_ORLD)){
700 fast_lreply(201, " %s %d %-16s %d %s",
701 dir_item->dump->date, dir_item->dump->level,
702 dir_item->dump->tape, dir_item->dump->file,
706 fast_lreply(201, " %s %d %-16s %s",
707 dir_item->dump->date, dir_item->dump->level,
708 dir_item->dump->tape, dir_item->path);
711 reply(200, " Opaque recursive list of %s", dir);
715 lreply(200, " Opaque list of %s", dir);
716 for (dir_item = get_dir_list(); dir_item != NULL;
717 dir_item = dir_item->next) {
718 if(am_has_feature(their_features, fe_amindexd_fileno_in_OLSD)){
719 lreply(201, " %s %d %-16s %d %s",
720 dir_item->dump->date, dir_item->dump->level,
721 dir_item->dump->tape, dir_item->dump->file,
725 lreply(201, " %s %d %-16s %s",
726 dir_item->dump->date, dir_item->dump->level,
727 dir_item->dump->tape, dir_item->path);
730 reply(200, " Opaque list of %s", dir);
737 /* returns the value of tapedev from the amanda.conf file if set,
738 otherwise reports an error */
739 int tapedev_is P((void))
743 /* check state okay to do this */
744 if (config_name == NULL) {
745 reply(501, "Must set config before asking about tapedev.");
749 /* get tapedev value */
750 if ((result = getconf_str(CNF_TAPEDEV)) == NULL)
752 reply(501, "Tapedev not set in config file.");
761 /* returns YES if dumps for disk are compressed, NO if not */
762 int are_dumps_compressed P((void))
766 /* check state okay to do this */
767 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
768 reply(501, "Must set config,host,disk name before asking about dumps.");
772 /* now go through the list of disks and find which have indexes */
773 for (diskp = disk_list->head; diskp != NULL; diskp = diskp->next)
774 if ((strcasecmp(diskp->host->hostname, dump_hostname) == 0)
775 && (strcmp(diskp->name, disk_name) == 0))
780 reply(501, "Couldn't find host/disk in disk file.");
784 /* send data to caller */
785 if (diskp->compress == COMP_NONE)
797 char *line = NULL, *part = NULL;
800 char *cmd_undo, cmd_undo_ch;
802 struct sockaddr_in his_addr;
803 struct hostent *his_name;
807 int user_validated = 0;
809 char *pgm = "amindexd"; /* in case argv[0] is not set */
815 * When called via inetd, it is not uncommon to forget to put the
816 * argv[0] value on the config line. On some systems (e.g. Solaris)
817 * this causes argv and/or argv[0] to be NULL, so we have to be
818 * careful getting our name.
820 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
821 if((pgm = strrchr(argv[0], '/')) != NULL) {
832 /* we'd rather not run as root */
835 if(client_uid == (uid_t) -1) {
836 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
839 initgroups(CLIENT_LOGIN, client_gid);
844 #endif /* FORCE_USERID */
848 dbprintf(("%s: version %s\n", get_pname(), version()));
850 if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) {
851 dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
852 debug_prefix_time(NULL)));
867 if(argc > 0 && strcmp(*argv, "-t") == 0) {
874 config_name = stralloc(*argv);
875 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
880 if(gethostname(local_hostname, sizeof(local_hostname)-1) == -1)
881 error("gethostname: %s", strerror(errno));
882 local_hostname[sizeof(local_hostname)-1] = '\0';
884 /* now trim domain off name */
887 while(ch && ch != '.') ch = *s++;
892 * Fake the remote address as the local address enough to get
893 * through the security check.
895 his_name = gethostbyname(local_hostname);
896 if(his_name == NULL) {
897 error("gethostbyname(%s) failed\n", local_hostname);
899 assert(his_name->h_addrtype == AF_INET);
900 his_addr.sin_family = his_name->h_addrtype;
901 his_addr.sin_port = htons(0);
902 memcpy((char *)&his_addr.sin_addr.s_addr,
903 (char *)his_name->h_addr_list[0], his_name->h_length);
905 /* who are we talking to? */
906 socklen = sizeof (his_addr);
907 if (getpeername(0, (struct sockaddr *)&his_addr, &socklen) == -1)
908 error("getpeername: %s", strerror(errno));
910 if (his_addr.sin_family != AF_INET || ntohs(his_addr.sin_port) == 20)
912 error("connection rejected from %s family %d port %d",
913 inet_ntoa(his_addr.sin_addr), his_addr.sin_family,
914 htons(his_addr.sin_port));
916 if ((his_name = gethostbyaddr((char *)&(his_addr.sin_addr),
917 sizeof(his_addr.sin_addr),
919 error("gethostbyaddr(%s): hostname lookup failed",
920 inet_ntoa(his_addr.sin_addr));
922 fp = s = his_name->h_name;
924 while(ch && ch != '.') ch = *s++;
926 remote_hostname = newstralloc(remote_hostname, fp);
929 /* clear these so we can detect when the have not been set by the client */
930 amfree(dump_hostname);
934 our_features = am_init_feature_set();
935 their_features = am_set_default_feature_set();
937 if (config_name != NULL && is_config_valid(config_name) != -1) {
941 reply(220, "%s AMANDA index server (%s) ready.", local_hostname,
944 /* a real simple parser since there are only a few commands */
947 /* get a line from the client */
950 if((part = agets(stdin)) == NULL) {
952 dbprintf(("%s: ? read error: %s\n",
953 debug_prefix_time(NULL), strerror(errno)));
955 dbprintf(("%s: ? unexpected EOF\n",
956 debug_prefix_time(NULL)));
959 dbprintf(("%s: ? unprocessed input:\n",
960 debug_prefix_time(NULL)));
961 dbprintf(("-----\n"));
962 dbprintf(("%s\n", line));
963 dbprintf(("-----\n"));
967 uncompress_remove = remove_files(uncompress_remove);
969 return 1; /* they hung up? */
972 strappend(line, part);
979 break; /* we have a whole line */
981 if((len = strlen(line)) > 0 && line[len-1] == '\r') {
982 line[len-1] = '\0'; /* zap the '\r' */
986 * Hmmm. We got a "line" from agets(), which means it saw
987 * a '\n' (or EOF, etc), but there was not a '\r' before it.
988 * Put a '\n' back in the buffer and loop for more.
990 strappend(line, "\n");
993 dbprintf(("%s: > %s\n", debug_prefix_time(NULL), line));
999 skip_whitespace(s, ch);
1001 reply(500, "Command not recognised/incorrect: %s", line);
1006 skip_non_whitespace(s, ch);
1007 cmd_undo = s-1; /* for error message */
1008 cmd_undo_ch = *cmd_undo;
1011 skip_whitespace(s, ch); /* find the argument */
1014 skip_non_whitespace(s, ch);
1019 if (!user_validated && strcmp(cmd, "SECURITY") == 0 && arg) {
1020 user_validated = security_ok(&his_addr, arg, 0, &errstr);
1021 if(user_validated) {
1022 reply(200, "Access OK");
1026 if (!user_validated) {
1028 reply(500, "Access not allowed: %s", errstr);
1030 reply(500, "Access not allowed");
1035 if (strcmp(cmd, "QUIT") == 0) {
1037 } else if (strcmp(cmd, "HOST") == 0 && arg) {
1038 /* set host we are restoring */
1040 if (is_dump_host_valid(arg) != -1)
1042 dump_hostname = newstralloc(dump_hostname, arg);
1043 reply(200, "Dump host set to %s.", dump_hostname);
1044 amfree(disk_name); /* invalidate any value */
1047 } else if (strcmp(cmd, "DISK") == 0 && arg) {
1049 if (is_disk_valid(arg) != -1) {
1050 disk_name = newstralloc(disk_name, arg);
1051 if (build_disk_table() != -1) {
1052 reply(200, "Disk set to %s.", disk_name);
1056 } else if (strcmp(cmd, "LISTDISK") == 0) {
1060 if (config_name == NULL || dump_hostname == NULL) {
1061 reply(501, "Must set config, host before listdisk");
1064 lreply(200, " List of disk for device %s on host %s", arg,
1066 for (disk = disk_list->head; disk!=NULL; disk = disk->next) {
1067 if(strcmp(disk->host->hostname, dump_hostname) == 0 &&
1068 ((disk->device && strcmp(disk->device, arg) == 0) ||
1069 (!disk->device && strcmp(disk->name, arg) == 0))) {
1070 fast_lreply(201, " %s", disk->name);
1075 reply(200, "List of disk for device %s on host %s", arg,
1079 reply(200, "No disk for device %s on host %s", arg,
1084 lreply(200, " List of disk for host %s", dump_hostname);
1085 for (disk = disk_list->head; disk!=NULL; disk = disk->next) {
1086 if(strcmp(disk->host->hostname, dump_hostname) == 0) {
1087 fast_lreply(201, " %s", disk->name);
1092 reply(200, "List of disk for host %s", dump_hostname);
1095 reply(200, "No disk for host %s", dump_hostname);
1099 } else if (strcmp(cmd, "SCNF") == 0 && arg) {
1101 amfree(config_name);
1103 config_name = newstralloc(config_name, arg);
1104 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
1105 if (is_config_valid(arg) != -1) {
1106 amfree(dump_hostname); /* invalidate any value */
1107 amfree(disk_name); /* invalidate any value */
1108 reply(200, "Config set to %s.", config_name);
1110 amfree(config_name);
1114 } else if (strcmp(cmd, "FEATURES") == 0 && arg) {
1115 char *our_feature_string = NULL;
1116 char *their_feature_string = NULL;
1118 am_release_feature_set(our_features);
1119 am_release_feature_set(their_features);
1120 our_features = am_init_feature_set();
1121 our_feature_string = am_feature_to_string(our_features);
1122 their_feature_string = newstralloc(target_date, arg);
1123 their_features = am_string_to_feature(their_feature_string);
1124 reply(200, "FEATURES %s", our_feature_string);
1125 amfree(our_feature_string);
1126 amfree(their_feature_string);
1128 } else if (strcmp(cmd, "DATE") == 0 && arg) {
1130 target_date = newstralloc(target_date, arg);
1131 reply(200, "Working date set to %s.", target_date);
1133 } else if (strcmp(cmd, "DHST") == 0) {
1134 (void)disk_history_list();
1135 } else if (strcmp(cmd, "OISD") == 0 && arg) {
1136 if (is_dir_valid_opaque(arg) != -1) {
1137 reply(200, "\"%s\" is a valid directory", arg);
1139 } else if (strcmp(cmd, "OLSD") == 0 && arg) {
1140 (void)opaque_ls(arg,0);
1141 } else if (strcmp(cmd, "ORLD") == 0 && arg) {
1142 (void)opaque_ls(arg,1);
1143 } else if (strcmp(cmd, "TAPE") == 0) {
1145 } else if (strcmp(cmd, "DCMP") == 0) {
1146 (void)are_dumps_compressed();
1148 *cmd_undo = cmd_undo_ch; /* restore the command line */
1149 reply(500, "Command not recognised/incorrect: %s", cmd);
1154 uncompress_remove = remove_files(uncompress_remove);
1155 free_find_result(&output_find);
1156 reply(200, "Good bye.");