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.86 2006/03/09 16:51:41 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.
50 #include "disk_history.h"
57 #ifdef HAVE_NETINET_IN_SYSTM_H
58 #include <netinet/in_systm.h>
61 #ifdef HAVE_NETINET_IP_H
62 #include <netinet/ip.h>
67 typedef struct REMOVE_ITEM
70 struct REMOVE_ITEM *next;
74 char local_hostname[MAX_HOSTNAME_LENGTH+1]; /* me! */
75 char *remote_hostname = NULL; /* the client */
76 char *dump_hostname = NULL; /* machine we are restoring */
77 char *disk_name; /* disk we are restoring */
78 char *target_date = NULL;
79 disklist_t disk_list; /* all disks in cur config */
80 find_result_t *output_find = NULL;
82 static int amindexd_debug = 0;
84 static REMOVE_ITEM *uncompress_remove = NULL;
85 /* uncompressed files to remove */
87 static am_feature_t *our_features = NULL;
88 static am_feature_t *their_features = NULL;
90 static REMOVE_ITEM *remove_files P((REMOVE_ITEM *));
91 static char *uncompress_file P((char *, char **));
92 static int process_ls_dump P((char *, DUMP_ITEM *, int, char **));
94 /* XXX this is a hack to make sure the printf-ish output buffer
95 for lreply and friends is big enough for long label strings.
96 Should go away if someone institutes a more fundamental fix
98 static int str_buffer_size = STR_SIZE;
100 static void reply P((int, char *, ...))
101 __attribute__ ((format (printf, 2, 3)));
102 static void lreply P((int, char *, ...))
103 __attribute__ ((format (printf, 2, 3)));
104 static void fast_lreply P((int, char *, ...))
105 __attribute__ ((format (printf, 2, 3)));
106 static int is_dump_host_valid P((char *));
107 static int is_disk_valid P((char *));
108 static int is_config_valid P((char *));
109 static int build_disk_table P((void));
110 static int disk_history_list P((void));
111 static int is_dir_valid_opaque P((char *));
112 static int opaque_ls P((char *, int));
113 static int tapedev_is P((void));
114 static int are_dumps_compressed P((void));
115 int main P((int, char **));
117 static REMOVE_ITEM *remove_files(remove)
123 dbprintf(("%s: removing index file: %s\n",
124 debug_prefix_time(NULL), remove->filename));
125 unlink(remove->filename);
126 amfree(remove->filename);
128 remove = remove->next;
134 static char *uncompress_file(filename_gz, emsg)
139 char *filename = NULL;
140 struct stat stat_filename;
144 filename = stralloc(filename_gz);
145 len = strlen(filename);
146 if(len > 3 && strcmp(&(filename[len-3]),".gz")==0) {
147 filename[len-3]='\0';
148 } else if(len > 2 && strcmp(&(filename[len-2]),".Z")==0) {
149 filename[len-2]='\0';
152 /* uncompress the file */
153 result=stat(filename,&stat_filename);
154 if(result==-1 && errno==ENOENT) { /* file does not exist */
155 REMOVE_ITEM *remove_file;
156 cmd = vstralloc(UNCOMPRESS_PATH,
157 #ifdef UNCOMPRESS_OPT
160 " \'", filename_gz, "\'",
163 " > ", "\'", filename, "\'",
165 dbprintf(("%s: uncompress command: %s\n",
166 debug_prefix_time(NULL), cmd));
167 if (system(cmd)!=0) {
169 *emsg = vstralloc("\"", cmd, "\" failed", NULL);
177 /* add at beginning */
178 remove_file = (REMOVE_ITEM *)alloc(sizeof(REMOVE_ITEM));
179 remove_file->filename = stralloc(filename);
180 remove_file->next = uncompress_remove;
181 uncompress_remove = remove_file;
182 } else if(!S_ISREG((stat_filename.st_mode))) {
184 *emsg = vstralloc("\"", filename, "\" is not a regular file", NULL);
190 /* already uncompressed */
196 /* find all matching entries in a dump listing */
197 /* return -1 if error */
198 static int process_ls_dump(dir, dump_item, recursive, emsg)
200 DUMP_ITEM *dump_item;
205 char *old_line = NULL;
206 char *filename = NULL;
208 char *dir_slash = NULL;
214 if (strcmp(dir, "/") == 0) {
215 dir_slash = stralloc(dir);
217 dir_slash = stralloc2(dir, "/");
220 filename_gz = getindexfname(dump_hostname, disk_name, dump_item->date,
222 if((filename = uncompress_file(filename_gz, emsg)) == NULL) {
229 if((fp = fopen(filename,"r"))==0) {
231 *emsg = stralloc(strerror(errno));
236 len_dir_slash=strlen(dir_slash);
238 for(; (line = agets(fp)) != NULL; free(line)) {
239 if(strncmp(dir_slash, line, len_dir_slash) == 0) {
241 s = line + len_dir_slash;
243 while(ch && ch != '/') ch = *s++;/* find end of the file name */
249 if(old_line == NULL || strcmp(line, old_line) != 0) {
250 add_dir_list_item(dump_item, line);
265 /* send a 1 line reply to the client */
266 printf_arglist_function1(static void reply, int, n, char *, fmt)
271 buf = alloc(str_buffer_size);
273 arglist_start(args, fmt);
274 snprintf(buf, str_buffer_size, "%03d ", n);
275 vsnprintf(buf+4, str_buffer_size-4, fmt, args);
278 if (printf("%s\r\n", buf) < 0)
280 dbprintf(("%s: ! error %d (%s) in printf\n",
281 debug_prefix_time(NULL), errno, strerror(errno)));
282 uncompress_remove = remove_files(uncompress_remove);
285 if (fflush(stdout) != 0)
287 dbprintf(("%s: ! error %d (%s) in fflush\n",
288 debug_prefix_time(NULL), errno, strerror(errno)));
289 uncompress_remove = remove_files(uncompress_remove);
292 dbprintf(("%s: < %s\n", debug_prefix_time(NULL), buf));
296 static void lreply_backend(int flush, int n, char *fmt, va_list args) {
299 buf = alloc(str_buffer_size);
301 snprintf(buf, str_buffer_size, "%03d-", n);
302 vsnprintf(buf+4, str_buffer_size-4, fmt, args);
304 if (printf("%s\r\n", buf) < 0)
306 dbprintf(("%s: ! error %d (%s) in printf\n",
307 debug_prefix_time(NULL), errno, strerror(errno)));
308 uncompress_remove = remove_files(uncompress_remove);
311 if (flush && fflush(stdout) != 0)
313 dbprintf(("%s: ! error %d (%s) in fflush\n",
314 debug_prefix_time(NULL), errno, strerror(errno)));
315 uncompress_remove = remove_files(uncompress_remove);
319 dbprintf(("%s: < %s\n", debug_prefix_time(NULL), buf));
323 /* send one line of a multi-line response */
324 printf_arglist_function1(static void lreply, int, n, char *, fmt)
328 arglist_start(args, fmt);
329 lreply_backend(1, n, fmt, args);
334 /* send one line of a multi-line response */
335 printf_arglist_function1(static void fast_lreply, int, n, char *, fmt)
339 arglist_start(args, fmt);
340 lreply_backend(0, n, fmt, args);
345 /* see if hostname is valid */
346 /* valid is defined to be that there is an index directory for it */
347 /* also do a security check on the requested dump hostname */
348 /* to restrict access to index records if required */
349 /* return -1 if not okay */
350 static int is_dump_host_valid(host)
353 struct stat dir_stat;
357 if (config_name == NULL) {
358 reply(501, "Must set config before setting host.");
363 /* only let a client restore itself for now unless it is the server */
364 if (strcasecmp(remote_hostname, local_hostname) == 0)
366 if (strcasecmp(remote_hostname, host) != 0)
369 "You don't have the necessary permissions to set dump host to %s.",
375 /* check that the config actually handles that host */
376 ihost = lookup_host(host);
378 reply(501, "Host %s is not in your disklist.", host);
382 /* assume an index dir already */
383 fn = getindexfname(host, NULL, NULL, 0);
384 if (stat (fn, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
385 reply(501, "No index records for host: %s. Have you enabled indexing?", host);
395 static int is_disk_valid(disk)
399 struct stat dir_stat;
402 if (config_name == NULL || dump_hostname == NULL) {
403 reply(501, "Must set config,host before setting disk.");
407 /* check that the config actually handles that disk */
408 idisk = lookup_disk(dump_hostname, disk);
410 reply(501, "Disk %s:%s is not in your disklist.", dump_hostname, disk);
414 /* assume an index dir already */
415 fn = getindexfname(dump_hostname, disk, NULL, 0);
416 if (stat (fn, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
417 reply(501, "No index records for disk: %s. Invalid?", disk);
427 static int is_config_valid(config)
434 struct stat dir_stat;
436 /* check that the config actually exists */
437 if (config == NULL) {
438 reply(501, "Must set config first.");
443 conffile = stralloc2(config_dir, CONFFILE_NAME);
444 if (read_conffile(conffile)) {
445 reply(501, "Could not read config file %s!", conffile);
451 conf_diskfile = getconf_str(CNF_DISKFILE);
452 if (*conf_diskfile == '/') {
453 conf_diskfile = stralloc(conf_diskfile);
455 conf_diskfile = stralloc2(config_dir, conf_diskfile);
457 if (read_diskfile(conf_diskfile, &disk_list) < 0) {
458 reply(501, "Could not read disk file %s!", conf_diskfile);
459 amfree(conf_diskfile);
462 amfree(conf_diskfile);
464 conf_tapelist = getconf_str(CNF_TAPELIST);
465 if (*conf_tapelist == '/') {
466 conf_tapelist = stralloc(conf_tapelist);
468 conf_tapelist = stralloc2(config_dir, conf_tapelist);
470 if(read_tapelist(conf_tapelist)) {
471 reply(501, "Could not read tapelist file %s!", conf_tapelist);
472 amfree(conf_tapelist);
475 amfree(conf_tapelist);
477 output_find = find_dump(1, &disk_list);
478 sort_find_result("DLKHpB", &output_find);
480 conf_indexdir = getconf_str(CNF_INDEXDIR);
481 if(*conf_indexdir == '/') {
482 conf_indexdir = stralloc(conf_indexdir);
484 conf_indexdir = stralloc2(config_dir, conf_indexdir);
486 if (stat (conf_indexdir, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
487 reply(501, "Index directory %s does not exist", conf_indexdir);
488 amfree(conf_indexdir);
491 amfree(conf_indexdir);
497 static int build_disk_table()
499 char date[3 * NUM_STR_SIZE + 2 + 1];
504 find_result_t *find_output;
506 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
507 reply(590, "Must set config,host,disk before building disk table");
516 for(find_output = output_find;
518 find_output = find_output->next) {
519 if(strcasecmp(dump_hostname, find_output->hostname) == 0 &&
520 strcmp(disk_name , find_output->diskname) == 0 &&
521 strcmp("OK" , find_output->status) == 0) {
523 if(strcmp("--", find_output->partnum)){
524 partnum = atoi(find_output->partnum);
527 * The sort order puts holding disk entries first. We want to
528 * use them if at all possible, so ignore any other entries
529 * for the same datestamp after we see a holding disk entry
530 * (as indicated by a filenum of zero).
532 if(find_output->datestamp == last_datestamp &&
533 find_output->level == last_level &&
534 partnum == last_partnum && last_filenum == 0) {
537 last_datestamp = find_output->datestamp;
538 last_filenum = find_output->filenum;
539 last_level = find_output->level;
540 last_partnum = partnum;
541 snprintf(date, sizeof(date), "%04d-%02d-%02d",
542 find_output->datestamp/10000,
543 (find_output->datestamp/100) %100,
544 find_output->datestamp %100);
545 add_dump(date, find_output->level, find_output->label,
546 find_output->filenum, partnum);
547 dbprintf(("%s: - %s %d %s %d %d\n",
548 debug_prefix_time(NULL), date, find_output->level,
549 find_output->label, find_output->filenum, partnum));
556 static int disk_history_list()
560 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
561 reply(502, "Must set config,host,disk before listing history");
565 lreply(200, " Dump history for config \"%s\" host \"%s\" disk \"%s\"",
566 config_name, dump_hostname, disk_name);
568 for (item=first_dump(); item!=NULL; item=next_dump(item)){
569 char *tapelist_str = marshal_tapelist(item->tapes, 1);
571 if(am_has_feature(their_features, fe_amindexd_marshall_in_DHST)){
572 str_buffer_size = strlen(item->date) + NUM_STR_SIZE +
573 strlen(tapelist_str) + 9;
574 lreply(201, " %s %d %s", item->date, item->level, tapelist_str);
577 str_buffer_size = strlen(item->date) + NUM_STR_SIZE +
578 strlen(tapelist_str) + NUM_STR_SIZE + 9;
579 lreply(201, " %s %d %s %d", item->date, item->level, tapelist_str,
582 str_buffer_size = STR_SIZE;
585 reply(200, "Dump history for config \"%s\" host \"%s\" disk \"%s\"",
586 config_name, dump_hostname, disk_name);
592 /* is the directory dir backed up - dir assumed complete relative to
594 /* opaque version of command */
595 static int is_dir_valid_opaque(dir)
603 char *filename_gz = NULL;
604 char *filename = NULL;
606 static char *emsg = NULL;
608 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
609 reply(502, "Must set config,host,disk before asking about directories");
612 if (target_date == NULL) {
613 reply(502, "Must set date before asking about directories");
617 /* scan through till we find first dump on or before date */
618 for (item=first_dump(); item!=NULL; item=next_dump(item))
619 if (strcmp(item->date, target_date) <= 0)
624 /* no dump for given date */
625 reply(500, "No dumps available on or before date \"%s\"", target_date);
629 if(strcmp(dir, "/") == 0) {
630 ldir = stralloc(dir);
632 ldir = stralloc2(dir, "/");
634 ldir_len = strlen(ldir);
636 /* go back till we hit a level 0 dump */
640 filename_gz = getindexfname(dump_hostname, disk_name,
641 item->date, item->level);
642 if((filename = uncompress_file(filename_gz, &emsg)) == NULL) {
643 reply(599, "System error %s", emsg);
650 dbprintf(("%s: f %s\n", debug_prefix_time(NULL), filename));
651 if ((fp = fopen(filename, "r")) == NULL) {
652 reply(599, "System error %s", strerror(errno));
657 for(; (line = agets(fp)) != NULL; free(line)) {
658 if (strncmp(line, ldir, ldir_len) != 0) {
659 continue; /* not found yet */
669 last_level = item->level;
672 item=next_dump(item);
673 } while ((item != NULL) && (item->level >= last_level));
674 } while (item != NULL);
678 reply(500, "\"%s\" is an invalid directory", dir);
682 static int opaque_ls(dir,recursive)
686 DUMP_ITEM *dump_item;
689 static char *emsg = NULL;
690 am_feature_e marshall_feature;
693 marshall_feature = fe_amindexd_marshall_in_ORLD;
695 marshall_feature = fe_amindexd_marshall_in_OLSD;
700 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
701 reply(502, "Must set config,host,disk before listing a directory");
704 if (target_date == NULL) {
705 reply(502, "Must set date before listing a directory");
709 /* scan through till we find first dump on or before date */
710 for (dump_item=first_dump(); dump_item!=NULL; dump_item=next_dump(dump_item))
711 if (strcmp(dump_item->date, target_date) <= 0)
714 if (dump_item == NULL)
716 /* no dump for given date */
717 reply(500, "No dumps available on or before date \"%s\"", target_date);
721 /* get data from that dump */
722 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
723 reply(599, "System error %s", emsg);
728 /* go back processing higher level dumps till we hit a level 0 dump */
729 last_level = dump_item->level;
730 while ((last_level != 0) && ((dump_item=next_dump(dump_item)) != NULL))
732 if (dump_item->level < last_level)
734 last_level = dump_item->level;
735 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
736 reply(599, "System error %s", emsg);
743 /* return the information to the caller */
744 lreply(200, " Opaque list of %s", dir);
745 for (dir_item = get_dir_list(); dir_item != NULL;
746 dir_item = dir_item->next) {
749 if (!am_has_feature(their_features, marshall_feature) &&
750 (num_entries(dir_item->dump->tapes) > 1 ||
751 dir_item->dump->tapes->numfiles > 1)) {
752 fast_lreply(501, " ERROR: Split dumps not supported"
753 " with old version of amrecover.");
756 if (am_has_feature(their_features, marshall_feature)) {
757 tapelist_str = marshal_tapelist(dir_item->dump->tapes, 1);
759 tapelist_str = dir_item->dump->tapes->label;
762 if((!recursive && am_has_feature(their_features,
763 fe_amindexd_fileno_in_OLSD))
765 (recursive && am_has_feature(their_features,
766 fe_amindexd_fileno_in_ORLD))) {
767 str_buffer_size = strlen(dir_item->dump->date) +
768 NUM_STR_SIZE + strlen(tapelist_str) +
769 strlen(dir_item->path) + NUM_STR_SIZE + 9;
770 fast_lreply(201, " %s %d %s %d %s",
771 dir_item->dump->date, dir_item->dump->level,
772 tapelist_str, dir_item->dump->file,
776 str_buffer_size = strlen(dir_item->dump->date) +
777 NUM_STR_SIZE + strlen(tapelist_str) +
778 strlen(dir_item->path) + 9;
779 fast_lreply(201, " %s %d %s %s",
780 dir_item->dump->date, dir_item->dump->level,
781 tapelist_str, dir_item->path);
783 if(am_has_feature(their_features, marshall_feature)) {
784 amfree(tapelist_str);
786 str_buffer_size = STR_SIZE;
789 reply(200, " Opaque list of %s", dir);
796 /* returns the value of changer or tapedev from the amanda.conf file if set,
797 otherwise reports an error */
798 static int tapedev_is()
802 /* check state okay to do this */
803 if (config_name == NULL) {
804 reply(501, "Must set config before asking about tapedev.");
808 /* use amrecover_changer if possible */
809 if ((result = getconf_str(CNF_AMRECOVER_CHANGER)) != NULL &&
811 dbprintf(("%s: tapedev_is amrecover_changer: %s\n",
812 debug_prefix_time(NULL), result));
817 /* use changer if possible */
818 if ((result = getconf_str(CNF_TPCHANGER)) != NULL && *result != '\0') {
819 dbprintf(("%s: tapedev_is tpchanger: %s\n",
820 debug_prefix_time(NULL), result));
825 /* get tapedev value */
826 if ((result = getconf_str(CNF_TAPEDEV)) != NULL && *result != '\0') {
827 dbprintf(("%s: tapedev_is tapedev: %s\n",
828 debug_prefix_time(NULL), result));
833 dbprintf(("%s: No tapedev or changer in config site.\n",
834 debug_prefix_time(NULL)));
835 reply(501, "Tapedev or changer not set in config file.");
840 /* returns YES if dumps for disk are compressed, NO if not */
841 static int are_dumps_compressed()
845 /* check state okay to do this */
846 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
847 reply(501, "Must set config,host,disk name before asking about dumps.");
851 /* now go through the list of disks and find which have indexes */
852 for (diskp = disk_list.head; diskp != NULL; diskp = diskp->next)
853 if ((strcasecmp(diskp->host->hostname, dump_hostname) == 0)
854 && (strcmp(diskp->name, disk_name) == 0))
859 reply(501, "Couldn't find host/disk in disk file.");
863 /* send data to caller */
864 if (diskp->compress == COMP_NONE)
876 char *line = NULL, *part = NULL;
879 char *cmd_undo, cmd_undo_ch;
881 struct sockaddr_in his_addr;
882 struct hostent *his_name;
886 int user_validated = 0;
888 char *pgm = "amindexd"; /* in case argv[0] is not set */
894 * When called via inetd, it is not uncommon to forget to put the
895 * argv[0] value on the config line. On some systems (e.g. Solaris)
896 * this causes argv and/or argv[0] to be NULL, so we have to be
897 * careful getting our name.
899 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
900 if((pgm = strrchr(argv[0], '/')) != NULL) {
909 /* Don't die when child closes pipe */
910 signal(SIGPIPE, SIG_IGN);
914 /* we'd rather not run as root */
917 if(client_uid == (uid_t) -1) {
918 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
921 initgroups(CLIENT_LOGIN, client_gid);
926 #endif /* FORCE_USERID */
929 dbprintf(("%s: version %s\n", get_pname(), version()));
932 error("argv == NULL\n");
935 if (! (argc >= 1 && argv[0] != NULL)) {
936 dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
937 debug_prefix_time(NULL)));
952 if(argc > 0 && strcmp(*argv, "-t") == 0) {
959 config_name = stralloc(*argv);
960 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
965 if(gethostname(local_hostname, sizeof(local_hostname)-1) == -1)
966 error("gethostname: %s", strerror(errno));
967 local_hostname[sizeof(local_hostname)-1] = '\0';
969 /* now trim domain off name */
972 while(ch && ch != '.') ch = *s++;
977 * Fake the remote address as the local address enough to get
978 * through the security check.
980 his_name = gethostbyname(local_hostname);
981 if(his_name == NULL) {
982 error("gethostbyname(%s) failed\n", local_hostname);
984 assert(his_name->h_addrtype == AF_INET);
985 his_addr.sin_family = his_name->h_addrtype;
986 his_addr.sin_port = htons(0);
987 memcpy((char *)&his_addr.sin_addr.s_addr,
988 (char *)his_name->h_addr_list[0], his_name->h_length);
990 /* who are we talking to? */
991 socklen = sizeof (his_addr);
992 if (getpeername(0, (struct sockaddr *)&his_addr, &socklen) == -1)
993 error("getpeername: %s", strerror(errno));
995 if (his_addr.sin_family != AF_INET || ntohs(his_addr.sin_port) == 20)
997 error("connection rejected from %s family %d port %d",
998 inet_ntoa(his_addr.sin_addr), his_addr.sin_family,
999 htons(his_addr.sin_port));
1001 if ((his_name = gethostbyaddr((char *)&(his_addr.sin_addr),
1002 sizeof(his_addr.sin_addr),
1003 AF_INET)) == NULL) {
1004 error("gethostbyaddr(%s): hostname lookup failed",
1005 inet_ntoa(his_addr.sin_addr));
1007 fp = s = his_name->h_name;
1009 while(ch && ch != '.') ch = *s++;
1011 remote_hostname = newstralloc(remote_hostname, fp);
1014 /* clear these so we can detect when the have not been set by the client */
1015 amfree(dump_hostname);
1017 amfree(target_date);
1019 our_features = am_init_feature_set();
1020 their_features = am_set_default_feature_set();
1022 if (config_name != NULL && is_config_valid(config_name) != -1) {
1026 reply(220, "%s AMANDA index server (%s) ready.", local_hostname,
1029 /* a real simple parser since there are only a few commands */
1032 /* get a line from the client */
1035 if((part = agets(stdin)) == NULL) {
1037 dbprintf(("%s: ? read error: %s\n",
1038 debug_prefix_time(NULL), strerror(errno)));
1040 dbprintf(("%s: ? unexpected EOF\n",
1041 debug_prefix_time(NULL)));
1044 dbprintf(("%s: ? unprocessed input:\n",
1045 debug_prefix_time(NULL)));
1046 dbprintf(("-----\n"));
1047 dbprintf(("? %s\n", line));
1048 dbprintf(("-----\n"));
1052 uncompress_remove = remove_files(uncompress_remove);
1054 return 1; /* they hung up? */
1057 strappend(line, part);
1063 if(amindexd_debug) {
1064 break; /* we have a whole line */
1066 if((len = strlen(line)) > 0 && line[len-1] == '\r') {
1067 line[len-1] = '\0'; /* zap the '\r' */
1071 * Hmmm. We got a "line" from agets(), which means it saw
1072 * a '\n' (or EOF, etc), but there was not a '\r' before it.
1073 * Put a '\n' back in the buffer and loop for more.
1075 strappend(line, "\n");
1078 dbprintf(("%s: > %s\n", debug_prefix_time(NULL), line));
1084 skip_whitespace(s, ch);
1086 reply(500, "Command not recognised/incorrect: %s", line);
1091 skip_non_whitespace(s, ch);
1092 cmd_undo = s-1; /* for error message */
1093 cmd_undo_ch = *cmd_undo;
1096 skip_whitespace(s, ch); /* find the argument */
1099 skip_non_whitespace(s, ch);
1104 if (!user_validated && strcmp(cmd, "SECURITY") == 0 && arg) {
1105 user_validated = check_security(&his_addr, arg, 0, &errstr);
1106 if(user_validated) {
1107 reply(200, "Access OK");
1111 if (!user_validated) { /* don't tell client the reason, just log it to debug log */
1112 reply(500, "Access not allowed");
1114 dbprintf(("%s: %s\n", debug_prefix_time(NULL), errstr));
1119 if (strcmp(cmd, "QUIT") == 0) {
1121 } else if (strcmp(cmd, "HOST") == 0 && arg) {
1122 /* set host we are restoring */
1124 if (is_dump_host_valid(arg) != -1)
1126 dump_hostname = newstralloc(dump_hostname, arg);
1127 reply(200, "Dump host set to %s.", dump_hostname);
1128 amfree(disk_name); /* invalidate any value */
1131 } else if (strcmp(cmd, "DISK") == 0 && arg) {
1133 if (is_disk_valid(arg) != -1) {
1134 disk_name = newstralloc(disk_name, arg);
1135 if (build_disk_table() != -1) {
1136 reply(200, "Disk set to %s.", disk_name);
1140 } else if (strcmp(cmd, "LISTDISK") == 0) {
1144 if (config_name == NULL || dump_hostname == NULL) {
1145 reply(501, "Must set config, host before listdisk");
1148 lreply(200, " List of disk for device %s on host %s", arg,
1150 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1151 if(strcmp(disk->host->hostname, dump_hostname) == 0 &&
1152 ((disk->device && strcmp(disk->device, arg) == 0) ||
1153 (!disk->device && strcmp(disk->name, arg) == 0))) {
1154 fast_lreply(201, " %s", disk->name);
1159 reply(200, "List of disk for device %s on host %s", arg,
1163 reply(200, "No disk for device %s on host %s", arg,
1168 lreply(200, " List of disk for host %s", dump_hostname);
1169 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1170 if(strcmp(disk->host->hostname, dump_hostname) == 0) {
1171 fast_lreply(201, " %s", disk->name);
1176 reply(200, "List of disk for host %s", dump_hostname);
1179 reply(200, "No disk for host %s", dump_hostname);
1183 } else if (strcmp(cmd, "SCNF") == 0 && arg) {
1185 amfree(config_name);
1187 config_name = newstralloc(config_name, arg);
1188 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
1189 if (is_config_valid(arg) != -1) {
1190 amfree(dump_hostname); /* invalidate any value */
1191 amfree(disk_name); /* invalidate any value */
1192 reply(200, "Config set to %s.", config_name);
1194 amfree(config_name);
1198 } else if (strcmp(cmd, "FEATURES") == 0 && arg) {
1199 char *our_feature_string = NULL;
1200 char *their_feature_string = NULL;
1202 am_release_feature_set(our_features);
1203 am_release_feature_set(their_features);
1204 our_features = am_init_feature_set();
1205 our_feature_string = am_feature_to_string(our_features);
1206 their_feature_string = newstralloc(target_date, arg);
1207 their_features = am_string_to_feature(their_feature_string);
1208 reply(200, "FEATURES %s", our_feature_string);
1209 amfree(our_feature_string);
1210 amfree(their_feature_string);
1212 } else if (strcmp(cmd, "DATE") == 0 && arg) {
1214 target_date = newstralloc(target_date, arg);
1215 reply(200, "Working date set to %s.", target_date);
1217 } else if (strcmp(cmd, "DHST") == 0) {
1218 (void)disk_history_list();
1219 } else if (strcmp(cmd, "OISD") == 0 && arg) {
1220 if (is_dir_valid_opaque(arg) != -1) {
1221 reply(200, "\"%s\" is a valid directory", arg);
1223 } else if (strcmp(cmd, "OLSD") == 0 && arg) {
1224 (void)opaque_ls(arg,0);
1225 } else if (strcmp(cmd, "ORLD") == 0 && arg) {
1226 (void)opaque_ls(arg,1);
1227 } else if (strcmp(cmd, "TAPE") == 0) {
1229 } else if (strcmp(cmd, "DCMP") == 0) {
1230 (void)are_dumps_compressed();
1232 *cmd_undo = cmd_undo_ch; /* restore the command line */
1233 reply(500, "Command not recognised/incorrect: %s", cmd);
1238 uncompress_remove = remove_files(uncompress_remove);
1239 free_find_result(&output_find);
1240 reply(200, "Good bye.");