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"
57 #include "pipespawn.h"
58 #include "sockaddr-util.h"
63 #define DBG(i, ...) do { \
64 if ((i) <= debug_amindexd) { \
65 g_debug(__VA_ARGS__); \
69 typedef struct REMOVE_ITEM
72 struct REMOVE_ITEM *next;
76 static int from_amandad;
77 static char local_hostname[MAX_HOSTNAME_LENGTH+1]; /* me! */
78 static char *dump_hostname = NULL; /* machine we are restoring */
79 static char *disk_name; /* disk we are restoring */
80 char *qdisk_name = NULL; /* disk we are restoring */
81 static char *target_date = NULL;
82 static disklist_t disk_list; /* all disks in cur config */
83 static find_result_t *output_find = NULL;
84 static g_option_t *g_options = NULL;
86 static int amindexd_debug = 0;
88 static REMOVE_ITEM *uncompress_remove = NULL;
89 /* uncompressed files to remove */
91 static am_feature_t *our_features = NULL;
92 static am_feature_t *their_features = NULL;
94 static int get_pid_status(int pid, char *program, GPtrArray **emsg);
95 static REMOVE_ITEM *remove_files(REMOVE_ITEM *);
96 static char *uncompress_file(char *, GPtrArray **);
97 static int process_ls_dump(char *, DUMP_ITEM *, int, GPtrArray **);
99 static size_t reply_buffer_size = 1;
100 static char *reply_buffer = NULL;
101 static char *amandad_auth = NULL;
105 static void reply_ptr_array(int, GPtrArray *);
106 static void reply(int, char *, ...) G_GNUC_PRINTF(2, 3);
107 static void lreply(int, char *, ...) G_GNUC_PRINTF(2, 3);
108 static void fast_lreply(int, char *, ...) G_GNUC_PRINTF(2, 3);
109 static am_host_t *is_dump_host_valid(char *);
110 static int is_disk_valid(char *);
111 static int check_and_load_config(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 *get_index_name(char *dump_hostname, char *hostname,
123 char *diskname, char *timestamps, int level);
124 static int get_index_dir(char *dump_hostname, char *hostname, char *diskname);
126 int main(int, char **);
136 amwait_t wait_status;
140 status = waitpid(pid, &wait_status, 0);
143 _("%s (%d) returned negative value: %s"),
144 program, pid, strerror(errno));
145 dbprintf("%s\n", msg);
146 g_ptr_array_add(*emsg, msg);
149 if (!WIFEXITED(wait_status)) {
151 _("%s exited with signal %d"),
152 program, WTERMSIG(wait_status));
153 dbprintf("%s\n", msg);
154 g_ptr_array_add(*emsg, msg);
156 } else if (WEXITSTATUS(wait_status) != 0) {
158 _("%s exited with status %d"),
159 program, WEXITSTATUS(wait_status));
160 dbprintf("%s\n", msg);
161 g_ptr_array_add(*emsg, msg);
175 dbprintf(_("removing index file: %s\n"), remove->filename);
176 unlink(remove->filename);
177 amfree(remove->filename);
179 remove = remove->next;
191 char *filename = NULL;
192 struct stat stat_filename;
199 int uncompress_errfd;
210 GPtrArray *uncompress_err;
212 FILE *uncompress_err_stream;
213 FILE *sort_err_stream;
215 filename = stralloc(filename_gz);
216 len = strlen(filename);
217 if(len > 3 && strcmp(&(filename[len-3]),".gz")==0) {
218 filename[len-3]='\0';
219 } else if(len > 2 && strcmp(&(filename[len-2]),".Z")==0) {
220 filename[len-2]='\0';
223 /* uncompress the file */
224 result=stat(filename,&stat_filename);
225 if(result==-1 && errno==ENOENT) { /* file does not exist */
227 REMOVE_ITEM *remove_file;
230 * Check that compressed file exists manually.
232 if (stat(filename_gz, &statbuf) < 0) {
233 msg = vstrallocf(_("Compressed file '%s' is inaccessable: %s"),
234 filename_gz, strerror(errno));
235 dbprintf("%s\n", msg);
236 g_ptr_array_add(*emsg, msg);
241 #ifdef UNCOMPRESS_OPT
242 # define PARAM_UNCOMPRESS_OPT UNCOMPRESS_OPT
244 # define PARAM_UNCOMPRESS_OPT skip_argument
247 nullfd = open("/dev/null", O_RDONLY);
249 indexfd = open(filename,O_WRONLY|O_CREAT, 0600);
251 msg = vstrallocf(_("Can't open '%s' for writting: %s"),
252 filename, strerror(errno));
253 dbprintf("%s\n", msg);
254 g_ptr_array_add(*emsg, msg);
260 /* just use our stderr directly for the pipe's stderr; in
261 * main() we send stderr to the debug file, or /dev/null
262 * if debugging is disabled */
264 /* start the uncompress process */
265 putenv(stralloc("LC_ALL=C"));
266 pid_gzip = pipespawn(UNCOMPRESS_PATH, STDOUT_PIPE|STDERR_PIPE, 0,
267 &nullfd, &pipe_from_gzip, &uncompress_errfd,
268 UNCOMPRESS_PATH, PARAM_UNCOMPRESS_OPT,
272 pipe_stream = fdopen(pipe_from_gzip,"r");
273 if(pipe_stream == NULL) {
274 msg = vstrallocf(_("Can't fdopen pipe from gzip: %s"),
276 dbprintf("%s\n", msg);
277 g_ptr_array_add(*emsg, msg);
283 /* start the sort process */
284 putenv(stralloc("LC_ALL=C"));
285 if (getconf_seen(CNF_TMPDIR)) {
286 gchar *tmpdir = getconf_str(CNF_TMPDIR);
287 pid_sort = pipespawn(SORT_PATH, STDIN_PIPE|STDERR_PIPE, 0,
288 &pipe_to_sort, &indexfd, &sort_errfd,
289 SORT_PATH, "-T", tmpdir, NULL);
291 pid_sort = pipespawn(SORT_PATH, STDIN_PIPE|STDERR_PIPE, 0,
292 &pipe_to_sort, &indexfd, &sort_errfd,
297 /* start a subprocess */
298 /* send all ouput from uncompress process to sort process */
305 dbprintf("%s\n", msg);
306 g_ptr_array_add(*emsg, msg);
311 while (fgets(line, STR_SIZE, pipe_stream) != NULL) {
312 if (line[0] != '\0') {
313 if (strchr(line,'/')) {
314 full_write(pipe_to_sort,line,strlen(line));
322 aclose(pipe_to_sort);
324 uncompress_err_stream = fdopen(uncompress_errfd, "r");
325 uncompress_err = g_ptr_array_new();
326 while (fgets(line, sizeof(line), uncompress_err_stream) != NULL) {
327 if (line[strlen(line)-1] == '\n')
328 line[strlen(line)-1] = '\0';
329 g_ptr_array_add(uncompress_err, vstrallocf(" %s", line));
330 dbprintf("Uncompress: %s\n", line);
332 fclose(uncompress_err_stream);
334 sort_err_stream = fdopen(sort_errfd, "r");
335 sort_err = g_ptr_array_new();
336 while (fgets(line, sizeof(line), sort_err_stream) != NULL) {
337 if (line[strlen(line)-1] == '\n')
338 line[strlen(line)-1] = '\0';
339 g_ptr_array_add(sort_err, vstrallocf(" %s", line));
340 dbprintf("Sort: %s\n", line);
342 fclose(sort_err_stream);
344 status = get_pid_status(pid_gzip, UNCOMPRESS_PATH, emsg);
345 if (status == 0 && filename) {
349 if (uncompress_err->len > 0) {
350 p_last = uncompress_err->pdata + uncompress_err->len;
351 for (p = uncompress_err->pdata; p < p_last ;p++) {
352 g_ptr_array_add(*emsg, (char *)*p);
355 g_ptr_array_free(uncompress_err, TRUE);
357 status = get_pid_status(pid_index, "index", emsg);
358 if (status == 0 && filename) {
363 status = get_pid_status(pid_sort, SORT_PATH, emsg);
364 if (status == 0 && filename) {
368 if (sort_err->len > 0) {
369 p_last = sort_err->pdata + sort_err->len;
370 for (p = sort_err->pdata; p < p_last ;p++) {
371 g_ptr_array_add(*emsg, (char *)*p);
374 g_ptr_array_free(sort_err, TRUE);
376 /* add at beginning */
378 remove_file = (REMOVE_ITEM *)alloc(SIZEOF(REMOVE_ITEM));
379 remove_file->filename = stralloc(filename);
380 remove_file->next = uncompress_remove;
381 uncompress_remove = remove_file;
384 } else if(!S_ISREG((stat_filename.st_mode))) {
385 msg = vstrallocf(_("\"%s\" is not a regular file"), filename);
386 dbprintf("%s\n", msg);
387 g_ptr_array_add(*emsg, msg);
397 /* find all matching entries in a dump listing */
398 /* return -1 if error */
402 DUMP_ITEM * dump_item,
406 char line[STR_SIZE], old_line[STR_SIZE];
407 char *filename = NULL;
409 char *dir_slash = NULL;
413 size_t len_dir_slash;
416 if (strcmp(dir, "/") == 0) {
417 dir_slash = stralloc(dir);
419 dir_slash = stralloc2(dir, "/");
422 filename_gz = get_index_name(dump_hostname, dump_item->hostname, disk_name,
423 dump_item->date, dump_item->level);
424 if (filename_gz == NULL) {
425 g_ptr_array_add(*emsg, stralloc(_("index file not found")));
429 filename = uncompress_file(filename_gz, emsg);
430 if(filename == NULL) {
437 if((fp = fopen(filename,"r"))==0) {
438 g_ptr_array_add(*emsg, vstrallocf("%s", strerror(errno)));
444 len_dir_slash=strlen(dir_slash);
446 while (fgets(line, STR_SIZE, fp) != NULL) {
447 if (line[0] != '\0') {
448 if(line[strlen(line)-1] == '\n')
449 line[strlen(line)-1] = '\0';
450 if(strncmp(dir_slash, line, len_dir_slash) == 0) {
452 s = line + len_dir_slash;
454 while(ch && ch != '/')
455 ch = *s++;/* find end of the file name */
461 if(strcmp(line, old_line) != 0) {
462 add_dir_list_item(dump_item, line);
463 strcpy(old_line, line);
485 while (p != emsg->pdata + emsg->len -1) {
486 fast_lreply(n, "%s", (char *)*p);
489 reply(n, "%s", (char *)*p);
492 /* send a 1 line reply to the client */
493 printf_arglist_function1(static void reply, int, n, char *, fmt)
499 reply_buffer = alloc(reply_buffer_size);
502 arglist_start(args, fmt);
503 len = g_vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
506 if (len > -1 && (size_t)len < reply_buffer_size-1)
509 reply_buffer_size *= 2;
510 amfree(reply_buffer);
511 reply_buffer = alloc(reply_buffer_size);
514 if (g_fprintf(cmdout,"%03d %s\r\n", n, reply_buffer) < 0)
516 dbprintf(_("! error %d (%s) in printf\n"), errno, strerror(errno));
517 uncompress_remove = remove_files(uncompress_remove);
520 if (fflush(cmdout) != 0)
522 dbprintf(_("! error %d (%s) in fflush\n"), errno, strerror(errno));
523 uncompress_remove = remove_files(uncompress_remove);
526 dbprintf(_("< %03d %s\n"), n, reply_buffer);
529 /* send one line of a multi-line response */
530 printf_arglist_function1(static void lreply, int, n, char *, fmt)
536 reply_buffer = alloc(reply_buffer_size);
539 arglist_start(args, fmt);
540 len = g_vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
543 if (len > -1 && (size_t)len < reply_buffer_size-1)
546 reply_buffer_size *= 2;
547 amfree(reply_buffer);
548 reply_buffer = alloc(reply_buffer_size);
551 if (g_fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0)
553 dbprintf(_("! error %d (%s) in printf\n"),
554 errno, strerror(errno));
555 uncompress_remove = remove_files(uncompress_remove);
558 if (fflush(cmdout) != 0)
560 dbprintf(_("! error %d (%s) in fflush\n"),
561 errno, strerror(errno));
562 uncompress_remove = remove_files(uncompress_remove);
566 dbprintf("< %03d-%s\n", n, reply_buffer);
570 /* send one line of a multi-line response */
571 printf_arglist_function1(static void fast_lreply, int, n, char *, fmt)
577 reply_buffer = alloc(reply_buffer_size);
580 arglist_start(args, fmt);
581 len = g_vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
584 if (len > -1 && (size_t)len < reply_buffer_size-1)
587 reply_buffer_size *= 2;
588 amfree(reply_buffer);
589 reply_buffer = alloc(reply_buffer_size);
592 if (g_fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0)
594 dbprintf(_("! error %d (%s) in printf\n"),
595 errno, strerror(errno));
596 uncompress_remove = remove_files(uncompress_remove);
600 DBG(2, "< %03d-%s", n, reply_buffer);
603 /* see if hostname is valid */
604 /* valid is defined to be that there is an index directory for it */
605 /* also do a security check on the requested dump hostname */
606 /* to restrict access to index records if required */
607 /* return -1 if not okay */
615 if (get_config_name() == NULL) {
616 reply(501, _("Must set config before setting host."));
620 /* check that the config actually handles that host */
621 ihost = lookup_host(host);
623 reply(501, _("Host %s is not in your disklist."), host);
627 /* check if an index dir exist */
628 if(get_index_dir(host, ihost->hostname, NULL)) {
632 /* check if an index dir exist for at least one DLE */
633 for(diskp = ihost->disks; diskp != NULL; diskp = diskp->hostnext) {
634 if (get_index_dir(diskp->hostname, NULL, NULL)) {
639 reply(501, _("No index records for host: %s. Have you enabled indexing?"),
649 dumptype_t *dt = disk->config;
650 host_limit_t *rl = NULL;
655 /* get the config: either for the DLE or the global config */
657 if (dumptype_seen(dt, DUMPTYPE_RECOVERY_LIMIT)) {
658 g_debug("using recovery limit from DLE");
659 rl = dumptype_get_recovery_limit(dt);
663 if (getconf_seen(CNF_RECOVERY_LIMIT)) {
664 g_debug("using global recovery limit");
665 rl = getconf_recovery_limit(CNF_RECOVERY_LIMIT);
669 g_debug("no recovery limit found; allowing access");
673 peer = getenv("AMANDA_AUTHENTICATED_PEER");
674 if (!peer || !*peer) {
675 g_warning("DLE has a recovery-limit, but no authenticated peer name is "
676 "available; rejecting");
680 /* check same-host */
681 dle_hostname = disk->host? disk->host->hostname : NULL;
682 if (rl->same_host && dle_hostname) {
683 if (0 == g_ascii_strcasecmp(peer, dle_hostname)) {
684 g_debug("peer matched same-host ('%s')", dle_hostname);
691 char myhostname[MAX_HOSTNAME_LENGTH+1];
692 if (gethostname(myhostname, MAX_HOSTNAME_LENGTH) == 0) {
693 myhostname[MAX_HOSTNAME_LENGTH] = '\0';
694 g_debug("server hostname: %s", myhostname);
695 if (0 == g_ascii_strcasecmp(peer, myhostname)) {
696 g_debug("peer matched server ('%s')", myhostname);
702 /* check the match list */
703 for (iter = rl->match_pats; iter; iter = iter->next) {
704 char *pat = iter->data;
705 if (match_host(pat, peer))
709 g_warning("peer '%s' does not match any of the recovery-limit restrictions; rejecting",
722 if (get_config_name() == NULL) {
723 reply(501, _("Must set config,host before setting disk."));
726 else if (dump_hostname == NULL) {
727 reply(501, _("Must set host before setting disk."));
731 /* check that the config actually handles that disk, and that recovery-limit allows it */
732 idisk = lookup_disk(dump_hostname, disk);
733 if(idisk == NULL || !is_disk_allowed(idisk)) {
734 qdisk = quote_string(disk);
735 reply(501, _("Disk %s:%s is not in the server's disklist."), dump_hostname, qdisk);
740 /* assume an index dir already */
741 if (get_index_dir(dump_hostname, idisk->hostname, disk) == 0) {
742 qdisk = quote_string(disk);
743 reply(501, _("No index records for disk: %s. Invalid?"), qdisk);
753 check_and_load_config(
759 struct stat dir_stat;
761 /* check that the config actually exists */
762 if (config == NULL) {
763 reply(501, _("Must set config first."));
767 /* (re-)initialize configuration with the new config name */
768 config_init(CONFIG_INIT_EXPLICIT_NAME, config);
769 if (config_errors(NULL) >= CFGERR_ERRORS) {
770 reply(501, _("Could not read config file for %s!"), config);
774 check_running_as(RUNNING_AS_DUMPUSER_PREFERRED);
776 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
777 read_diskfile(conf_diskfile, &disk_list);
778 amfree(conf_diskfile);
779 if (config_errors(NULL) >= CFGERR_ERRORS) {
780 reply(501, _("Could not read disk file %s!"), conf_diskfile);
784 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
785 if(read_tapelist(conf_tapelist)) {
786 reply(501, _("Could not read tapelist file %s!"), conf_tapelist);
787 amfree(conf_tapelist);
790 amfree(conf_tapelist);
792 dbrename(get_config_name(), DBG_SUBDIR_SERVER);
794 output_find = find_dump(&disk_list);
795 /* the 'w' here sorts by write timestamp, so that the first instance of
796 * any particular datestamp/host/disk/level/part that we see is the one
798 sort_find_result("DLKHpwB", &output_find);
800 conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR));
801 if (stat (conf_indexdir, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
802 reply(501, _("Index directory %s does not exist"), conf_indexdir);
803 amfree(conf_indexdir);
806 amfree(conf_indexdir);
813 build_disk_table(void)
816 char *last_timestamp;
820 find_result_t *find_output;
822 if (get_config_name() == NULL) {
823 reply(590, _("Must set config,host,disk before building disk table"));
826 else if (dump_hostname == NULL) {
827 reply(590, _("Must set host,disk before building disk table"));
830 else if (disk_name == NULL) {
831 reply(590, _("Must set disk before building disk table"));
836 last_timestamp = NULL;
837 last_filenum = (off_t)-1;
840 for(find_output = output_find;
842 find_output = find_output->next) {
843 if(strcasecmp(dump_hostname, find_output->hostname) == 0 &&
844 strcmp(disk_name , find_output->diskname) == 0 &&
845 strcmp("OK" , find_output->status) == 0 &&
846 strcmp("OK" , find_output->dump_status) == 0) {
848 * The sort order puts holding disk entries first. We want to
849 * use them if at all possible, so ignore any other entries
850 * for the same datestamp after we see a holding disk entry
851 * (as indicated by a filenum of zero).
854 strcmp(find_output->timestamp, last_timestamp) == 0 &&
855 find_output->level == last_level &&
859 /* ignore duplicate partnum */
861 strcmp(find_output->timestamp, last_timestamp) == 0 &&
862 find_output->level == last_level &&
863 find_output->partnum == last_partnum) {
866 last_timestamp = find_output->timestamp;
867 last_filenum = find_output->filenum;
868 last_level = find_output->level;
869 last_partnum = find_output->partnum;
870 date = amindexd_nicedate(find_output->timestamp);
871 add_dump(find_output->hostname, date, find_output->level,
872 find_output->label, find_output->filenum,
873 find_output->partnum, find_output->totalparts);
874 dbprintf("- %s %d %s %lld %d %d\n",
875 date, find_output->level,
877 (long long)find_output->filenum,
878 find_output->partnum, find_output->totalparts);
889 disk_history_list(void)
894 if (get_config_name() == NULL) {
895 reply(502, _("Must set config,host,disk before listing history"));
898 else if (dump_hostname == NULL) {
899 reply(502, _("Must set host,disk before listing history"));
902 else if (disk_name == NULL) {
903 reply(502, _("Must set disk before listing history"));
907 lreply(200, _(" Dump history for config \"%s\" host \"%s\" disk %s"),
908 get_config_name(), dump_hostname, qdisk_name);
910 for (item=first_dump(); item!=NULL; item=next_dump(item)){
911 char *tapelist_str = marshal_tapelist(item->tapes, 1);
913 strncpy(date, item->date, 20);
915 if(!am_has_feature(their_features,fe_amrecover_timestamp))
918 if(am_has_feature(their_features, fe_amindexd_marshall_in_DHST)){
919 lreply(201, " %s %d %s", date, item->level, tapelist_str);
922 lreply(201, " %s %d %s %lld", date, item->level,
923 tapelist_str, (long long)item->file);
925 amfree(tapelist_str);
928 reply(200, _("Dump history for config \"%s\" host \"%s\" disk %s"),
929 get_config_name(), dump_hostname, qdisk_name);
936 * is the directory dir backed up - dir assumed complete relative to
939 /* opaque version of command */
949 char *filename_gz = NULL;
950 char *filename = NULL;
952 GPtrArray *emsg = NULL;
954 if (get_config_name() == NULL || dump_hostname == NULL || disk_name == NULL) {
955 reply(502, _("Must set config,host,disk before asking about directories"));
958 else if (dump_hostname == NULL) {
959 reply(502, _("Must set host,disk before asking about directories"));
962 else if (disk_name == NULL) {
963 reply(502, _("Must set disk before asking about directories"));
966 else if (target_date == NULL) {
967 reply(502, _("Must set date before asking about directories"));
970 /* scan through till we find first dump on or before date */
971 for (item=first_dump(); item!=NULL; item=next_dump(item))
972 if (cmp_date(item->date, target_date) <= 0)
977 /* no dump for given date */
978 reply(500, _("No dumps available on or before date \"%s\""), target_date);
982 if(strcmp(dir, "/") == 0) {
983 ldir = stralloc(dir);
985 ldir = stralloc2(dir, "/");
987 ldir_len = strlen(ldir);
989 /* go back till we hit a level 0 dump */
993 filename_gz = get_index_name(dump_hostname, item->hostname, disk_name,
994 item->date, item->level);
995 if (filename_gz == NULL) {
996 reply(599, "index not found");
1000 emsg = g_ptr_array_new();
1001 if((filename = uncompress_file(filename_gz, &emsg)) == NULL) {
1002 reply_ptr_array(599, emsg);
1003 amfree(filename_gz);
1004 g_ptr_array_free_full(emsg);
1008 g_ptr_array_free_full(emsg);
1009 amfree(filename_gz);
1010 dbprintf("f %s\n", filename);
1011 if ((fp = fopen(filename, "r")) == NULL) {
1012 reply(599, _("System error: %s"), strerror(errno));
1017 while (fgets(line, STR_SIZE, fp) != NULL) {
1018 if (line[0] == '\0')
1020 if(line[strlen(line)-1] == '\n')
1021 line[strlen(line)-1] = '\0';
1022 if (strncmp(line, ldir, ldir_len) != 0) {
1023 continue; /* not found yet */
1032 last_level = item->level;
1035 item=next_dump(item);
1036 } while ((item != NULL) && (item->level >= last_level));
1037 } while (item != NULL);
1041 reply(500, _("\"%s\" is an invalid directory"), dir);
1050 DUMP_ITEM *dump_item;
1052 int level, last_level;
1053 GPtrArray *emsg = NULL;
1054 am_feature_e marshall_feature;
1057 marshall_feature = fe_amindexd_marshall_in_ORLD;
1059 marshall_feature = fe_amindexd_marshall_in_OLSD;
1064 if (get_config_name() == NULL) {
1065 reply(502, _("Must set config,host,disk before listing a directory"));
1068 else if (dump_hostname == NULL) {
1069 reply(502, _("Must set host,disk before listing a directory"));
1072 else if (disk_name == NULL) {
1073 reply(502, _("Must set disk before listing a directory"));
1076 else if (target_date == NULL) {
1077 reply(502, _("Must set date before listing a directory"));
1081 /* scan through till we find first dump on or before date */
1082 for (dump_item=first_dump(); dump_item!=NULL; dump_item=next_dump(dump_item))
1083 if (cmp_date(dump_item->date, target_date) <= 0)
1086 if (dump_item == NULL)
1088 /* no dump for given date */
1089 reply(500, _("No dumps available on or before date \"%s\""), target_date);
1093 /* get data from that dump */
1094 emsg = g_ptr_array_new();
1095 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
1096 reply_ptr_array(599, emsg);
1097 g_ptr_array_free_full(emsg);
1101 /* go back processing higher level dumps till we hit a level 0 dump */
1102 last_level = dump_item->level;
1103 while ((last_level != 0) && ((dump_item=next_dump(dump_item)) != NULL))
1105 if (dump_item->level < last_level)
1107 last_level = dump_item->level;
1108 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
1109 reply_ptr_array(599, emsg);
1110 g_ptr_array_free_full(emsg);
1115 g_ptr_array_free_full(emsg);
1117 /* return the information to the caller */
1118 lreply(200, _(" Opaque list of %s"), dir);
1119 for(level=0; level < DUMP_LEVELS; level++) {
1120 for (dir_item = get_dir_list(); dir_item != NULL;
1121 dir_item = dir_item->next) {
1123 if(dir_item->dump->level == level) {
1124 if (!am_has_feature(their_features, marshall_feature) &&
1125 (num_entries(dir_item->dump->tapes) > 1 ||
1126 dir_item->dump->tapes->numfiles > 1)) {
1127 fast_lreply(501, _(" ERROR: Split dumps not supported"
1128 " with old version of amrecover."));
1132 opaque_ls_one(dir_item, marshall_feature, recursive);
1137 reply(200, _(" Opaque list of %s"), dir);
1144 DIR_ITEM * dir_item,
1145 am_feature_e marshall_feature,
1150 char *qtapelist_str;
1153 if (am_has_feature(their_features, marshall_feature)) {
1154 tapelist_str = marshal_tapelist(dir_item->dump->tapes, 1);
1156 tapelist_str = dir_item->dump->tapes->label;
1159 if (am_has_feature(their_features, fe_amindexd_quote_label)) {
1160 qtapelist_str = quote_string(tapelist_str);
1162 qtapelist_str = stralloc(tapelist_str);
1164 strncpy(date, dir_item->dump->date, 20);
1166 if(!am_has_feature(their_features,fe_amrecover_timestamp))
1169 qpath = quote_string(dir_item->path);
1170 if((!recursive && am_has_feature(their_features,
1171 fe_amindexd_fileno_in_OLSD)) ||
1172 (recursive && am_has_feature(their_features,
1173 fe_amindexd_fileno_in_ORLD))) {
1174 fast_lreply(201, " %s %d %s %lld %s",
1176 dir_item->dump->level,
1178 (long long)dir_item->dump->file,
1183 fast_lreply(201, " %s %d %s %s",
1184 date, dir_item->dump->level,
1185 qtapelist_str, qpath);
1188 if(am_has_feature(their_features, marshall_feature)) {
1189 amfree(tapelist_str);
1191 amfree(qtapelist_str);
1195 * returns the value of changer or tapedev from the amanda.conf file if set,
1196 * otherwise reports an error
1204 /* check state okay to do this */
1205 if (get_config_name() == NULL) {
1206 reply(501, _("Must set config before asking about tapedev."));
1210 /* use amrecover_changer if possible */
1211 if ((result = getconf_str(CNF_AMRECOVER_CHANGER)) != NULL &&
1213 dbprintf(_("tapedev_is amrecover_changer: %s\n"), result);
1214 reply(200, "%s", result);
1218 /* use changer if possible */
1219 if ((result = getconf_str(CNF_TPCHANGER)) != NULL && *result != '\0') {
1220 dbprintf(_("tapedev_is tpchanger: %s\n"), result);
1221 reply(200, "%s", result);
1225 /* get tapedev value */
1226 if ((result = getconf_str(CNF_TAPEDEV)) != NULL && *result != '\0') {
1227 dbprintf(_("tapedev_is tapedev: %s\n"), result);
1228 reply(200, "%s", result);
1232 dbprintf(_("No tapedev or tpchanger in config site.\n"));
1234 reply(501, _("Tapedev or tpchanger not set in config file."));
1239 /* returns YES if dumps for disk are compressed, NO if not */
1241 are_dumps_compressed(void)
1245 /* check state okay to do this */
1246 if (get_config_name() == NULL) {
1247 reply(501, _("Must set config,host,disk name before asking about dumps."));
1250 else if (dump_hostname == NULL) {
1251 reply(501, _("Must set host,disk name before asking about dumps."));
1254 else if (disk_name == NULL) {
1255 reply(501, _("Must set disk name before asking about dumps."));
1259 /* now go through the list of disks and find which have indexes */
1260 for (diskp = disk_list.head; diskp != NULL; diskp = diskp->next) {
1261 if ((strcasecmp(diskp->host->hostname, dump_hostname) == 0)
1262 && (strcmp(diskp->name, disk_name) == 0)) {
1267 if (diskp == NULL) {
1268 reply(501, _("Couldn't find host/disk in disk file."));
1272 /* send data to caller */
1273 if (diskp->compress == COMP_NONE)
1286 char *line = NULL, *part = NULL;
1289 char *cmd_undo, cmd_undo_ch;
1290 socklen_t_equiv socklen;
1291 sockaddr_union his_addr;
1295 int user_validated = 0;
1296 char *errstr = NULL;
1297 char *pgm = "amindexd"; /* in case argv[0] is not set */
1298 char his_hostname[MAX_HOSTNAME_LENGTH];
1299 char *cfg_opt = NULL;
1301 if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
1302 printf("amindexd-%s\n", VERSION);
1307 * Configure program for internationalization:
1308 * 1) Only set the message locale for now.
1309 * 2) Set textdomain for all amanda related programs to "amanda"
1310 * We don't want to be forced to support dozens of message catalogs.
1312 setlocale(LC_MESSAGES, "C");
1313 textdomain("amanda");
1315 safe_fd(DATA_FD_OFFSET, 2);
1316 openbsd_fd_inform();
1320 * When called via inetd, it is not uncommon to forget to put the
1321 * argv[0] value on the config line. On some systems (e.g. Solaris)
1322 * this causes argv and/or argv[0] to be NULL, so we have to be
1323 * careful getting our name.
1325 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
1326 if((pgm = strrchr(argv[0], '/')) != NULL) {
1335 /* Don't die when child closes pipe */
1336 signal(SIGPIPE, SIG_IGN);
1338 dbopen(DBG_SUBDIR_SERVER);
1339 dbprintf(_("version %s\n"), VERSION);
1342 error("argv == NULL\n");
1345 if (! (argc >= 1 && argv[0] != NULL)) {
1346 dbprintf(_("WARNING: argv[0] not defined: check inetd.conf\n"));
1349 debug_dup_stderr_to_debug();
1356 if(argc > 0 && strcmp(*argv, "-t") == 0) {
1362 if(argc > 0 && strcmp(*argv, "amandad") == 0) {
1367 amandad_auth = *argv;
1383 if(gethostname(local_hostname, SIZEOF(local_hostname)-1) == -1) {
1384 error(_("gethostname: %s"), strerror(errno));
1387 local_hostname[SIZEOF(local_hostname)-1] = '\0';
1389 /* now trim domain off name */
1392 while(ch && ch != '.') ch = *s++;
1396 if(from_amandad == 0) {
1397 if(!amindexd_debug) {
1398 /* who are we talking to? */
1399 socklen = sizeof (his_addr);
1400 if (getpeername(0, (struct sockaddr *)&his_addr, &socklen) == -1)
1401 error(_("getpeername: %s"), strerror(errno));
1403 /* Try a reverse (IP->hostname) resolution, and fail if it does
1404 * not work -- this is a basic security check */
1405 if (getnameinfo((struct sockaddr *)&his_addr, SS_LEN(&his_addr),
1406 his_hostname, sizeof(his_hostname),
1409 error(_("getnameinfo(%s): hostname lookup failed"),
1410 str_sockaddr(&his_addr));
1415 /* Set up the input and output FILEs */
1420 /* read the REQ packet */
1421 for(; (line = agets(stdin)) != NULL; free(line)) {
1422 if(strncmp_const(line, "OPTIONS ") == 0) {
1423 if (g_options != NULL) {
1424 dbprintf(_("REQ packet specified multiple OPTIONS.\n"));
1425 free_g_options(g_options);
1427 g_options = parse_g_options(line+8, 1);
1428 if(!g_options->hostname) {
1429 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
1430 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
1431 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
1437 if(amandad_auth && g_options->auth) {
1438 if(strcasecmp(amandad_auth, g_options->auth) != 0) {
1439 g_printf(_("ERROR recover program ask for auth=%s while amindexd is configured for '%s'\n"),
1440 g_options->auth, amandad_auth);
1441 error(_("amindexd: ERROR recover program ask for auth=%s while amindexd is configured for '%s'"),
1442 g_options->auth, amandad_auth);
1446 /* send the REP packet */
1447 g_printf("CONNECT MESG %d\n", DATA_FD_OFFSET);
1454 cmdout = fdopen(DATA_FD_OFFSET + 0, "a");
1456 error(_("amindexd: Can't fdopen(%d): %s"), DATA_FD_OFFSET + 0, strerror(errno));
1460 cmdin = fdopen(DATA_FD_OFFSET + 1, "r");
1462 error(_("amindexd: Can't fdopen(%d): %s"), DATA_FD_OFFSET + 1, strerror(errno));
1467 /* clear these so we can detect when the have not been set by the client */
1468 amfree(dump_hostname);
1471 amfree(target_date);
1473 our_features = am_init_feature_set();
1474 their_features = am_set_default_feature_set();
1476 if (cfg_opt != NULL && check_and_load_config(cfg_opt) != -1) { /* load the config */
1480 reply(220, _("%s AMANDA index server (%s) ready."), local_hostname,
1483 user_validated = from_amandad;
1485 /* a real simple parser since there are only a few commands */
1488 /* get a line from the client */
1490 if((part = agets(cmdin)) == NULL) {
1492 dbprintf(_("? read error: %s\n"), strerror(errno));
1494 dbprintf(_("? unexpected EOF\n"));
1497 dbprintf(_("? unprocessed input:\n"));
1498 dbprintf("-----\n");
1499 dbprintf("? %s\n", line);
1500 dbprintf("-----\n");
1504 uncompress_remove = remove_files(uncompress_remove);
1506 return 1; /* they hung up? */
1508 strappend(line, part); /* Macro: line can be null */
1511 if(amindexd_debug) {
1512 break; /* we have a whole line */
1514 if((len = strlen(line)) > 0 && line[len-1] == '\r') {
1515 line[len-1] = '\0'; /* zap the '\r' */
1519 * Hmmm. We got a "line" from agets(), which means it saw
1520 * a '\n' (or EOF, etc), but there was not a '\r' before it.
1521 * Put a '\n' back in the buffer and loop for more.
1523 strappend(line, "\n");
1526 dbprintf("> %s\n", line);
1533 skip_whitespace(s, ch);
1535 reply(500, _("Command not recognised/incorrect: %s"), line);
1541 skip_non_whitespace(s, ch);
1542 cmd_undo = s-1; /* for error message */
1543 cmd_undo_ch = *cmd_undo;
1546 skip_whitespace(s, ch); /* find the argument */
1549 skip_quoted_string(s, ch);
1550 arg = unquote_string(arg);
1555 if (!user_validated && strcmp(cmd, "SECURITY") == 0 && arg) {
1556 user_validated = amindexd_debug ||
1558 (sockaddr_union *)&his_addr,
1560 if(user_validated) {
1561 reply(200, _("Access OK"));
1566 if (!user_validated) { /* don't tell client the reason, just log it to debug log */
1567 reply(500, _("Access not allowed"));
1569 dbprintf("%s\n", errstr);
1574 if (strcmp(cmd, "QUIT") == 0) {
1577 } else if (strcmp(cmd, "HOST") == 0 && arg) {
1579 /* set host we are restoring */
1581 if ((lhost = is_dump_host_valid(arg)) != NULL)
1583 dump_hostname = newstralloc(dump_hostname, lhost->hostname);
1584 reply(200, _("Dump host set to %s."), dump_hostname);
1585 amfree(qdisk_name); /* invalidate any value */
1586 amfree(disk_name); /* invalidate any value */
1589 } else if (strcmp(cmd, "LISTHOST") == 0) {
1595 if (get_config_name() == NULL) {
1596 reply(501, _("Must set config before listhost"));
1599 lreply(200, _(" List hosts for config %s"), get_config_name());
1600 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1602 for (diskdup = disk_list.head; diskdup!=disk; diskdup = diskdup->next) {
1603 if(strcmp(diskdup->host->hostname, disk->host->hostname) == 0) {
1609 fast_lreply(201, " %s", disk->host->hostname);
1614 reply(200, _(" List hosts for config %s"), get_config_name());
1617 reply(200, _("No hosts for config %s"), get_config_name());
1621 } else if (strcmp(cmd, "DISK") == 0 && arg) {
1623 if (is_disk_valid(arg) != -1) {
1624 disk_name = newstralloc(disk_name, arg);
1625 qdisk_name = quote_string(disk_name);
1626 if (build_disk_table() != -1) {
1627 reply(200, _("Disk set to %s."), qdisk_name);
1631 } else if (strcmp(cmd, "DLE") == 0) {
1637 dp = lookup_disk(dump_hostname, disk_name);
1638 if (dp->line == 0) {
1639 reply(200, "NODLE");
1641 GPtrArray *errarray;
1644 b64disk = amxml_format_tag("disk", dp->name);
1645 dp->host->features = their_features;
1646 errarray = validate_optionstr(dp);
1647 if (errarray->len > 0) {
1648 for (i=0; i < errarray->len; i++) {
1649 g_debug(_("ERROR: %s:%s %s"),
1650 dump_hostname, disk_name,
1651 (char *)g_ptr_array_index(errarray, i));
1653 g_ptr_array_free(errarray, TRUE);
1654 reply(200, "NODLE");
1656 optionstr = xml_optionstr(dp, 0);
1657 l = vstralloc("<dle>\n",
1658 " <program>", dp->program, "</program>\n", NULL);
1659 if (dp->application) {
1660 application_t *application;
1663 application = lookup_application(dp->application);
1664 g_assert(application != NULL);
1665 xml_app = xml_application(dp, application,
1667 vstrextend(&l, xml_app, NULL);
1670 vstrextend(&l, " ", b64disk, "\n", NULL);
1672 char *b64device = amxml_format_tag("diskdevice",
1674 vstrextend(&l, " ", b64device, "\n", NULL);
1677 vstrextend(&l, optionstr, "</dle>\n", NULL);
1678 ql = quote_string(l);
1679 reply(200, "%s", ql);
1686 } else if (strcmp(cmd, "LISTDISK") == 0) {
1691 if (get_config_name() == NULL) {
1692 reply(501, _("Must set config, host before listdisk"));
1694 else if (dump_hostname == NULL) {
1695 reply(501, _("Must set host before listdisk"));
1698 lreply(200, _(" List of disk for device %s on host %s"), arg,
1700 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1702 if (strcasecmp(disk->host->hostname, dump_hostname) == 0 &&
1703 ((disk->device && strcmp(disk->device, arg) == 0) ||
1704 (!disk->device && strcmp(disk->name, arg) == 0))) {
1705 qname = quote_string(disk->name);
1706 fast_lreply(201, " %s", qname);
1712 reply(200, _("List of disk for device %s on host %s"), arg,
1716 reply(200, _("No disk for device %s on host %s"), arg,
1721 lreply(200, _(" List of disk for host %s"), dump_hostname);
1722 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1723 if(strcasecmp(disk->host->hostname, dump_hostname) == 0) {
1724 qname = quote_string(disk->name);
1725 fast_lreply(201, " %s", qname);
1731 reply(200, _("List of disk for host %s"), dump_hostname);
1734 reply(200, _("No disk for host %s"), dump_hostname);
1738 } else if (strcmp(cmd, "SCNF") == 0 && arg) {
1740 if (check_and_load_config(arg) != -1) { /* try to load the new config */
1741 amfree(dump_hostname); /* invalidate any value */
1742 amfree(qdisk_name); /* invalidate any value */
1743 amfree(disk_name); /* invalidate any value */
1744 reply(200, _("Config set to %s."), get_config_name());
1745 } /* check_and_load_config replies with any failure messages */
1747 } else if (strcmp(cmd, "FEATURES") == 0 && arg) {
1748 char *our_feature_string = NULL;
1749 char *their_feature_string = NULL;
1751 am_release_feature_set(our_features);
1752 am_release_feature_set(their_features);
1753 our_features = am_init_feature_set();
1754 our_feature_string = am_feature_to_string(our_features);
1755 their_feature_string = newstralloc(their_feature_string, arg);
1756 their_features = am_string_to_feature(their_feature_string);
1757 if (!their_features) {
1758 g_warning("Invalid client feature set '%s'", their_feature_string);
1759 their_features = am_set_default_feature_set();
1761 reply(200, "FEATURES %s", our_feature_string);
1762 amfree(our_feature_string);
1763 amfree(their_feature_string);
1765 } else if (strcmp(cmd, "DATE") == 0 && arg) {
1767 target_date = newstralloc(target_date, arg);
1768 reply(200, _("Working date set to %s."), target_date);
1770 } else if (strcmp(cmd, "DHST") == 0) {
1771 (void)disk_history_list();
1772 } else if (strcmp(cmd, "OISD") == 0 && arg) {
1773 if (is_dir_valid_opaque(arg) != -1) {
1774 reply(200, _("\"%s\" is a valid directory"), arg);
1776 } else if (strcmp(cmd, "OLSD") == 0 && arg) {
1777 (void)opaque_ls(arg,0);
1778 } else if (strcmp(cmd, "ORLD") == 0 && arg) {
1779 (void)opaque_ls(arg, 1);
1780 } else if (strcmp(cmd, "TAPE") == 0) {
1782 } else if (strcmp(cmd, "DCMP") == 0) {
1783 (void)are_dumps_compressed();
1785 *cmd_undo = cmd_undo_ch; /* restore the command line */
1786 reply(500, _("Command not recognised/incorrect: %s"), cmd);
1792 uncompress_remove = remove_files(uncompress_remove);
1793 free_find_result(&output_find);
1794 reply(200, _("Good bye."));
1803 static char nice[20];
1804 int year, month, day;
1805 int hours, minutes, seconds;
1806 char date[9], atime[7];
1807 int numdate, numtime;
1809 strncpy(date, datestamp, 8);
1811 numdate = atoi(date);
1812 year = numdate / 10000;
1813 month = (numdate / 100) % 100;
1814 day = numdate % 100;
1816 if(strlen(datestamp) <= 8) {
1817 g_snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d",
1821 strncpy(atime, &(datestamp[8]), 6);
1823 numtime = atoi(atime);
1824 hours = numtime / 10000;
1825 minutes = (numtime / 100) % 100;
1826 seconds = numtime % 100;
1828 g_snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d-%02d-%02d-%02d",
1829 year, month, day, hours, minutes, seconds);
1840 return strncmp(date1, date2, strlen(date2));
1845 char *dump_hostname,
1849 struct stat dir_stat;
1852 char *lower_hostname;
1854 lower_hostname = stralloc(dump_hostname);
1855 for(s=lower_hostname; *s != '\0'; s++)
1858 fn = getindexfname(dump_hostname, diskname, NULL, 0);
1859 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1860 amfree(lower_hostname);
1865 if (hostname != NULL) {
1866 fn = getindexfname(hostname, diskname, NULL, 0);
1867 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1868 amfree(lower_hostname);
1874 fn = getindexfname(lower_hostname, diskname, NULL, 0);
1875 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1876 amfree(lower_hostname);
1881 if(diskname != NULL) {
1882 fn = getoldindexfname(dump_hostname, diskname, NULL, 0);
1883 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1884 amfree(lower_hostname);
1889 if (hostname != NULL) {
1890 fn = getoldindexfname(hostname, diskname, NULL, 0);
1891 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1892 amfree(lower_hostname);
1898 fn = getoldindexfname(lower_hostname, diskname, NULL, 0);
1899 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1900 amfree(lower_hostname);
1906 amfree(lower_hostname);
1912 char *dump_hostname,
1918 struct stat dir_stat;
1921 char *lower_hostname;
1923 lower_hostname = stralloc(dump_hostname);
1924 for(s=lower_hostname; *s != '\0'; s++)
1927 fn = getindexfname(dump_hostname, diskname, timestamps, level);
1928 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1929 amfree(lower_hostname);
1933 if(hostname != NULL) {
1934 fn = getindexfname(hostname, diskname, timestamps, level);
1935 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1936 amfree(lower_hostname);
1941 fn = getindexfname(lower_hostname, diskname, timestamps, level);
1942 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1943 amfree(lower_hostname);
1947 if(diskname != NULL) {
1948 fn = getoldindexfname(dump_hostname, diskname, timestamps, level);
1949 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1950 amfree(lower_hostname);
1954 if(hostname != NULL) {
1955 fn = getoldindexfname(hostname, diskname, timestamps, level);
1956 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1957 amfree(lower_hostname);
1962 fn = getoldindexfname(lower_hostname, diskname, timestamps, level);
1963 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1964 amfree(lower_hostname);
1968 amfree(lower_hostname);