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 2006/07/25 18:27:57 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"
58 #include "pipespawn.h"
62 #define amindexd_debug(i,x) do { \
63 if ((i) <= debug_amindexd) { \
68 typedef struct REMOVE_ITEM
71 struct REMOVE_ITEM *next;
75 static int from_amandad;
76 static char local_hostname[MAX_HOSTNAME_LENGTH+1]; /* me! */
77 static char *dump_hostname = NULL; /* machine we are restoring */
78 static char *disk_name; /* disk we are restoring */
79 char *qdisk_name = NULL; /* disk we are restoring */
80 static char *target_date = NULL;
81 static disklist_t disk_list; /* all disks in cur config */
82 static find_result_t *output_find = NULL;
83 static g_option_t *g_options = NULL;
85 static int amindexd_debug = 0;
87 static REMOVE_ITEM *uncompress_remove = NULL;
88 /* uncompressed files to remove */
90 static am_feature_t *our_features = NULL;
91 static am_feature_t *their_features = NULL;
93 static REMOVE_ITEM *remove_files(REMOVE_ITEM *);
94 static char *uncompress_file(char *, char **);
95 static int process_ls_dump(char *, DUMP_ITEM *, int, char **);
97 static size_t reply_buffer_size = 1;
98 static char *reply_buffer = NULL;
99 static char *amandad_auth = NULL;
103 static void reply(int, char *, ...)
104 __attribute__ ((format (printf, 2, 3)));
105 static void lreply(int, char *, ...)
106 __attribute__ ((format (printf, 2, 3)));
107 static void fast_lreply(int, char *, ...)
108 __attribute__ ((format (printf, 2, 3)));
109 static am_host_t *is_dump_host_valid(char *);
110 static int is_disk_valid(char *);
111 static int is_config_valid(char *);
112 static int build_disk_table(void);
113 static int disk_history_list(void);
114 static int is_dir_valid_opaque(char *);
115 static int opaque_ls(char *, int);
116 static void opaque_ls_one (DIR_ITEM *dir_item, am_feature_e marshall_feature,
118 static int tapedev_is(void);
119 static int are_dumps_compressed(void);
120 static char *amindexd_nicedate (char *datestamp);
121 static int cmp_date (const char *date1, const char *date2);
122 static char *clean_backslash(char *line);
123 static char *get_index_name(char *dump_hostname, char *hostname,
124 char *diskname, char *timestamps, int level);
125 static int get_index_dir(char *dump_hostname, char *hostname, char *diskname);
127 int main(int, char **);
136 dbprintf(("%s: removing index file: %s\n",
137 debug_prefix_time(NULL), remove->filename));
138 unlink(remove->filename);
139 amfree(remove->filename);
141 remove = remove->next;
153 char *filename = NULL;
154 struct stat stat_filename;
167 amwait_t wait_status;
170 filename = stralloc(filename_gz);
171 len = strlen(filename);
172 if(len > 3 && strcmp(&(filename[len-3]),".gz")==0) {
173 filename[len-3]='\0';
174 } else if(len > 2 && strcmp(&(filename[len-2]),".Z")==0) {
175 filename[len-2]='\0';
178 /* uncompress the file */
179 result=stat(filename,&stat_filename);
180 if(result==-1 && errno==ENOENT) { /* file does not exist */
182 REMOVE_ITEM *remove_file;
185 * Check that compressed file exists manually.
187 if (stat(filename_gz, &statbuf) < 0) {
188 *emsg = newvstralloc(*emsg, "Compressed file '",
190 "' is inaccessable: ",
193 dbprintf(("%s\n",*emsg));
198 #ifdef UNCOMPRESS_OPT
199 # define PARAM_UNCOMPRESS_OPT UNCOMPRESS_OPT
201 # define PARAM_UNCOMPRESS_OPT skip_argument
207 debugfd = open("/dev/null", O_WRONLY);
211 nullfd = open("/dev/null", O_RDONLY);
212 indexfd = open(filename,O_WRONLY|O_CREAT, 0600);
214 *emsg = newvstralloc(*emsg, "Can't open '",
215 filename, "' for writting: ",
218 dbprintf(("%s\n",*emsg));
223 /* start the uncompress process */
224 putenv(stralloc("LC_ALL=C"));
225 pid_gzip = pipespawn(UNCOMPRESS_PATH, STDOUT_PIPE,
226 &nullfd, &pipe_from_gzip, &debugfd,
227 UNCOMPRESS_PATH, PARAM_UNCOMPRESS_OPT,
231 pipe_stream = fdopen(pipe_from_gzip,"r");
232 if(pipe_stream == NULL) {
233 *emsg = newvstralloc(*emsg, "Can't fdopen pipe from gzip: ",
236 dbprintf(("%s\n",*emsg));
241 /* start the sort process */
242 pid_sort = pipespawn(SORT_PATH, STDIN_PIPE,
243 &pipe_to_sort, &indexfd, &debugfd,
245 if (debugnullfd == 1)
249 /* send all ouput from uncompress process to sort process */
250 /* clean the data with clean_backslash */
251 while (fgets(line, STR_SIZE, pipe_stream) != NULL) {
252 if (line[0] != '\0') {
253 if (index(line,'/')) {
254 clean_backslash(line);
255 fullwrite(pipe_to_sort,line,strlen(line));
261 aclose(pipe_to_sort);
262 if (waitpid(pid_gzip, &wait_status, 0) < 0) {
263 if (!WIFEXITED(wait_status)) {
264 dbprintf(("Uncompress exited with signal %d",
265 WTERMSIG(wait_status)));
266 } else if (WEXITSTATUS(wait_status) != 0) {
267 dbprintf(("Uncompress exited with status %d",
268 WEXITSTATUS(wait_status)));
270 dbprintf(("Uncompres returned negative value: %s",
274 if (waitpid(pid_sort, &wait_status, 0)) {
275 if (!WIFEXITED(wait_status)) {
276 dbprintf(("Sort exited with signal %d",
277 WTERMSIG(wait_status)));
278 } else if (WEXITSTATUS(wait_status) != 0) {
279 dbprintf(("Sort exited with status %d",
280 WEXITSTATUS(wait_status)));
282 dbprintf(("Sort returned negative value: %s",
287 /* add at beginning */
288 remove_file = (REMOVE_ITEM *)alloc(SIZEOF(REMOVE_ITEM));
289 remove_file->filename = stralloc(filename);
290 remove_file->next = uncompress_remove;
291 uncompress_remove = remove_file;
292 } else if(!S_ISREG((stat_filename.st_mode))) {
294 *emsg = vstralloc("\"", filename, "\" is not a regular file", NULL);
304 /* find all matching entries in a dump listing */
305 /* return -1 if error */
309 DUMP_ITEM * dump_item,
314 char *old_line = NULL;
315 char *filename = NULL;
317 char *dir_slash = NULL;
321 size_t len_dir_slash;
323 if (strcmp(dir, "/") == 0) {
324 dir_slash = stralloc(dir);
326 dir_slash = stralloc2(dir, "/");
329 filename_gz = get_index_name(dump_hostname, dump_item->hostname, disk_name,
330 dump_item->date, dump_item->level);
331 if (filename_gz == NULL) {
332 *emsg = stralloc("index file not found");
336 if((filename = uncompress_file(filename_gz, emsg)) == NULL) {
343 if((fp = fopen(filename,"r"))==0) {
345 *emsg = stralloc(strerror(errno));
350 len_dir_slash=strlen(dir_slash);
352 while (fgets(line, STR_SIZE, fp) != NULL) {
353 if (line[0] != '\0') {
354 if(line[strlen(line)-1] == '\n')
355 line[strlen(line)-1] = '\0';
356 if(strncmp(dir_slash, line, len_dir_slash) == 0) {
358 s = line + len_dir_slash;
360 while(ch && ch != '/')
361 ch = *s++;/* find end of the file name */
367 if(old_line == NULL || strcmp(line, old_line) != 0) {
368 add_dir_list_item(dump_item, line);
370 old_line = stralloc(line);
376 /*@i@*/ amfree(old_line);
382 /* send a 1 line reply to the client */
383 printf_arglist_function1(static void reply, int, n, char *, fmt)
389 reply_buffer = alloc(reply_buffer_size);
392 arglist_start(args, fmt);
393 len = vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
396 if (len > -1 && (size_t)len < reply_buffer_size-1)
399 reply_buffer_size *= 2;
400 amfree(reply_buffer);
401 reply_buffer = alloc(reply_buffer_size);
404 if (fprintf(cmdout,"%03d %s\r\n", n, reply_buffer) < 0)
406 dbprintf(("%s: ! error %d (%s) in printf\n",
407 debug_prefix_time(NULL), errno, strerror(errno)));
408 uncompress_remove = remove_files(uncompress_remove);
411 if (fflush(cmdout) != 0)
413 dbprintf(("%s: ! error %d (%s) in fflush\n",
414 debug_prefix_time(NULL), errno, strerror(errno)));
415 uncompress_remove = remove_files(uncompress_remove);
418 dbprintf(("%s: < %03d %s\n", debug_prefix_time(NULL), n, reply_buffer));
421 /* send one line of a multi-line response */
422 printf_arglist_function1(static void lreply, int, n, char *, fmt)
428 reply_buffer = alloc(reply_buffer_size);
431 arglist_start(args, fmt);
432 len = vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
435 if (len > -1 && (size_t)len < reply_buffer_size-1)
438 reply_buffer_size *= 2;
439 amfree(reply_buffer);
440 reply_buffer = alloc(reply_buffer_size);
443 if (fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0)
445 dbprintf(("%s: ! error %d (%s) in printf\n",
446 debug_prefix_time(NULL), errno, strerror(errno)));
447 uncompress_remove = remove_files(uncompress_remove);
450 if (fflush(cmdout) != 0)
452 dbprintf(("%s: ! error %d (%s) in fflush\n",
453 debug_prefix_time(NULL), errno, strerror(errno)));
454 uncompress_remove = remove_files(uncompress_remove);
458 dbprintf(("%s: < %03d-%s\n", debug_prefix_time(NULL), n, reply_buffer));
462 /* send one line of a multi-line response */
463 printf_arglist_function1(static void fast_lreply, int, n, char *, fmt)
469 reply_buffer = alloc(reply_buffer_size);
472 arglist_start(args, fmt);
473 len = vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
476 if (len > -1 && (size_t)len < reply_buffer_size-1)
479 reply_buffer_size *= 2;
480 amfree(reply_buffer);
481 reply_buffer = alloc(reply_buffer_size);
484 if (fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0)
486 dbprintf(("%s: ! error %d (%s) in printf\n",
487 debug_prefix_time(NULL), errno, strerror(errno)));
488 uncompress_remove = remove_files(uncompress_remove);
492 dbprintf(("%s: < %03d-%s\n", debug_prefix_time(NULL), n, reply_buffer));
495 /* see if hostname is valid */
496 /* valid is defined to be that there is an index directory for it */
497 /* also do a security check on the requested dump hostname */
498 /* to restrict access to index records if required */
499 /* return -1 if not okay */
507 if (config_name == NULL) {
508 reply(501, "Must set config before setting host.");
512 /* check that the config actually handles that host */
513 ihost = lookup_host(host);
515 reply(501, "Host %s is not in your disklist.", host);
519 /* check if an index dir exist */
520 if(get_index_dir(host, ihost->hostname, NULL)) {
524 /* check if an index dir exist for at least one DLE */
525 for(diskp = ihost->disks; diskp != NULL; diskp = diskp->hostnext) {
526 if (get_index_dir(diskp->hostname, NULL, NULL)) {
531 reply(501, "No index records for host: %s. Have you enabled indexing?", host);
543 if (config_name == NULL) {
544 reply(501, "Must set config,host before setting disk.");
547 else if (dump_hostname == NULL) {
548 reply(501, "Must set host before setting disk.");
552 /* check that the config actually handles that disk */
553 idisk = lookup_disk(dump_hostname, disk);
555 qdisk = quote_string(disk);
556 reply(501, "Disk %s:%s is not in your disklist.", dump_hostname, qdisk);
561 /* assume an index dir already */
562 if (get_index_dir(dump_hostname, idisk->hostname, disk) == 0) {
563 qdisk = quote_string(disk);
564 reply(501, "No index records for disk: %s. Invalid?", qdisk);
581 struct stat dir_stat;
583 /* check that the config actually exists */
584 if (config == NULL) {
585 reply(501, "Must set config first.");
590 conffile = stralloc2(config_dir, CONFFILE_NAME);
591 if (read_conffile(conffile)) {
592 reply(501, "Could not read config file %s!", conffile);
598 conf_diskfile = getconf_str(CNF_DISKFILE);
599 if (*conf_diskfile == '/') {
600 conf_diskfile = stralloc(conf_diskfile);
602 conf_diskfile = stralloc2(config_dir, conf_diskfile);
604 if (read_diskfile(conf_diskfile, &disk_list) < 0) {
605 reply(501, "Could not read disk file %s!", conf_diskfile);
606 amfree(conf_diskfile);
609 amfree(conf_diskfile);
611 conf_tapelist = getconf_str(CNF_TAPELIST);
612 if (*conf_tapelist == '/') {
613 conf_tapelist = stralloc(conf_tapelist);
615 conf_tapelist = stralloc2(config_dir, conf_tapelist);
617 if(read_tapelist(conf_tapelist)) {
618 reply(501, "Could not read tapelist file %s!", conf_tapelist);
619 amfree(conf_tapelist);
622 amfree(conf_tapelist);
624 dbrename(config, DBG_SUBDIR_SERVER);
626 output_find = find_dump(1, &disk_list);
627 sort_find_result("DLKHpB", &output_find);
629 conf_indexdir = getconf_str(CNF_INDEXDIR);
630 if(*conf_indexdir == '/') {
631 conf_indexdir = stralloc(conf_indexdir);
633 conf_indexdir = stralloc2(config_dir, conf_indexdir);
635 if (stat (conf_indexdir, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
636 reply(501, "Index directory %s does not exist", conf_indexdir);
637 amfree(conf_indexdir);
640 amfree(conf_indexdir);
647 build_disk_table(void)
650 char *last_timestamp;
654 find_result_t *find_output;
656 if (config_name == NULL) {
657 reply(590, "Must set config,host,disk before building disk table");
660 else if (dump_hostname == NULL) {
661 reply(590, "Must set host,disk before building disk table");
664 else if (disk_name == NULL) {
665 reply(590, "Must set disk before building disk table");
670 last_timestamp = NULL;
671 last_filenum = (off_t)-1;
674 for(find_output = output_find;
676 find_output = find_output->next) {
677 if(strcasecmp(dump_hostname, find_output->hostname) == 0 &&
678 strcmp(disk_name , find_output->diskname) == 0 &&
679 strcmp("OK" , find_output->status) == 0) {
681 if(strcmp("--", find_output->partnum)){
682 partnum = atoi(find_output->partnum);
685 * The sort order puts holding disk entries first. We want to
686 * use them if at all possible, so ignore any other entries
687 * for the same datestamp after we see a holding disk entry
688 * (as indicated by a filenum of zero).
691 strcmp(find_output->timestamp, last_timestamp) == 0 &&
692 find_output->level == last_level &&
693 partnum == last_partnum && last_filenum == 0) {
696 last_timestamp = find_output->timestamp;
697 last_filenum = find_output->filenum;
698 last_level = find_output->level;
699 last_partnum = partnum;
700 date = amindexd_nicedate(find_output->timestamp);
701 add_dump(find_output->hostname, date, find_output->level,
702 find_output->label, find_output->filenum, partnum);
703 dbprintf(("%s: - %s %d %s " OFF_T_FMT " %d\n",
704 debug_prefix_time(NULL), date, find_output->level,
706 (OFF_T_FMT_TYPE)find_output->filenum,
715 disk_history_list(void)
720 if (config_name == NULL) {
721 reply(502, "Must set config,host,disk before listing history");
724 else if (dump_hostname == NULL) {
725 reply(502, "Must set host,disk before listing history");
728 else if (disk_name == NULL) {
729 reply(502, "Must set disk before listing history");
733 lreply(200, " Dump history for config \"%s\" host \"%s\" disk %s",
734 config_name, dump_hostname, qdisk_name);
736 for (item=first_dump(); item!=NULL; item=next_dump(item)){
737 char *tapelist_str = marshal_tapelist(item->tapes, 1);
739 strncpy(date, item->date, 20);
741 if(!am_has_feature(their_features,fe_amrecover_timestamp))
744 if(am_has_feature(their_features, fe_amindexd_marshall_in_DHST)){
745 lreply(201, " %s %d %s", date, item->level, tapelist_str);
748 lreply(201, " %s %d %s " OFF_T_FMT, date, item->level,
749 tapelist_str, (OFF_T_FMT_TYPE)item->file);
751 amfree(tapelist_str);
754 reply(200, "Dump history for config \"%s\" host \"%s\" disk %s",
755 config_name, dump_hostname, qdisk_name);
762 * is the directory dir backed up - dir assumed complete relative to
765 /* opaque version of command */
775 char *filename_gz = NULL;
776 char *filename = NULL;
778 static char *emsg = NULL;
780 if (config_name == NULL || dump_hostname == NULL || disk_name == NULL) {
781 reply(502, "Must set config,host,disk before asking about directories");
784 else if (dump_hostname == NULL) {
785 reply(502, "Must set host,disk before asking about directories");
788 else if (disk_name == NULL) {
789 reply(502, "Must set disk before asking about directories");
792 else if (target_date == NULL) {
793 reply(502, "Must set date before asking about directories");
796 /* scan through till we find first dump on or before date */
797 for (item=first_dump(); item!=NULL; item=next_dump(item))
798 if (cmp_date(item->date, target_date) <= 0)
803 /* no dump for given date */
804 reply(500, "No dumps available on or before date \"%s\"", target_date);
808 if(strcmp(dir, "/") == 0) {
809 ldir = stralloc(dir);
811 ldir = stralloc2(dir, "/");
813 ldir_len = strlen(ldir);
815 /* go back till we hit a level 0 dump */
819 filename_gz = get_index_name(dump_hostname, item->hostname, disk_name,
820 item->date, item->level);
821 if (filename_gz == NULL) {
822 reply(599, "index not found");
826 if((filename = uncompress_file(filename_gz, &emsg)) == NULL) {
827 reply(599, "System error %s", emsg);
834 dbprintf(("%s: f %s\n", debug_prefix_time(NULL), filename));
835 if ((fp = fopen(filename, "r")) == NULL) {
836 reply(599, "System error %s", strerror(errno));
841 while (fgets(line, STR_SIZE, fp) != NULL) {
844 if(line[strlen(line)-1] == '\n')
845 line[strlen(line)-1] = '\0';
846 if (strncmp(line, ldir, ldir_len) != 0) {
847 continue; /* not found yet */
856 last_level = item->level;
859 item=next_dump(item);
860 } while ((item != NULL) && (item->level >= last_level));
861 } while (item != NULL);
865 reply(500, "\"%s\" is an invalid directory", dir);
874 DUMP_ITEM *dump_item;
876 int level, last_level;
877 static char *emsg = NULL;
878 am_feature_e marshall_feature;
881 marshall_feature = fe_amindexd_marshall_in_ORLD;
883 marshall_feature = fe_amindexd_marshall_in_OLSD;
888 if (config_name == NULL) {
889 reply(502, "Must set config,host,disk before listing a directory");
892 else if (dump_hostname == NULL) {
893 reply(502, "Must set host,disk before listing a directory");
896 else if (disk_name == NULL) {
897 reply(502, "Must set disk before listing a directory");
900 else if (target_date == NULL) {
901 reply(502, "Must set date before listing a directory");
905 /* scan through till we find first dump on or before date */
906 for (dump_item=first_dump(); dump_item!=NULL; dump_item=next_dump(dump_item))
907 if (cmp_date(dump_item->date, target_date) <= 0)
910 if (dump_item == NULL)
912 /* no dump for given date */
913 reply(500, "No dumps available on or before date \"%s\"", target_date);
917 /* get data from that dump */
918 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
919 reply(599, "System error %s", emsg);
924 /* go back processing higher level dumps till we hit a level 0 dump */
925 last_level = dump_item->level;
926 while ((last_level != 0) && ((dump_item=next_dump(dump_item)) != NULL))
928 if (dump_item->level < last_level)
930 last_level = dump_item->level;
931 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
932 reply(599, "System error %s", emsg);
939 /* return the information to the caller */
940 lreply(200, " Opaque list of %s", dir);
941 for(level=0; level<=9; level++) {
942 for (dir_item = get_dir_list(); dir_item != NULL;
943 dir_item = dir_item->next) {
945 if(dir_item->dump->level == level) {
946 if (!am_has_feature(their_features, marshall_feature) &&
947 (num_entries(dir_item->dump->tapes) > 1 ||
948 dir_item->dump->tapes->numfiles > 1)) {
949 fast_lreply(501, " ERROR: Split dumps not supported"
950 " with old version of amrecover.");
954 opaque_ls_one(dir_item, marshall_feature, recursive);
959 reply(200, " Opaque list of %s", dir);
967 am_feature_e marshall_feature,
974 if (am_has_feature(their_features, marshall_feature)) {
975 tapelist_str = marshal_tapelist(dir_item->dump->tapes, 1);
977 tapelist_str = dir_item->dump->tapes->label;
980 strncpy(date, dir_item->dump->date, 20);
982 if(!am_has_feature(their_features,fe_amrecover_timestamp))
985 qpath = quote_string(dir_item->path);
986 if((!recursive && am_has_feature(their_features,
987 fe_amindexd_fileno_in_OLSD)) ||
988 (recursive && am_has_feature(their_features,
989 fe_amindexd_fileno_in_ORLD))) {
990 fast_lreply(201, " %s %d %s " OFF_T_FMT " %s",
992 dir_item->dump->level,
994 (OFF_T_FMT_TYPE)dir_item->dump->file,
999 fast_lreply(201, " %s %d %s %s",
1000 date, dir_item->dump->level,
1001 tapelist_str, qpath);
1004 if(am_has_feature(their_features, marshall_feature)) {
1005 amfree(tapelist_str);
1010 * returns the value of changer or tapedev from the amanda.conf file if set,
1011 * otherwise reports an error
1019 /* check state okay to do this */
1020 if (config_name == NULL) {
1021 reply(501, "Must set config before asking about tapedev.");
1025 /* use amrecover_changer if possible */
1026 if ((result = getconf_str(CNF_AMRECOVER_CHANGER)) != NULL &&
1028 dbprintf(("%s: tapedev_is amrecover_changer: %s\n",
1029 debug_prefix_time(NULL), result));
1034 /* use changer if possible */
1035 if ((result = getconf_str(CNF_TPCHANGER)) != NULL && *result != '\0') {
1036 dbprintf(("%s: tapedev_is tpchanger: %s\n",
1037 debug_prefix_time(NULL), result));
1042 /* get tapedev value */
1043 if ((result = getconf_str(CNF_TAPEDEV)) != NULL && *result != '\0') {
1044 dbprintf(("%s: tapedev_is tapedev: %s\n",
1045 debug_prefix_time(NULL), result));
1050 dbprintf(("%s: No tapedev or tpchanger in config site.\n",
1051 debug_prefix_time(NULL)));
1052 reply(501, "Tapedev or tpchanger not set in config file.");
1057 /* returns YES if dumps for disk are compressed, NO if not */
1059 are_dumps_compressed(void)
1063 /* check state okay to do this */
1064 if (config_name == NULL) {
1065 reply(501, "Must set config,host,disk name before asking about dumps.");
1068 else if (dump_hostname == NULL) {
1069 reply(501, "Must set host,disk name before asking about dumps.");
1072 else if (disk_name == NULL) {
1073 reply(501, "Must set disk name before asking about dumps.");
1077 /* now go through the list of disks and find which have indexes */
1078 for (diskp = disk_list.head; diskp != NULL; diskp = diskp->next) {
1079 if ((strcasecmp(diskp->host->hostname, dump_hostname) == 0)
1080 && (strcmp(diskp->name, disk_name) == 0)) {
1085 if (diskp == NULL) {
1086 reply(501, "Couldn't find host/disk in disk file.");
1090 /* send data to caller */
1091 if (diskp->compress == COMP_NONE)
1104 char *line = NULL, *part = NULL;
1107 char *cmd_undo, cmd_undo_ch;
1109 struct sockaddr_storage his_addr;
1113 int user_validated = 0;
1114 char *errstr = NULL;
1115 char *pgm = "amindexd"; /* in case argv[0] is not set */
1116 char his_hostname[MAX_HOSTNAME_LENGTH];
1118 safe_fd(DATA_FD_OFFSET, 2);
1122 * When called via inetd, it is not uncommon to forget to put the
1123 * argv[0] value on the config line. On some systems (e.g. Solaris)
1124 * this causes argv and/or argv[0] to be NULL, so we have to be
1125 * careful getting our name.
1127 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
1128 if((pgm = strrchr(argv[0], '/')) != NULL) {
1137 /* Don't die when child closes pipe */
1138 signal(SIGPIPE, SIG_IGN);
1142 /* we'd rather not run as root */
1144 if(geteuid() == 0) {
1145 if(client_uid == (uid_t) -1) {
1146 error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
1151 initgroups(CLIENT_LOGIN, client_gid);
1157 #endif /* FORCE_USERID */
1159 dbopen(DBG_SUBDIR_SERVER);
1160 dbprintf(("%s: version %s\n", get_pname(), version()));
1163 error("argv == NULL\n");
1166 if (! (argc >= 1 && argv[0] != NULL)) {
1167 dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
1168 debug_prefix_time(NULL)));
1183 if(argc > 0 && strcmp(*argv, "-t") == 0) {
1189 if(argc > 0 && strcmp(*argv, "amandad") == 0) {
1194 amandad_auth = *argv;
1205 config_name = stralloc(*argv);
1206 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
1211 if(gethostname(local_hostname, SIZEOF(local_hostname)-1) == -1) {
1212 error("gethostname: %s", strerror(errno));
1215 local_hostname[SIZEOF(local_hostname)-1] = '\0';
1217 /* now trim domain off name */
1220 while(ch && ch != '.') ch = *s++;
1224 if(from_amandad == 0) {
1225 if(!amindexd_debug) {
1226 /* who are we talking to? */
1227 socklen = sizeof (his_addr);
1228 if (getpeername(0, (struct sockaddr *)&his_addr, &socklen) == -1)
1229 error("getpeername: %s", strerror(errno));
1231 /* Try a reverse (IP->hostname) resolution, and fail if it does
1232 * not work -- this is a basic security check */
1233 if (getnameinfo((struct sockaddr *)&his_addr, SS_LEN(&his_addr),
1234 his_hostname, sizeof(his_hostname),
1237 error(_("getnameinfo(%s): hostname lookup failed"),
1238 str_sockaddr(&his_addr));
1243 /* Set up the input and output FILEs */
1248 /* read the REQ packet */
1249 for(; (line = agets(stdin)) != NULL; free(line)) {
1250 if(strncmp_const(line, "OPTIONS ") == 0) {
1251 g_options = parse_g_options(line+8, 1);
1252 if(!g_options->hostname) {
1253 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
1254 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
1255 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
1261 if(amandad_auth && g_options->auth) {
1262 if(strcasecmp(amandad_auth, g_options->auth) != 0) {
1263 printf("ERROR recover program ask for auth=%s while amindexd is configured for '%s'\n",
1264 g_options->auth, amandad_auth);
1265 error("amindexd: ERROR recover program ask for auth=%s while amindexd is configured for '%s'",
1266 g_options->auth, amandad_auth);
1270 /* send the REP packet */
1271 printf("CONNECT MESG %d\n", DATA_FD_OFFSET);
1278 cmdout = fdopen(DATA_FD_OFFSET + 0, "a");
1280 error(_("amindexd: Can't fdopen(%d): %s"), DATA_FD_OFFSET + 0, strerror(errno));
1284 cmdin = fdopen(DATA_FD_OFFSET + 1, "r");
1286 error(_("amindexd: Can't fdopen(%d): %s"), DATA_FD_OFFSET + 1, strerror(errno));
1291 /* clear these so we can detect when the have not been set by the client */
1292 amfree(dump_hostname);
1295 amfree(target_date);
1297 our_features = am_init_feature_set();
1298 their_features = am_set_default_feature_set();
1300 if (config_name != NULL && is_config_valid(config_name) != -1) {
1304 reply(220, "%s AMANDA index server (%s) ready.", local_hostname,
1307 user_validated = from_amandad;
1309 /* a real simple parser since there are only a few commands */
1312 /* get a line from the client */
1314 if((part = agets(cmdin)) == NULL) {
1316 dbprintf(("%s: ? read error: %s\n",
1317 debug_prefix_time(NULL), strerror(errno)));
1319 dbprintf(("%s: ? unexpected EOF\n",
1320 debug_prefix_time(NULL)));
1323 dbprintf(("%s: ? unprocessed input:\n",
1324 debug_prefix_time(NULL)));
1325 dbprintf(("-----\n"));
1326 dbprintf(("? %s\n", line));
1327 dbprintf(("-----\n"));
1331 uncompress_remove = remove_files(uncompress_remove);
1333 return 1; /* they hung up? */
1335 strappend(line, part); /* Macro: line can be null */
1338 if(amindexd_debug) {
1339 break; /* we have a whole line */
1341 if((len = strlen(line)) > 0 && line[len-1] == '\r') {
1342 line[len-1] = '\0'; /* zap the '\r' */
1346 * Hmmm. We got a "line" from agets(), which means it saw
1347 * a '\n' (or EOF, etc), but there was not a '\r' before it.
1348 * Put a '\n' back in the buffer and loop for more.
1350 strappend(line, "\n");
1353 dbprintf(("%s: > %s\n", debug_prefix_time(NULL), line));
1360 skip_whitespace(s, ch);
1362 reply(500, "Command not recognised/incorrect: %s", line);
1368 skip_non_whitespace(s, ch);
1369 cmd_undo = s-1; /* for error message */
1370 cmd_undo_ch = *cmd_undo;
1373 skip_whitespace(s, ch); /* find the argument */
1376 skip_quoted_string(s, ch);
1377 arg = unquote_string(arg);
1382 if (!user_validated && strcmp(cmd, "SECURITY") == 0 && arg) {
1383 user_validated = amindexd_debug ||
1385 (struct sockaddr_storage *)&his_addr,
1387 if(user_validated) {
1388 reply(200, "Access OK");
1393 if (!user_validated) { /* don't tell client the reason, just log it to debug log */
1394 reply(500, "Access not allowed");
1396 dbprintf(("%s: %s\n", debug_prefix_time(NULL), errstr));
1401 if (strcmp(cmd, "QUIT") == 0) {
1404 } else if (strcmp(cmd, "HOST") == 0 && arg) {
1406 /* set host we are restoring */
1408 if ((lhost = is_dump_host_valid(arg)) != NULL)
1410 dump_hostname = newstralloc(dump_hostname, lhost->hostname);
1411 reply(200, "Dump host set to %s.", dump_hostname);
1412 amfree(qdisk_name); /* invalidate any value */
1413 amfree(disk_name); /* invalidate any value */
1416 } else if (strcmp(cmd, "LISTHOST") == 0) {
1422 if (config_name == NULL) {
1423 reply(501, "Must set config before listhost");
1426 lreply(200, " List hosts for config %s", config_name);
1427 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1429 for (diskdup = disk_list.head; diskdup!=disk; diskdup = diskdup->next) {
1430 if(strcmp(diskdup->host->hostname, disk->host->hostname) == 0) {
1436 fast_lreply(201, " %s", disk->host->hostname);
1441 reply(200, " List hosts for config %s", config_name);
1444 reply(200, "No hosts for config %s", config_name);
1448 } else if (strcmp(cmd, "DISK") == 0 && arg) {
1450 if (is_disk_valid(arg) != -1) {
1451 disk_name = newstralloc(disk_name, arg);
1452 qdisk_name = quote_string(disk_name);
1453 if (build_disk_table() != -1) {
1454 reply(200, "Disk set to %s.", qdisk_name);
1458 } else if (strcmp(cmd, "LISTDISK") == 0) {
1463 if (config_name == NULL) {
1464 reply(501, "Must set config, host before listdisk");
1466 else if (dump_hostname == NULL) {
1467 reply(501, "Must set host before listdisk");
1470 lreply(200, " List of disk for device %s on host %s", arg,
1472 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1474 if (strcasecmp(disk->host->hostname, dump_hostname) == 0 &&
1475 ((disk->device && strcmp(disk->device, arg) == 0) ||
1476 (!disk->device && strcmp(disk->name, arg) == 0))) {
1477 qname = quote_string(disk->name);
1478 fast_lreply(201, " %s", qname);
1484 reply(200, "List of disk for device %s on host %s", arg,
1488 reply(200, "No disk for device %s on host %s", arg,
1493 lreply(200, " List of disk for host %s", dump_hostname);
1494 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1495 if(strcasecmp(disk->host->hostname, dump_hostname) == 0) {
1496 qname = quote_string(disk->name);
1497 fast_lreply(201, " %s", qname);
1503 reply(200, "List of disk for host %s", dump_hostname);
1506 reply(200, "No disk for host %s", dump_hostname);
1510 } else if (strcmp(cmd, "SCNF") == 0 && arg) {
1512 amfree(config_name);
1514 config_name = newstralloc(config_name, arg);
1515 config_dir = vstralloc(CONFIG_DIR, "/", config_name, "/", NULL);
1516 if (is_config_valid(arg) != -1) {
1517 amfree(dump_hostname); /* invalidate any value */
1518 amfree(qdisk_name); /* invalidate any value */
1519 amfree(disk_name); /* invalidate any value */
1520 reply(200, "Config set to %s.", config_name);
1522 amfree(config_name);
1526 } else if (strcmp(cmd, "FEATURES") == 0 && arg) {
1527 char *our_feature_string = NULL;
1528 char *their_feature_string = NULL;
1530 am_release_feature_set(our_features);
1531 am_release_feature_set(their_features);
1532 our_features = am_init_feature_set();
1533 our_feature_string = am_feature_to_string(our_features);
1534 their_feature_string = newstralloc(their_feature_string, arg);
1535 their_features = am_string_to_feature(their_feature_string);
1536 reply(200, "FEATURES %s", our_feature_string);
1537 amfree(our_feature_string);
1538 amfree(their_feature_string);
1540 } else if (strcmp(cmd, "DATE") == 0 && arg) {
1542 target_date = newstralloc(target_date, arg);
1543 reply(200, "Working date set to %s.", target_date);
1545 } else if (strcmp(cmd, "DHST") == 0) {
1546 (void)disk_history_list();
1547 } else if (strcmp(cmd, "OISD") == 0 && arg) {
1548 if (is_dir_valid_opaque(arg) != -1) {
1549 reply(200, "\"%s\" is a valid directory", arg);
1551 } else if (strcmp(cmd, "OLSD") == 0 && arg) {
1552 (void)opaque_ls(arg,0);
1553 } else if (strcmp(cmd, "ORLD") == 0 && arg) {
1554 (void)opaque_ls(arg, 1);
1555 } else if (strcmp(cmd, "TAPE") == 0) {
1557 } else if (strcmp(cmd, "DCMP") == 0) {
1558 (void)are_dumps_compressed();
1560 *cmd_undo = cmd_undo_ch; /* restore the command line */
1561 reply(500, "Command not recognised/incorrect: %s", cmd);
1567 uncompress_remove = remove_files(uncompress_remove);
1568 free_find_result(&output_find);
1569 reply(200, "Good bye.");
1578 static char nice[20];
1579 int year, month, day;
1580 int hours, minutes, seconds;
1581 char date[9], atime[7];
1582 int numdate, numtime;
1584 strncpy(date, datestamp, 8);
1586 numdate = atoi(date);
1587 year = numdate / 10000;
1588 month = (numdate / 100) % 100;
1589 day = numdate % 100;
1591 if(strlen(datestamp) <= 8) {
1592 snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d",
1596 strncpy(atime, &(datestamp[8]), 6);
1598 numtime = atoi(atime);
1599 hours = numtime / 10000;
1600 minutes = (numtime / 100) % 100;
1601 seconds = numtime % 100;
1603 snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d-%02d-%02d-%02d",
1604 year, month, day, hours, minutes, seconds);
1615 return strncmp(date1, date2, strlen(date2));
1622 char *s = line, *s1, *s2;
1631 if (*s != '\0' && isdigit(*s) &&
1632 *s1 != '\0' && isdigit(*s1) &&
1633 *s2 != '\0' && isdigit(*s2)) {
1634 /* this is \000, an octal value */
1635 i = ((*s)-'0')*64 + ((*s1)-'0')*8 + ((*s2)-'0');
1638 } else if (*s == '\\') { /* we remove one / */
1640 } else { /* we keep the / */
1656 char *dump_hostname,
1660 struct stat dir_stat;
1663 char *lower_hostname;
1665 lower_hostname = stralloc(dump_hostname);
1666 for(s=lower_hostname; *s != '\0'; s++)
1669 fn = getindexfname(dump_hostname, diskname, NULL, 0);
1670 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1671 amfree(lower_hostname);
1676 if (hostname != NULL) {
1677 fn = getindexfname(hostname, diskname, NULL, 0);
1678 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1679 amfree(lower_hostname);
1685 fn = getindexfname(lower_hostname, diskname, NULL, 0);
1686 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1687 amfree(lower_hostname);
1692 if(diskname != NULL) {
1693 fn = getoldindexfname(dump_hostname, diskname, NULL, 0);
1694 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1695 amfree(lower_hostname);
1700 if (hostname != NULL) {
1701 fn = getoldindexfname(hostname, diskname, NULL, 0);
1702 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1703 amfree(lower_hostname);
1709 fn = getoldindexfname(lower_hostname, diskname, NULL, 0);
1710 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1711 amfree(lower_hostname);
1717 amfree(lower_hostname);
1723 char *dump_hostname,
1729 struct stat dir_stat;
1732 char *lower_hostname;
1734 lower_hostname = stralloc(dump_hostname);
1735 for(s=lower_hostname; *s != '\0'; s++)
1738 fn = getindexfname(dump_hostname, diskname, timestamps, level);
1739 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1740 amfree(lower_hostname);
1744 if(hostname != NULL) {
1745 fn = getindexfname(hostname, diskname, timestamps, level);
1746 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1747 amfree(lower_hostname);
1752 fn = getindexfname(lower_hostname, diskname, timestamps, level);
1753 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1754 amfree(lower_hostname);
1758 if(diskname != NULL) {
1759 fn = getoldindexfname(dump_hostname, diskname, timestamps, level);
1760 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1761 amfree(lower_hostname);
1765 if(hostname != NULL) {
1766 fn = getoldindexfname(hostname, diskname, timestamps, level);
1767 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1768 amfree(lower_hostname);
1773 fn = getoldindexfname(lower_hostname, diskname, timestamps, level);
1774 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1775 amfree(lower_hostname);
1779 amfree(lower_hostname);