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.106.2.2 2006/09/27 12:04:09 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"
61 typedef struct REMOVE_ITEM
64 struct REMOVE_ITEM *next;
68 static int from_amandad;
69 static char local_hostname[MAX_HOSTNAME_LENGTH+1]; /* me! */
70 static char *remote_hostname = NULL; /* the client */
71 static char *dump_hostname = NULL; /* machine we are restoring */
72 static char *disk_name; /* disk we are restoring */
73 char *qdisk_name = NULL; /* disk we are restoring */
74 static char *target_date = NULL;
75 static disklist_t disk_list; /* all disks in cur config */
76 static find_result_t *output_find = NULL;
77 static g_option_t *g_options = NULL;
78 static int cmdfdin, cmdfdout;
80 static int amindexd_debug = 0;
82 static REMOVE_ITEM *uncompress_remove = NULL;
83 /* uncompressed files to remove */
85 static am_feature_t *our_features = NULL;
86 static am_feature_t *their_features = NULL;
88 static REMOVE_ITEM *remove_files(REMOVE_ITEM *);
89 static char *uncompress_file(char *, char **);
90 static int process_ls_dump(char *, DUMP_ITEM *, int, char **);
92 static size_t reply_buffer_size = 1;
93 static char *reply_buffer = NULL;
94 static char *amandad_auth = NULL;
98 static void reply(int, char *, ...)
99 __attribute__ ((format (printf, 2, 3)));
100 static void lreply(int, char *, ...)
101 __attribute__ ((format (printf, 2, 3)));
102 static void fast_lreply(int, char *, ...)
103 __attribute__ ((format (printf, 2, 3)));
104 static int is_dump_host_valid(char *);
105 static int is_disk_valid(char *);
106 static int is_config_valid(char *);
107 static int build_disk_table(void);
108 static int disk_history_list(void);
109 static int is_dir_valid_opaque(char *);
110 static int opaque_ls(char *, int);
111 static void opaque_ls_one (DIR_ITEM *dir_item, am_feature_e marshall_feature,
113 static int tapedev_is(void);
114 static int are_dumps_compressed(void);
115 static char *amindexd_nicedate (char *datestamp);
116 static int cmp_date (const char *date1, const char *date2);
117 int main(int, char **);
126 dbprintf(("%s: removing index file: %s\n",
127 debug_prefix_time(NULL), remove->filename));
128 unlink(remove->filename);
129 amfree(remove->filename);
131 remove = remove->next;
143 char *filename = NULL;
144 struct stat stat_filename;
148 filename = stralloc(filename_gz);
149 len = strlen(filename);
150 if(len > 3 && strcmp(&(filename[len-3]),".gz")==0) {
151 filename[len-3]='\0';
152 } else if(len > 2 && strcmp(&(filename[len-2]),".Z")==0) {
153 filename[len-2]='\0';
156 /* uncompress the file */
157 result=stat(filename,&stat_filename);
158 if(result==-1 && errno==ENOENT) { /* file does not exist */
160 REMOVE_ITEM *remove_file;
163 * Check that compressed file exists manually.
165 if (stat(filename_gz, &statbuf) < 0) {
166 *emsg = newvstralloc(*emsg, "Compressed file '",
168 "' is inaccessable: ",
171 dbprintf(("%s\n",*emsg));
176 cmd = vstralloc(UNCOMPRESS_PATH,
177 #ifdef UNCOMPRESS_OPT
180 " \'", filename_gz, "\'",
182 " | (LC_ALL=C; export LC_ALL ; sort) ",
183 " > ", "\'", filename, "\'",
185 dbprintf(("%s: uncompress command: %s\n",
186 debug_prefix_time(NULL), cmd));
187 if (system(cmd) != 0) {
188 *emsg = newvstralloc(*emsg, "\"", cmd, "\" failed", NULL);
196 /* add at beginning */
197 remove_file = (REMOVE_ITEM *)alloc(SIZEOF(REMOVE_ITEM));
198 remove_file->filename = stralloc(filename);
199 remove_file->next = uncompress_remove;
200 uncompress_remove = remove_file;
201 } else if(!S_ISREG((stat_filename.st_mode))) {
203 *emsg = vstralloc("\"", filename, "\" is not a regular file", NULL);
213 /* find all matching entries in a dump listing */
214 /* return -1 if error */
218 DUMP_ITEM * dump_item,
223 char *old_line = NULL;
224 char *filename = NULL;
226 char *dir_slash = NULL;
230 size_t len_dir_slash;
232 if (strcmp(dir, "/") == 0) {
233 dir_slash = stralloc(dir);
235 dir_slash = stralloc2(dir, "/");
238 filename_gz = getindexfname(dump_hostname, disk_name, dump_item->date,
240 if((filename = uncompress_file(filename_gz, emsg)) == NULL) {
247 if((fp = fopen(filename,"r"))==0) {
249 *emsg = stralloc(strerror(errno));
254 len_dir_slash=strlen(dir_slash);
256 while ((line = agets(fp)) != NULL) {
257 if (line[0] != '\0') {
258 if(strncmp(dir_slash, line, len_dir_slash) == 0) {
260 s = line + len_dir_slash;
262 while(ch && ch != '/')
263 ch = *s++;/* find end of the file name */
269 if(old_line == NULL || strcmp(line, old_line) != 0) {
270 add_dir_list_item(dump_item, line);
277 /*@i@*/ amfree(line);
280 /*@i@*/ amfree(old_line);
286 /* send a 1 line reply to the client */
287 printf_arglist_function1(static void reply, int, n, char *, fmt)
293 reply_buffer = alloc(reply_buffer_size);
296 arglist_start(args, fmt);
297 len = vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
300 if (len > -1 && (size_t)len < reply_buffer_size-1)
303 reply_buffer_size *= 2;
304 amfree(reply_buffer);
305 reply_buffer = alloc(reply_buffer_size);
308 if (fprintf(cmdout,"%03d %s\r\n", n, reply_buffer) < 0)
310 dbprintf(("%s: ! error %d (%s) in printf\n",
311 debug_prefix_time(NULL), errno, strerror(errno)));
312 uncompress_remove = remove_files(uncompress_remove);
315 if (fflush(cmdout) != 0)
317 dbprintf(("%s: ! error %d (%s) in fflush\n",
318 debug_prefix_time(NULL), errno, strerror(errno)));
319 uncompress_remove = remove_files(uncompress_remove);
322 dbprintf(("%s: < %03d %s\n", debug_prefix_time(NULL), n, reply_buffer));
325 /* send one line of a multi-line response */
326 printf_arglist_function1(static void lreply, int, n, char *, fmt)
332 reply_buffer = alloc(reply_buffer_size);
335 arglist_start(args, fmt);
336 len = vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
339 if (len > -1 && (size_t)len < reply_buffer_size-1)
342 reply_buffer_size *= 2;
343 amfree(reply_buffer);
344 reply_buffer = alloc(reply_buffer_size);
347 if (fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0)
349 dbprintf(("%s: ! error %d (%s) in printf\n",
350 debug_prefix_time(NULL), errno, strerror(errno)));
351 uncompress_remove = remove_files(uncompress_remove);
354 if (fflush(cmdout) != 0)
356 dbprintf(("%s: ! error %d (%s) in fflush\n",
357 debug_prefix_time(NULL), errno, strerror(errno)));
358 uncompress_remove = remove_files(uncompress_remove);
362 dbprintf(("%s: < %03d-%s\n", debug_prefix_time(NULL), n, reply_buffer));
366 /* send one line of a multi-line response */
367 printf_arglist_function1(static void fast_lreply, int, n, char *, fmt)
373 reply_buffer = alloc(reply_buffer_size);
376 arglist_start(args, fmt);
377 len = vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
380 if (len > -1 && (size_t)len < reply_buffer_size-1)
383 reply_buffer_size *= 2;
384 amfree(reply_buffer);
385 reply_buffer = alloc(reply_buffer_size);
388 if (fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0)
390 dbprintf(("%s: ! error %d (%s) in printf\n",
391 debug_prefix_time(NULL), errno, strerror(errno)));
392 uncompress_remove = remove_files(uncompress_remove);
396 dbprintf(("%s: < %03d-%s\n", debug_prefix_time(NULL), n, reply_buffer));
399 /* see if hostname is valid */
400 /* valid is defined to be that there is an index directory for it */
401 /* also do a security check on the requested dump hostname */
402 /* to restrict access to index records if required */
403 /* return -1 if not okay */
408 struct stat dir_stat;
412 if (config_name == NULL) {
413 reply(501, "Must set config before setting host.");
418 /* only let a client restore itself for now unless it is the server */
419 if (strcasecmp(remote_hostname, local_hostname) == 0)
421 if (strcasecmp(remote_hostname, host) != 0)
424 "You don't have the necessary permissions to set dump host to %s.",
430 /* check that the config actually handles that host */
431 ihost = lookup_host(host);
433 reply(501, "Host %s is not in your disklist.", host);
437 /* assume an index dir already */
438 fn = getindexfname(host, NULL, NULL, 0);
439 if (stat (fn, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
440 reply(501, "No index records for host: %s. Have you enabled indexing?", host);
455 struct stat dir_stat;
459 if (config_name == NULL) {
460 reply(501, "Must set config,host before setting disk.");
463 else if (dump_hostname == NULL) {
464 reply(501, "Must set host before setting disk.");
468 /* check that the config actually handles that disk */
469 idisk = lookup_disk(dump_hostname, disk);
471 qdisk = quote_string(disk);
472 reply(501, "Disk %s:%s is not in your disklist.", dump_hostname, qdisk);
477 /* assume an index dir already */
478 fn = getindexfname(dump_hostname, disk, NULL, 0);
479 if (stat (fn, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
480 qdisk = quote_string(disk);
481 reply(501, "No index records for disk: %s. Invalid?", qdisk);
500 struct stat dir_stat;
502 /* check that the config actually exists */
503 if (config == NULL) {
504 reply(501, "Must set config first.");
509 conffile = stralloc2(config_dir, CONFFILE_NAME);
510 if (read_conffile(conffile)) {
511 reply(501, "Could not read config file %s!", conffile);
517 conf_diskfile = getconf_str(CNF_DISKFILE);
518 if (*conf_diskfile == '/') {
519 conf_diskfile = stralloc(conf_diskfile);
521 conf_diskfile = stralloc2(config_dir, conf_diskfile);
523 if (read_diskfile(conf_diskfile, &disk_list) < 0) {
524 reply(501, "Could not read disk file %s!", conf_diskfile);
525 amfree(conf_diskfile);
528 amfree(conf_diskfile);
530 conf_tapelist = getconf_str(CNF_TAPELIST);
531 if (*conf_tapelist == '/') {
532 conf_tapelist = stralloc(conf_tapelist);
534 conf_tapelist = stralloc2(config_dir, conf_tapelist);
536 if(read_tapelist(conf_tapelist)) {
537 reply(501, "Could not read tapelist file %s!", conf_tapelist);
538 amfree(conf_tapelist);
541 amfree(conf_tapelist);
543 dbrename(config, DBG_SUBDIR_SERVER);
545 output_find = find_dump(1, &disk_list);
546 sort_find_result("DLKHpB", &output_find);
548 conf_indexdir = getconf_str(CNF_INDEXDIR);
549 if(*conf_indexdir == '/') {
550 conf_indexdir = stralloc(conf_indexdir);
552 conf_indexdir = stralloc2(config_dir, conf_indexdir);
554 if (stat (conf_indexdir, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
555 reply(501, "Index directory %s does not exist", conf_indexdir);
556 amfree(conf_indexdir);
559 amfree(conf_indexdir);
566 build_disk_table(void)
569 char *last_timestamp;
573 find_result_t *find_output;
575 if (config_name == NULL) {
576 reply(590, "Must set config,host,disk before building disk table");
579 else if (dump_hostname == NULL) {
580 reply(590, "Must set host,disk before building disk table");
583 else if (disk_name == NULL) {
584 reply(590, "Must set disk before building disk table");
589 last_timestamp = NULL;
590 last_filenum = (off_t)-1;
593 for(find_output = output_find;
595 find_output = find_output->next) {
596 if(strcasecmp(dump_hostname, find_output->hostname) == 0 &&
597 strcmp(disk_name , find_output->diskname) == 0 &&
598 strcmp("OK" , find_output->status) == 0) {
600 if(strcmp("--", find_output->partnum)){
601 partnum = atoi(find_output->partnum);
604 * The sort order puts holding disk entries first. We want to
605 * use them if at all possible, so ignore any other entries
606 * for the same datestamp after we see a holding disk entry
607 * (as indicated by a filenum of zero).
610 strcmp(find_output->timestamp, last_timestamp) == 0 &&
611 find_output->level == last_level &&
612 partnum == last_partnum && last_filenum == 0) {
615 last_timestamp = find_output->timestamp;
616 last_filenum = find_output->filenum;
617 last_level = find_output->level;
618 last_partnum = partnum;
619 date = amindexd_nicedate(find_output->timestamp);
620 add_dump(date, find_output->level, find_output->label,
621 find_output->filenum, partnum);
622 dbprintf(("%s: - %s %d %s " OFF_T_FMT " %d\n",
623 debug_prefix_time(NULL), date, find_output->level,
625 (OFF_T_FMT_TYPE)find_output->filenum,
634 disk_history_list(void)
639 if (config_name == NULL) {
640 reply(502, "Must set config,host,disk before listing history");
643 else if (dump_hostname == NULL) {
644 reply(502, "Must set host,disk before listing history");
647 else if (disk_name == NULL) {
648 reply(502, "Must set disk before listing history");
652 lreply(200, " Dump history for config \"%s\" host \"%s\" disk %s",
653 config_name, dump_hostname, qdisk_name);
655 for (item=first_dump(); item!=NULL; item=next_dump(item)){
656 char *tapelist_str = marshal_tapelist(item->tapes, 1);
658 strncpy(date, item->date, 20);
660 if(!am_has_feature(their_features,fe_amrecover_timestamp))
663 if(am_has_feature(their_features, fe_amindexd_marshall_in_DHST)){
664 lreply(201, " %s %d %s", date, item->level, tapelist_str);
667 lreply(201, " %s %d %s " OFF_T_FMT, date, item->level,
668 tapelist_str, (OFF_T_FMT_TYPE)item->file);
670 amfree(tapelist_str);
673 reply(200, "Dump history for config \"%s\" host \"%s\" disk %s",
674 config_name, dump_hostname, qdisk_name);
681 * is the directory dir backed up - dir assumed complete relative to
684 /* opaque version of command */
694 char *filename_gz = NULL;
695 char *filename = NULL;
697 static char *emsg = NULL;
699 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
700 reply(502, "Must set config,host,disk before asking about directories");
703 else if (dump_hostname == NULL) {
704 reply(502, "Must set host,disk before asking about directories");
707 else if (disk_name == NULL) {
708 reply(502, "Must set disk before asking about directories");
711 else if (target_date == NULL) {
712 reply(502, "Must set date before asking about directories");
716 /* scan through till we find first dump on or before date */
717 for (item=first_dump(); item!=NULL; item=next_dump(item))
718 if (cmp_date(item->date, target_date) <= 0)
723 /* no dump for given date */
724 reply(500, "No dumps available on or before date \"%s\"", target_date);
728 if(strcmp(dir, "/") == 0) {
729 ldir = stralloc(dir);
731 ldir = stralloc2(dir, "/");
733 ldir_len = strlen(ldir);
735 /* go back till we hit a level 0 dump */
739 filename_gz = getindexfname(dump_hostname, disk_name,
740 item->date, item->level);
741 if((filename = uncompress_file(filename_gz, &emsg)) == NULL) {
742 reply(599, "System error %s", emsg);
749 dbprintf(("%s: f %s\n", debug_prefix_time(NULL), filename));
750 if ((fp = fopen(filename, "r")) == NULL) {
751 reply(599, "System error %s", strerror(errno));
756 for(; (line = agets(fp)) != NULL; free(line)) {
759 if (strncmp(line, ldir, ldir_len) != 0) {
760 continue; /* not found yet */
770 last_level = item->level;
773 item=next_dump(item);
774 } while ((item != NULL) && (item->level >= last_level));
775 } while (item != NULL);
779 reply(500, "\"%s\" is an invalid directory", dir);
788 DUMP_ITEM *dump_item;
790 int level, last_level;
791 static char *emsg = NULL;
792 am_feature_e marshall_feature;
795 marshall_feature = fe_amindexd_marshall_in_ORLD;
797 marshall_feature = fe_amindexd_marshall_in_OLSD;
802 if (config_name == NULL) {
803 reply(502, "Must set config,host,disk before listing a directory");
806 else if (dump_hostname == NULL) {
807 reply(502, "Must set host,disk before listing a directory");
810 else if (disk_name == NULL) {
811 reply(502, "Must set disk before listing a directory");
814 else if (target_date == NULL) {
815 reply(502, "Must set date before listing a directory");
819 /* scan through till we find first dump on or before date */
820 for (dump_item=first_dump(); dump_item!=NULL; dump_item=next_dump(dump_item))
821 if (cmp_date(dump_item->date, target_date) <= 0)
824 if (dump_item == NULL)
826 /* no dump for given date */
827 reply(500, "No dumps available on or before date \"%s\"", target_date);
831 /* get data from that dump */
832 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
833 reply(599, "System error %s", emsg);
838 /* go back processing higher level dumps till we hit a level 0 dump */
839 last_level = dump_item->level;
840 while ((last_level != 0) && ((dump_item=next_dump(dump_item)) != NULL))
842 if (dump_item->level < last_level)
844 last_level = dump_item->level;
845 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
846 reply(599, "System error %s", emsg);
853 /* return the information to the caller */
854 lreply(200, " Opaque list of %s", dir);
855 for(level=0; level<=9; level++) {
856 for (dir_item = get_dir_list(); dir_item != NULL;
857 dir_item = dir_item->next) {
859 if(dir_item->dump->level == level) {
860 if (!am_has_feature(their_features, marshall_feature) &&
861 (num_entries(dir_item->dump->tapes) > 1 ||
862 dir_item->dump->tapes->numfiles > 1)) {
863 fast_lreply(501, " ERROR: Split dumps not supported"
864 " with old version of amrecover.");
868 opaque_ls_one(dir_item, marshall_feature, recursive);
873 reply(200, " Opaque list of %s", dir);
881 am_feature_e marshall_feature,
888 if (am_has_feature(their_features, marshall_feature)) {
889 tapelist_str = marshal_tapelist(dir_item->dump->tapes, 1);
891 tapelist_str = dir_item->dump->tapes->label;
894 strncpy(date, dir_item->dump->date, 20);
896 if(!am_has_feature(their_features,fe_amrecover_timestamp))
899 qpath = quote_string(dir_item->path);
900 if((!recursive && am_has_feature(their_features,
901 fe_amindexd_fileno_in_OLSD)) ||
902 (recursive && am_has_feature(their_features,
903 fe_amindexd_fileno_in_ORLD))) {
904 fast_lreply(201, " %s %d %s " OFF_T_FMT " %s",
906 dir_item->dump->level,
908 (OFF_T_FMT_TYPE)dir_item->dump->file,
913 fast_lreply(201, " %s %d %s %s",
914 date, dir_item->dump->level,
915 tapelist_str, qpath);
918 if(am_has_feature(their_features, marshall_feature)) {
919 amfree(tapelist_str);
924 * returns the value of changer or tapedev from the amanda.conf file if set,
925 * otherwise reports an error
933 /* check state okay to do this */
934 if (config_name == NULL) {
935 reply(501, "Must set config before asking about tapedev.");
939 /* use amrecover_changer if possible */
940 if ((result = getconf_str(CNF_AMRECOVER_CHANGER)) != NULL &&
942 dbprintf(("%s: tapedev_is amrecover_changer: %s\n",
943 debug_prefix_time(NULL), result));
948 /* use changer if possible */
949 if ((result = getconf_str(CNF_TPCHANGER)) != NULL && *result != '\0') {
950 dbprintf(("%s: tapedev_is tpchanger: %s\n",
951 debug_prefix_time(NULL), result));
956 /* get tapedev value */
957 if ((result = getconf_str(CNF_TAPEDEV)) != NULL && *result != '\0') {
958 dbprintf(("%s: tapedev_is tapedev: %s\n",
959 debug_prefix_time(NULL), result));
964 dbprintf(("%s: No tapedev or changer in config site.\n",
965 debug_prefix_time(NULL)));
966 reply(501, "Tapedev or changer not set in config file.");
971 /* returns YES if dumps for disk are compressed, NO if not */
973 are_dumps_compressed(void)
977 /* check state okay to do this */
978 if (config_name == NULL) {
979 reply(501, "Must set config,host,disk name before asking about dumps.");
982 else if (dump_hostname == NULL) {
983 reply(501, "Must set host,disk name before asking about dumps.");
986 else if (disk_name == NULL) {
987 reply(501, "Must set disk name before asking about dumps.");
991 /* now go through the list of disks and find which have indexes */
992 for (diskp = disk_list.head; diskp != NULL; diskp = diskp->next) {
993 if ((strcasecmp(diskp->host->hostname, dump_hostname) == 0)
994 && (strcmp(diskp->name, disk_name) == 0)) {
1000 reply(501, "Couldn't find host/disk in disk file.");
1004 /* send data to caller */
1005 if (diskp->compress == COMP_NONE)
1018 char *line = NULL, *part = NULL;
1021 char *cmd_undo, cmd_undo_ch;
1023 struct sockaddr_in his_addr;
1024 struct hostent *his_name;
1028 int user_validated = 0;
1029 char *errstr = NULL;
1030 char *pgm = "amindexd"; /* in case argv[0] is not set */
1032 safe_fd(DATA_FD_OFFSET, 2);
1036 * When called via inetd, it is not uncommon to forget to put the
1037 * argv[0] value on the config line. On some systems (e.g. Solaris)
1038 * this causes argv and/or argv[0] to be NULL, so we have to be
1039 * careful getting our name.
1041 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
1042 if((pgm = strrchr(argv[0], '/')) != NULL) {
1051 /* Don't die when child closes pipe */
1052 signal(SIGPIPE, SIG_IGN);
1056 /* we'd rather not run as root */
1058 if(geteuid() == 0) {
1059 if(client_uid == (uid_t) -1) {
1060 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
1065 initgroups(CLIENT_LOGIN, client_gid);
1071 #endif /* FORCE_USERID */
1073 dbopen(DBG_SUBDIR_SERVER);
1074 dbprintf(("%s: version %s\n", get_pname(), version()));
1077 error("argv == NULL\n");
1080 if (! (argc >= 1 && argv[0] != NULL)) {
1081 dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
1082 debug_prefix_time(NULL)));
1097 if(argc > 0 && strcmp(*argv, "-t") == 0) {
1103 if(argc > 0 && strcmp(*argv, "amandad") == 0) {
1108 amandad_auth = *argv;
1119 config_name = stralloc(*argv);
1120 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
1125 if(gethostname(local_hostname, SIZEOF(local_hostname)-1) == -1) {
1126 error("gethostname: %s", strerror(errno));
1129 local_hostname[SIZEOF(local_hostname)-1] = '\0';
1131 /* now trim domain off name */
1134 while(ch && ch != '.') ch = *s++;
1138 if(from_amandad == 0) {
1139 if(amindexd_debug) {
1141 * Fake the remote address as the local address enough to get
1142 * through the security check.
1144 his_name = gethostbyname(local_hostname);
1145 if(his_name == NULL) {
1146 error("gethostbyname(%s) failed\n", local_hostname);
1149 assert((sa_family_t)his_name->h_addrtype == (sa_family_t)AF_INET);
1150 his_addr.sin_family = (sa_family_t)his_name->h_addrtype;
1151 his_addr.sin_port = (in_port_t)htons(0);
1152 memcpy((void *)&his_addr.sin_addr.s_addr,
1153 (void *)his_name->h_addr_list[0],
1154 (size_t)his_name->h_length);
1156 /* who are we talking to? */
1157 socklen = sizeof (his_addr);
1158 if (getpeername(0, (struct sockaddr *)&his_addr, &socklen) == -1)
1159 error("getpeername: %s", strerror(errno));
1161 if ((his_addr.sin_family != (sa_family_t)AF_INET)
1162 || (ntohs(his_addr.sin_port) == 20)) {
1163 error("connection rejected from %s family %d port %d",
1164 inet_ntoa(his_addr.sin_addr), his_addr.sin_family,
1165 htons(his_addr.sin_port));
1168 if ((his_name = gethostbyaddr((char *)&(his_addr.sin_addr),
1169 sizeof(his_addr.sin_addr),
1170 AF_INET)) == NULL) {
1171 error("gethostbyaddr(%s): hostname lookup failed",
1172 inet_ntoa(his_addr.sin_addr));
1175 fp = s = stralloc(his_name->h_name);
1177 while(ch && ch != '.') ch = *s++;
1179 remote_hostname = newstralloc(remote_hostname, fp);
1186 cmdfdout = DATA_FD_OFFSET + 0;
1187 cmdfdin = DATA_FD_OFFSET + 1;
1189 /* read the REQ packet */
1190 for(; (line = agets(stdin)) != NULL; free(line)) {
1191 #define sc "OPTIONS "
1192 if(strncmp(line, sc, sizeof(sc)-1) == 0) {
1194 g_options = parse_g_options(line+8, 1);
1195 if(!g_options->hostname) {
1196 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
1197 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
1198 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
1204 if(amandad_auth && g_options->auth) {
1205 if(strcasecmp(amandad_auth, g_options->auth) != 0) {
1206 printf("ERROR recover program ask for auth=%s while amindexd is configured for '%s'\n",
1207 g_options->auth, amandad_auth);
1208 error("amindexd: ERROR recover program ask for auth=%s while amindexd is configured for '%s'",
1209 g_options->auth, amandad_auth);
1213 /* send the REP packet */
1214 printf("CONNECT MESG %d\n", DATA_FD_OFFSET);
1221 cmdout = fdopen(cmdfdout, "a");
1223 error("amindexd: Can't fdopen(cmdfdout): %s", strerror(errno));
1227 cmdin = fdopen(cmdfdin, "r");
1229 error("amindexd: Can't fdopen(cmdfdin): %s", strerror(errno));
1234 /* clear these so we can detect when the have not been set by the client */
1235 amfree(dump_hostname);
1238 amfree(target_date);
1240 our_features = am_init_feature_set();
1241 their_features = am_set_default_feature_set();
1243 if (config_name != NULL && is_config_valid(config_name) != -1) {
1247 reply(220, "%s AMANDA index server (%s) ready.", local_hostname,
1250 user_validated = from_amandad;
1252 /* a real simple parser since there are only a few commands */
1255 /* get a line from the client */
1257 if((part = agets(cmdin)) == NULL) {
1259 dbprintf(("%s: ? read error: %s\n",
1260 debug_prefix_time(NULL), strerror(errno)));
1262 dbprintf(("%s: ? unexpected EOF\n",
1263 debug_prefix_time(NULL)));
1266 dbprintf(("%s: ? unprocessed input:\n",
1267 debug_prefix_time(NULL)));
1268 dbprintf(("-----\n"));
1269 dbprintf(("? %s\n", line));
1270 dbprintf(("-----\n"));
1274 uncompress_remove = remove_files(uncompress_remove);
1276 return 1; /* they hung up? */
1278 strappend(line, part); /* Macro: line can be null */
1281 if(amindexd_debug) {
1282 break; /* we have a whole line */
1284 if((len = strlen(line)) > 0 && line[len-1] == '\r') {
1285 line[len-1] = '\0'; /* zap the '\r' */
1289 * Hmmm. We got a "line" from agets(), which means it saw
1290 * a '\n' (or EOF, etc), but there was not a '\r' before it.
1291 * Put a '\n' back in the buffer and loop for more.
1293 strappend(line, "\n");
1296 dbprintf(("%s: > %s\n", debug_prefix_time(NULL), line));
1303 skip_whitespace(s, ch);
1305 reply(500, "Command not recognised/incorrect: %s", line);
1311 skip_non_whitespace(s, ch);
1312 cmd_undo = s-1; /* for error message */
1313 cmd_undo_ch = *cmd_undo;
1316 skip_whitespace(s, ch); /* find the argument */
1319 skip_quoted_string(s, ch);
1320 arg = unquote_string(arg);
1325 if (!user_validated && strcmp(cmd, "SECURITY") == 0 && arg) {
1326 user_validated = check_security(&his_addr, arg, 0, &errstr);
1327 if(user_validated) {
1328 reply(200, "Access OK");
1333 if (!user_validated) { /* don't tell client the reason, just log it to debug log */
1334 reply(500, "Access not allowed");
1336 dbprintf(("%s: %s\n", debug_prefix_time(NULL), errstr));
1341 if (strcmp(cmd, "QUIT") == 0) {
1344 } else if (strcmp(cmd, "HOST") == 0 && arg) {
1345 /* set host we are restoring */
1347 if (is_dump_host_valid(arg) != -1)
1349 dump_hostname = newstralloc(dump_hostname, arg);
1350 reply(200, "Dump host set to %s.", dump_hostname);
1351 amfree(qdisk_name); /* invalidate any value */
1352 amfree(disk_name); /* invalidate any value */
1355 } else if (strcmp(cmd, "LISTHOST") == 0) {
1361 if (config_name == NULL) {
1362 reply(501, "Must set config before listhost");
1365 lreply(200, " List hosts for config %s", config_name);
1366 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1368 for (diskdup = disk_list.head; diskdup!=disk; diskdup = diskdup->next) {
1369 if(strcmp(diskdup->host->hostname, disk->host->hostname) == 0) {
1375 fast_lreply(201, " %s", disk->host->hostname);
1380 reply(200, " List hosts for config %s", config_name);
1383 reply(200, "No hosts for config %s", config_name);
1387 } else if (strcmp(cmd, "DISK") == 0 && arg) {
1389 if (is_disk_valid(arg) != -1) {
1390 disk_name = newstralloc(disk_name, arg);
1391 qdisk_name = quote_string(disk_name);
1392 if (build_disk_table() != -1) {
1393 reply(200, "Disk set to %s.", qdisk_name);
1397 } else if (strcmp(cmd, "LISTDISK") == 0) {
1402 if (config_name == NULL) {
1403 reply(501, "Must set config, host before listdisk");
1405 else if (dump_hostname == NULL) {
1406 reply(501, "Must set host before listdisk");
1409 lreply(200, " List of disk for device %s on host %s", arg,
1411 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1413 if (strcmp(disk->host->hostname, dump_hostname) == 0 &&
1414 ((disk->device && strcmp(disk->device, arg) == 0) ||
1415 (!disk->device && strcmp(disk->name, arg) == 0))) {
1416 qname = quote_string(disk->name);
1417 fast_lreply(201, " %s", qname);
1423 reply(200, "List of disk for device %s on host %s", arg,
1427 reply(200, "No disk for device %s on host %s", arg,
1432 lreply(200, " List of disk for host %s", dump_hostname);
1433 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1434 if(strcmp(disk->host->hostname, dump_hostname) == 0) {
1435 qname = quote_string(disk->name);
1436 fast_lreply(201, " %s", qname);
1442 reply(200, "List of disk for host %s", dump_hostname);
1445 reply(200, "No disk for host %s", dump_hostname);
1449 } else if (strcmp(cmd, "SCNF") == 0 && arg) {
1451 amfree(config_name);
1453 config_name = newstralloc(config_name, arg);
1454 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
1455 if (is_config_valid(arg) != -1) {
1456 amfree(dump_hostname); /* invalidate any value */
1457 amfree(qdisk_name); /* invalidate any value */
1458 amfree(disk_name); /* invalidate any value */
1459 reply(200, "Config set to %s.", config_name);
1461 amfree(config_name);
1465 } else if (strcmp(cmd, "FEATURES") == 0 && arg) {
1466 char *our_feature_string = NULL;
1467 char *their_feature_string = NULL;
1469 am_release_feature_set(our_features);
1470 am_release_feature_set(their_features);
1471 our_features = am_init_feature_set();
1472 our_feature_string = am_feature_to_string(our_features);
1473 their_feature_string = newstralloc(their_feature_string, arg);
1474 their_features = am_string_to_feature(their_feature_string);
1475 reply(200, "FEATURES %s", our_feature_string);
1476 amfree(our_feature_string);
1477 amfree(their_feature_string);
1479 } else if (strcmp(cmd, "DATE") == 0 && arg) {
1481 target_date = newstralloc(target_date, arg);
1482 reply(200, "Working date set to %s.", target_date);
1484 } else if (strcmp(cmd, "DHST") == 0) {
1485 (void)disk_history_list();
1486 } else if (strcmp(cmd, "OISD") == 0 && arg) {
1487 if (is_dir_valid_opaque(arg) != -1) {
1488 reply(200, "\"%s\" is a valid directory", arg);
1490 } else if (strcmp(cmd, "OLSD") == 0 && arg) {
1491 (void)opaque_ls(arg,0);
1492 } else if (strcmp(cmd, "ORLD") == 0 && arg) {
1493 (void)opaque_ls(arg, 1);
1494 } else if (strcmp(cmd, "TAPE") == 0) {
1496 } else if (strcmp(cmd, "DCMP") == 0) {
1497 (void)are_dumps_compressed();
1499 *cmd_undo = cmd_undo_ch; /* restore the command line */
1500 reply(500, "Command not recognised/incorrect: %s", cmd);
1506 uncompress_remove = remove_files(uncompress_remove);
1507 free_find_result(&output_find);
1508 reply(200, "Good bye.");
1517 static char nice[20];
1518 int year, month, day;
1519 int hours, minutes, seconds;
1520 char date[9], atime[7];
1521 int numdate, numtime;
1523 strncpy(date, datestamp, 8);
1525 numdate = atoi(date);
1526 year = numdate / 10000;
1527 month = (numdate / 100) % 100;
1528 day = numdate % 100;
1530 if(strlen(datestamp) <= 8) {
1531 snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d",
1535 strncpy(atime, &(datestamp[8]), 6);
1537 numtime = atoi(atime);
1538 hours = numtime / 10000;
1539 minutes = (numtime / 100) % 100;
1540 seconds = numtime % 100;
1542 snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d-%02d-%02d-%02d",
1543 year, month, day, hours, minutes, seconds);
1554 return strncmp(date1, date2, strlen(date2));