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.
49 #include "disk_history.h"
56 #include "pipespawn.h"
57 #include "sockaddr-util.h"
62 #define DBG(i, ...) do { \
63 if ((i) <= debug_amindexd) { \
64 g_debug(__VA_ARGS__); \
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 int get_pid_status(int pid, char *program, GPtrArray **emsg);
94 static REMOVE_ITEM *remove_files(REMOVE_ITEM *);
95 static char *uncompress_file(char *, GPtrArray **);
96 static int process_ls_dump(char *, DUMP_ITEM *, int, GPtrArray **);
98 static size_t reply_buffer_size = 1;
99 static char *reply_buffer = NULL;
100 static char *amandad_auth = NULL;
104 static void reply_ptr_array(int, GPtrArray *);
105 static void reply(int, char *, ...) G_GNUC_PRINTF(2, 3);
106 static void lreply(int, char *, ...) G_GNUC_PRINTF(2, 3);
107 static void fast_lreply(int, char *, ...) G_GNUC_PRINTF(2, 3);
108 static am_host_t *is_dump_host_valid(char *);
109 static int is_disk_valid(char *);
110 static int check_and_load_config(char *);
111 static int build_disk_table(void);
112 static int disk_history_list(void);
113 static int is_dir_valid_opaque(char *);
114 static int opaque_ls(char *, int);
115 static void opaque_ls_one (DIR_ITEM *dir_item, am_feature_e marshall_feature,
117 static int tapedev_is(void);
118 static int are_dumps_compressed(void);
119 static char *amindexd_nicedate (char *datestamp);
120 static int cmp_date (const char *date1, const char *date2);
121 static char *get_index_name(char *dump_hostname, char *hostname,
122 char *diskname, char *timestamps, int level);
123 static int get_index_dir(char *dump_hostname, char *hostname, char *diskname);
125 int main(int, char **);
135 amwait_t wait_status;
139 status = waitpid(pid, &wait_status, 0);
142 _("%s (%d) returned negative value: %s"),
143 program, pid, strerror(errno));
144 dbprintf("%s\n", msg);
145 g_ptr_array_add(*emsg, msg);
148 if (!WIFEXITED(wait_status)) {
150 _("%s exited with signal %d"),
151 program, WTERMSIG(wait_status));
152 dbprintf("%s\n", msg);
153 g_ptr_array_add(*emsg, msg);
155 } else if (WEXITSTATUS(wait_status) != 0) {
157 _("%s exited with status %d"),
158 program, WEXITSTATUS(wait_status));
159 dbprintf("%s\n", msg);
160 g_ptr_array_add(*emsg, msg);
174 dbprintf(_("removing index file: %s\n"), remove->filename);
175 unlink(remove->filename);
176 amfree(remove->filename);
178 remove = remove->next;
190 char *filename = NULL;
191 struct stat stat_filename;
198 int uncompress_errfd;
209 GPtrArray *uncompress_err;
211 FILE *uncompress_err_stream;
212 FILE *sort_err_stream;
214 filename = stralloc(filename_gz);
215 len = strlen(filename);
216 if(len > 3 && strcmp(&(filename[len-3]),".gz")==0) {
217 filename[len-3]='\0';
218 } else if(len > 2 && strcmp(&(filename[len-2]),".Z")==0) {
219 filename[len-2]='\0';
222 /* uncompress the file */
223 result=stat(filename,&stat_filename);
224 if(result==-1 && errno==ENOENT) { /* file does not exist */
226 REMOVE_ITEM *remove_file;
229 * Check that compressed file exists manually.
231 if (stat(filename_gz, &statbuf) < 0) {
232 msg = vstrallocf(_("Compressed file '%s' is inaccessable: %s"),
233 filename_gz, strerror(errno));
234 dbprintf("%s\n", msg);
235 g_ptr_array_add(*emsg, msg);
240 #ifdef UNCOMPRESS_OPT
241 # define PARAM_UNCOMPRESS_OPT UNCOMPRESS_OPT
243 # define PARAM_UNCOMPRESS_OPT skip_argument
246 nullfd = open("/dev/null", O_RDONLY);
248 indexfd = open(filename,O_WRONLY|O_CREAT, 0600);
250 msg = vstrallocf(_("Can't open '%s' for writting: %s"),
251 filename, strerror(errno));
252 dbprintf("%s\n", msg);
253 g_ptr_array_add(*emsg, msg);
259 /* just use our stderr directly for the pipe's stderr; in
260 * main() we send stderr to the debug file, or /dev/null
261 * if debugging is disabled */
263 /* start the uncompress process */
264 putenv(stralloc("LC_ALL=C"));
265 pid_gzip = pipespawn(UNCOMPRESS_PATH, STDOUT_PIPE|STDERR_PIPE, 0,
266 &nullfd, &pipe_from_gzip, &uncompress_errfd,
267 UNCOMPRESS_PATH, PARAM_UNCOMPRESS_OPT,
271 pipe_stream = fdopen(pipe_from_gzip,"r");
272 if(pipe_stream == NULL) {
273 msg = vstrallocf(_("Can't fdopen pipe from gzip: %s"),
275 dbprintf("%s\n", msg);
276 g_ptr_array_add(*emsg, msg);
282 /* start the sort process */
283 putenv(stralloc("LC_ALL=C"));
284 pid_sort = pipespawn(SORT_PATH, STDIN_PIPE|STDERR_PIPE, 0,
285 &pipe_to_sort, &indexfd, &sort_errfd,
289 /* start a subprocess */
290 /* send all ouput from uncompress process to sort process */
297 dbprintf("%s\n", msg);
298 g_ptr_array_add(*emsg, msg);
303 while (fgets(line, STR_SIZE, pipe_stream) != NULL) {
304 if (line[0] != '\0') {
305 if (strchr(line,'/')) {
306 full_write(pipe_to_sort,line,strlen(line));
314 aclose(pipe_to_sort);
316 uncompress_err_stream = fdopen(uncompress_errfd, "r");
317 uncompress_err = g_ptr_array_new();
318 while (fgets(line, sizeof(line), uncompress_err_stream) != NULL) {
319 if (line[strlen(line)-1] == '\n')
320 line[strlen(line)-1] = '\0';
321 g_ptr_array_add(uncompress_err, vstrallocf(" %s", line));
322 dbprintf("Uncompress: %s\n", line);
324 fclose(uncompress_err_stream);
326 sort_err_stream = fdopen(sort_errfd, "r");
327 sort_err = g_ptr_array_new();
328 while (fgets(line, sizeof(line), sort_err_stream) != NULL) {
329 if (line[strlen(line)-1] == '\n')
330 line[strlen(line)-1] = '\0';
331 g_ptr_array_add(sort_err, vstrallocf(" %s", line));
332 dbprintf("Sort: %s\n", line);
334 fclose(sort_err_stream);
336 status = get_pid_status(pid_gzip, UNCOMPRESS_PATH, emsg);
337 if (status == 0 && filename) {
341 if (uncompress_err->len > 0) {
342 p_last = uncompress_err->pdata + uncompress_err->len;
343 for (p = uncompress_err->pdata; p < p_last ;p++) {
344 g_ptr_array_add(*emsg, (char *)*p);
347 g_ptr_array_free(uncompress_err, TRUE);
349 status = get_pid_status(pid_index, "index", emsg);
350 if (status == 0 && filename) {
355 status = get_pid_status(pid_sort, SORT_PATH, emsg);
356 if (status == 0 && filename) {
360 if (sort_err->len > 0) {
361 p_last = sort_err->pdata + sort_err->len;
362 for (p = sort_err->pdata; p < p_last ;p++) {
363 g_ptr_array_add(*emsg, (char *)*p);
366 g_ptr_array_free(sort_err, TRUE);
368 /* add at beginning */
370 remove_file = (REMOVE_ITEM *)alloc(SIZEOF(REMOVE_ITEM));
371 remove_file->filename = stralloc(filename);
372 remove_file->next = uncompress_remove;
373 uncompress_remove = remove_file;
376 } else if(!S_ISREG((stat_filename.st_mode))) {
377 msg = vstrallocf(_("\"%s\" is not a regular file"), filename);
378 dbprintf("%s\n", msg);
379 g_ptr_array_add(*emsg, msg);
389 /* find all matching entries in a dump listing */
390 /* return -1 if error */
394 DUMP_ITEM * dump_item,
398 char line[STR_SIZE], old_line[STR_SIZE];
399 char *filename = NULL;
401 char *dir_slash = NULL;
405 size_t len_dir_slash;
408 if (strcmp(dir, "/") == 0) {
409 dir_slash = stralloc(dir);
411 dir_slash = stralloc2(dir, "/");
414 filename_gz = get_index_name(dump_hostname, dump_item->hostname, disk_name,
415 dump_item->date, dump_item->level);
416 if (filename_gz == NULL) {
417 g_ptr_array_add(*emsg, stralloc(_("index file not found")));
421 filename = uncompress_file(filename_gz, emsg);
422 if(filename == NULL) {
429 if((fp = fopen(filename,"r"))==0) {
430 g_ptr_array_add(*emsg, vstrallocf("%s", strerror(errno)));
436 len_dir_slash=strlen(dir_slash);
438 while (fgets(line, STR_SIZE, fp) != NULL) {
439 if (line[0] != '\0') {
440 if(line[strlen(line)-1] == '\n')
441 line[strlen(line)-1] = '\0';
442 if(strncmp(dir_slash, line, len_dir_slash) == 0) {
444 s = line + len_dir_slash;
446 while(ch && ch != '/')
447 ch = *s++;/* find end of the file name */
453 if(strcmp(line, old_line) != 0) {
454 add_dir_list_item(dump_item, line);
455 strcpy(old_line, line);
477 while (p != emsg->pdata + emsg->len -1) {
478 fast_lreply(n, "%s", (char *)*p);
481 reply(n, "%s", (char *)*p);
484 /* send a 1 line reply to the client */
485 printf_arglist_function1(static void reply, int, n, char *, fmt)
491 reply_buffer = alloc(reply_buffer_size);
494 arglist_start(args, fmt);
495 len = g_vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
498 if (len > -1 && (size_t)len < reply_buffer_size-1)
501 reply_buffer_size *= 2;
502 amfree(reply_buffer);
503 reply_buffer = alloc(reply_buffer_size);
506 if (g_fprintf(cmdout,"%03d %s\r\n", n, reply_buffer) < 0)
508 dbprintf(_("! error %d (%s) in printf\n"), errno, strerror(errno));
509 uncompress_remove = remove_files(uncompress_remove);
512 if (fflush(cmdout) != 0)
514 dbprintf(_("! error %d (%s) in fflush\n"), errno, strerror(errno));
515 uncompress_remove = remove_files(uncompress_remove);
518 dbprintf(_("< %03d %s\n"), n, reply_buffer);
521 /* send one line of a multi-line response */
522 printf_arglist_function1(static void lreply, int, n, char *, fmt)
528 reply_buffer = alloc(reply_buffer_size);
531 arglist_start(args, fmt);
532 len = g_vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
535 if (len > -1 && (size_t)len < reply_buffer_size-1)
538 reply_buffer_size *= 2;
539 amfree(reply_buffer);
540 reply_buffer = alloc(reply_buffer_size);
543 if (g_fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0)
545 dbprintf(_("! error %d (%s) in printf\n"),
546 errno, strerror(errno));
547 uncompress_remove = remove_files(uncompress_remove);
550 if (fflush(cmdout) != 0)
552 dbprintf(_("! error %d (%s) in fflush\n"),
553 errno, strerror(errno));
554 uncompress_remove = remove_files(uncompress_remove);
558 dbprintf("< %03d-%s\n", n, reply_buffer);
562 /* send one line of a multi-line response */
563 printf_arglist_function1(static void fast_lreply, int, n, char *, fmt)
569 reply_buffer = alloc(reply_buffer_size);
572 arglist_start(args, fmt);
573 len = g_vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
576 if (len > -1 && (size_t)len < reply_buffer_size-1)
579 reply_buffer_size *= 2;
580 amfree(reply_buffer);
581 reply_buffer = alloc(reply_buffer_size);
584 if (g_fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0)
586 dbprintf(_("! error %d (%s) in printf\n"),
587 errno, strerror(errno));
588 uncompress_remove = remove_files(uncompress_remove);
592 DBG(2, "< %03d-%s", n, reply_buffer);
595 /* see if hostname is valid */
596 /* valid is defined to be that there is an index directory for it */
597 /* also do a security check on the requested dump hostname */
598 /* to restrict access to index records if required */
599 /* return -1 if not okay */
607 if (get_config_name() == NULL) {
608 reply(501, _("Must set config before setting host."));
612 /* check that the config actually handles that host */
613 ihost = lookup_host(host);
615 reply(501, _("Host %s is not in your disklist."), host);
619 /* check if an index dir exist */
620 if(get_index_dir(host, ihost->hostname, NULL)) {
624 /* check if an index dir exist for at least one DLE */
625 for(diskp = ihost->disks; diskp != NULL; diskp = diskp->hostnext) {
626 if (get_index_dir(diskp->hostname, NULL, NULL)) {
631 reply(501, _("No index records for host: %s. Have you enabled indexing?"),
644 if (get_config_name() == NULL) {
645 reply(501, _("Must set config,host before setting disk."));
648 else if (dump_hostname == NULL) {
649 reply(501, _("Must set host before setting disk."));
653 /* check that the config actually handles that disk */
654 idisk = lookup_disk(dump_hostname, disk);
656 qdisk = quote_string(disk);
657 reply(501, _("Disk %s:%s is not in your disklist."), dump_hostname, qdisk);
662 /* assume an index dir already */
663 if (get_index_dir(dump_hostname, idisk->hostname, disk) == 0) {
664 qdisk = quote_string(disk);
665 reply(501, _("No index records for disk: %s. Invalid?"), qdisk);
675 check_and_load_config(
681 struct stat dir_stat;
683 /* check that the config actually exists */
684 if (config == NULL) {
685 reply(501, _("Must set config first."));
689 /* (re-)initialize configuration with the new config name */
690 config_init(CONFIG_INIT_EXPLICIT_NAME, config);
691 if (config_errors(NULL) >= CFGERR_ERRORS) {
692 reply(501, _("Could not read config file for %s!"), config);
696 check_running_as(RUNNING_AS_DUMPUSER_PREFERRED);
698 conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
699 read_diskfile(conf_diskfile, &disk_list);
700 amfree(conf_diskfile);
701 if (config_errors(NULL) >= CFGERR_ERRORS) {
702 reply(501, _("Could not read disk file %s!"), conf_diskfile);
706 conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
707 if(read_tapelist(conf_tapelist)) {
708 reply(501, _("Could not read tapelist file %s!"), conf_tapelist);
709 amfree(conf_tapelist);
712 amfree(conf_tapelist);
714 dbrename(get_config_name(), DBG_SUBDIR_SERVER);
716 output_find = find_dump(&disk_list);
717 /* the 'w' here sorts by write timestamp, so that the first instance of
718 * any particular datestamp/host/disk/level/part that we see is the one
720 sort_find_result("DLKHpwB", &output_find);
722 conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR));
723 if (stat (conf_indexdir, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
724 reply(501, _("Index directory %s does not exist"), conf_indexdir);
725 amfree(conf_indexdir);
728 amfree(conf_indexdir);
735 build_disk_table(void)
738 char *last_timestamp;
742 find_result_t *find_output;
744 if (get_config_name() == NULL) {
745 reply(590, _("Must set config,host,disk before building disk table"));
748 else if (dump_hostname == NULL) {
749 reply(590, _("Must set host,disk before building disk table"));
752 else if (disk_name == NULL) {
753 reply(590, _("Must set disk before building disk table"));
758 last_timestamp = NULL;
759 last_filenum = (off_t)-1;
762 for(find_output = output_find;
764 find_output = find_output->next) {
765 if(strcasecmp(dump_hostname, find_output->hostname) == 0 &&
766 strcmp(disk_name , find_output->diskname) == 0 &&
767 strcmp("OK" , find_output->status) == 0 &&
768 strcmp("OK" , find_output->dump_status) == 0) {
770 * The sort order puts holding disk entries first. We want to
771 * use them if at all possible, so ignore any other entries
772 * for the same datestamp after we see a holding disk entry
773 * (as indicated by a filenum of zero).
776 strcmp(find_output->timestamp, last_timestamp) == 0 &&
777 find_output->level == last_level &&
781 /* ignore duplicate partnum */
783 strcmp(find_output->timestamp, last_timestamp) == 0 &&
784 find_output->level == last_level &&
785 find_output->partnum == last_partnum) {
788 last_timestamp = find_output->timestamp;
789 last_filenum = find_output->filenum;
790 last_level = find_output->level;
791 last_partnum = find_output->partnum;
792 date = amindexd_nicedate(find_output->timestamp);
793 add_dump(find_output->hostname, date, find_output->level,
794 find_output->label, find_output->filenum,
795 find_output->partnum, find_output->totalparts);
796 dbprintf("- %s %d %s %lld %d %d\n",
797 date, find_output->level,
799 (long long)find_output->filenum,
800 find_output->partnum, find_output->totalparts);
811 disk_history_list(void)
816 if (get_config_name() == NULL) {
817 reply(502, _("Must set config,host,disk before listing history"));
820 else if (dump_hostname == NULL) {
821 reply(502, _("Must set host,disk before listing history"));
824 else if (disk_name == NULL) {
825 reply(502, _("Must set disk before listing history"));
829 lreply(200, _(" Dump history for config \"%s\" host \"%s\" disk %s"),
830 get_config_name(), dump_hostname, qdisk_name);
832 for (item=first_dump(); item!=NULL; item=next_dump(item)){
833 char *tapelist_str = marshal_tapelist(item->tapes, 1);
835 strncpy(date, item->date, 20);
837 if(!am_has_feature(their_features,fe_amrecover_timestamp))
840 if(am_has_feature(their_features, fe_amindexd_marshall_in_DHST)){
841 lreply(201, " %s %d %s", date, item->level, tapelist_str);
844 lreply(201, " %s %d %s %lld", date, item->level,
845 tapelist_str, (long long)item->file);
847 amfree(tapelist_str);
850 reply(200, _("Dump history for config \"%s\" host \"%s\" disk %s"),
851 get_config_name(), dump_hostname, qdisk_name);
858 * is the directory dir backed up - dir assumed complete relative to
861 /* opaque version of command */
871 char *filename_gz = NULL;
872 char *filename = NULL;
874 GPtrArray *emsg = NULL;
876 if (get_config_name() == NULL || dump_hostname == NULL || disk_name == NULL) {
877 reply(502, _("Must set config,host,disk before asking about directories"));
880 else if (dump_hostname == NULL) {
881 reply(502, _("Must set host,disk before asking about directories"));
884 else if (disk_name == NULL) {
885 reply(502, _("Must set disk before asking about directories"));
888 else if (target_date == NULL) {
889 reply(502, _("Must set date before asking about directories"));
892 /* scan through till we find first dump on or before date */
893 for (item=first_dump(); item!=NULL; item=next_dump(item))
894 if (cmp_date(item->date, target_date) <= 0)
899 /* no dump for given date */
900 reply(500, _("No dumps available on or before date \"%s\""), target_date);
904 if(strcmp(dir, "/") == 0) {
905 ldir = stralloc(dir);
907 ldir = stralloc2(dir, "/");
909 ldir_len = strlen(ldir);
911 /* go back till we hit a level 0 dump */
915 filename_gz = get_index_name(dump_hostname, item->hostname, disk_name,
916 item->date, item->level);
917 if (filename_gz == NULL) {
918 reply(599, "index not found");
922 emsg = g_ptr_array_new();
923 if((filename = uncompress_file(filename_gz, &emsg)) == NULL) {
924 reply_ptr_array(599, emsg);
926 g_ptr_array_free_full(emsg);
930 g_ptr_array_free_full(emsg);
932 dbprintf("f %s\n", filename);
933 if ((fp = fopen(filename, "r")) == NULL) {
934 reply(599, _("System error: %s"), strerror(errno));
939 while (fgets(line, STR_SIZE, fp) != NULL) {
942 if(line[strlen(line)-1] == '\n')
943 line[strlen(line)-1] = '\0';
944 if (strncmp(line, ldir, ldir_len) != 0) {
945 continue; /* not found yet */
954 last_level = item->level;
957 item=next_dump(item);
958 } while ((item != NULL) && (item->level >= last_level));
959 } while (item != NULL);
963 reply(500, _("\"%s\" is an invalid directory"), dir);
972 DUMP_ITEM *dump_item;
974 int level, last_level;
975 GPtrArray *emsg = NULL;
976 am_feature_e marshall_feature;
979 marshall_feature = fe_amindexd_marshall_in_ORLD;
981 marshall_feature = fe_amindexd_marshall_in_OLSD;
986 if (get_config_name() == NULL) {
987 reply(502, _("Must set config,host,disk before listing a directory"));
990 else if (dump_hostname == NULL) {
991 reply(502, _("Must set host,disk before listing a directory"));
994 else if (disk_name == NULL) {
995 reply(502, _("Must set disk before listing a directory"));
998 else if (target_date == NULL) {
999 reply(502, _("Must set date before listing a directory"));
1003 /* scan through till we find first dump on or before date */
1004 for (dump_item=first_dump(); dump_item!=NULL; dump_item=next_dump(dump_item))
1005 if (cmp_date(dump_item->date, target_date) <= 0)
1008 if (dump_item == NULL)
1010 /* no dump for given date */
1011 reply(500, _("No dumps available on or before date \"%s\""), target_date);
1015 /* get data from that dump */
1016 emsg = g_ptr_array_new();
1017 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
1018 reply_ptr_array(599, emsg);
1019 g_ptr_array_free_full(emsg);
1023 /* go back processing higher level dumps till we hit a level 0 dump */
1024 last_level = dump_item->level;
1025 while ((last_level != 0) && ((dump_item=next_dump(dump_item)) != NULL))
1027 if (dump_item->level < last_level)
1029 last_level = dump_item->level;
1030 if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
1031 reply_ptr_array(599, emsg);
1032 g_ptr_array_free_full(emsg);
1037 g_ptr_array_free_full(emsg);
1039 /* return the information to the caller */
1040 lreply(200, _(" Opaque list of %s"), dir);
1041 for(level=0; level < DUMP_LEVELS; level++) {
1042 for (dir_item = get_dir_list(); dir_item != NULL;
1043 dir_item = dir_item->next) {
1045 if(dir_item->dump->level == level) {
1046 if (!am_has_feature(their_features, marshall_feature) &&
1047 (num_entries(dir_item->dump->tapes) > 1 ||
1048 dir_item->dump->tapes->numfiles > 1)) {
1049 fast_lreply(501, _(" ERROR: Split dumps not supported"
1050 " with old version of amrecover."));
1054 opaque_ls_one(dir_item, marshall_feature, recursive);
1059 reply(200, _(" Opaque list of %s"), dir);
1066 DIR_ITEM * dir_item,
1067 am_feature_e marshall_feature,
1074 if (am_has_feature(their_features, marshall_feature)) {
1075 tapelist_str = marshal_tapelist(dir_item->dump->tapes, 1);
1077 tapelist_str = dir_item->dump->tapes->label;
1080 strncpy(date, dir_item->dump->date, 20);
1082 if(!am_has_feature(their_features,fe_amrecover_timestamp))
1085 qpath = quote_string(dir_item->path);
1086 if((!recursive && am_has_feature(their_features,
1087 fe_amindexd_fileno_in_OLSD)) ||
1088 (recursive && am_has_feature(their_features,
1089 fe_amindexd_fileno_in_ORLD))) {
1090 fast_lreply(201, " %s %d %s %lld %s",
1092 dir_item->dump->level,
1094 (long long)dir_item->dump->file,
1099 fast_lreply(201, " %s %d %s %s",
1100 date, dir_item->dump->level,
1101 tapelist_str, qpath);
1104 if(am_has_feature(their_features, marshall_feature)) {
1105 amfree(tapelist_str);
1110 * returns the value of changer or tapedev from the amanda.conf file if set,
1111 * otherwise reports an error
1119 /* check state okay to do this */
1120 if (get_config_name() == NULL) {
1121 reply(501, _("Must set config before asking about tapedev."));
1125 /* use amrecover_changer if possible */
1126 if ((result = getconf_str(CNF_AMRECOVER_CHANGER)) != NULL &&
1128 dbprintf(_("tapedev_is amrecover_changer: %s\n"), result);
1129 reply(200, "%s", result);
1133 /* use changer if possible */
1134 if ((result = getconf_str(CNF_TPCHANGER)) != NULL && *result != '\0') {
1135 dbprintf(_("tapedev_is tpchanger: %s\n"), result);
1136 reply(200, "%s", result);
1140 /* get tapedev value */
1141 if ((result = getconf_str(CNF_TAPEDEV)) != NULL && *result != '\0') {
1142 dbprintf(_("tapedev_is tapedev: %s\n"), result);
1143 reply(200, "%s", result);
1147 dbprintf(_("No tapedev or tpchanger in config site.\n"));
1149 reply(501, _("Tapedev or tpchanger not set in config file."));
1154 /* returns YES if dumps for disk are compressed, NO if not */
1156 are_dumps_compressed(void)
1160 /* check state okay to do this */
1161 if (get_config_name() == NULL) {
1162 reply(501, _("Must set config,host,disk name before asking about dumps."));
1165 else if (dump_hostname == NULL) {
1166 reply(501, _("Must set host,disk name before asking about dumps."));
1169 else if (disk_name == NULL) {
1170 reply(501, _("Must set disk name before asking about dumps."));
1174 /* now go through the list of disks and find which have indexes */
1175 for (diskp = disk_list.head; diskp != NULL; diskp = diskp->next) {
1176 if ((strcasecmp(diskp->host->hostname, dump_hostname) == 0)
1177 && (strcmp(diskp->name, disk_name) == 0)) {
1182 if (diskp == NULL) {
1183 reply(501, _("Couldn't find host/disk in disk file."));
1187 /* send data to caller */
1188 if (diskp->compress == COMP_NONE)
1201 char *line = NULL, *part = NULL;
1204 char *cmd_undo, cmd_undo_ch;
1205 socklen_t_equiv socklen;
1206 sockaddr_union his_addr;
1210 int user_validated = 0;
1211 char *errstr = NULL;
1212 char *pgm = "amindexd"; /* in case argv[0] is not set */
1213 char his_hostname[MAX_HOSTNAME_LENGTH];
1214 char *cfg_opt = NULL;
1217 * Configure program for internationalization:
1218 * 1) Only set the message locale for now.
1219 * 2) Set textdomain for all amanda related programs to "amanda"
1220 * We don't want to be forced to support dozens of message catalogs.
1222 setlocale(LC_MESSAGES, "C");
1223 textdomain("amanda");
1225 safe_fd(DATA_FD_OFFSET, 2);
1226 openbsd_fd_inform();
1230 * When called via inetd, it is not uncommon to forget to put the
1231 * argv[0] value on the config line. On some systems (e.g. Solaris)
1232 * this causes argv and/or argv[0] to be NULL, so we have to be
1233 * careful getting our name.
1235 if (argc >= 1 && argv != NULL && argv[0] != NULL) {
1236 if((pgm = strrchr(argv[0], '/')) != NULL) {
1245 /* Don't die when child closes pipe */
1246 signal(SIGPIPE, SIG_IGN);
1248 dbopen(DBG_SUBDIR_SERVER);
1249 dbprintf(_("version %s\n"), VERSION);
1252 error("argv == NULL\n");
1255 if (! (argc >= 1 && argv[0] != NULL)) {
1256 dbprintf(_("WARNING: argv[0] not defined: check inetd.conf\n"));
1259 debug_dup_stderr_to_debug();
1266 if(argc > 0 && strcmp(*argv, "-t") == 0) {
1272 if(argc > 0 && strcmp(*argv, "amandad") == 0) {
1277 amandad_auth = *argv;
1293 if(gethostname(local_hostname, SIZEOF(local_hostname)-1) == -1) {
1294 error(_("gethostname: %s"), strerror(errno));
1297 local_hostname[SIZEOF(local_hostname)-1] = '\0';
1299 /* now trim domain off name */
1302 while(ch && ch != '.') ch = *s++;
1306 if(from_amandad == 0) {
1307 if(!amindexd_debug) {
1308 /* who are we talking to? */
1309 socklen = sizeof (his_addr);
1310 if (getpeername(0, (struct sockaddr *)&his_addr, &socklen) == -1)
1311 error(_("getpeername: %s"), strerror(errno));
1313 /* Try a reverse (IP->hostname) resolution, and fail if it does
1314 * not work -- this is a basic security check */
1315 if (getnameinfo((struct sockaddr *)&his_addr, SS_LEN(&his_addr),
1316 his_hostname, sizeof(his_hostname),
1319 error(_("getnameinfo(%s): hostname lookup failed"),
1320 str_sockaddr(&his_addr));
1325 /* Set up the input and output FILEs */
1330 /* read the REQ packet */
1331 for(; (line = agets(stdin)) != NULL; free(line)) {
1332 if(strncmp_const(line, "OPTIONS ") == 0) {
1333 if (g_options != NULL) {
1334 dbprintf(_("REQ packet specified multiple OPTIONS.\n"));
1335 free_g_options(g_options);
1337 g_options = parse_g_options(line+8, 1);
1338 if(!g_options->hostname) {
1339 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
1340 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
1341 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
1347 if(amandad_auth && g_options->auth) {
1348 if(strcasecmp(amandad_auth, g_options->auth) != 0) {
1349 g_printf(_("ERROR recover program ask for auth=%s while amindexd is configured for '%s'\n"),
1350 g_options->auth, amandad_auth);
1351 error(_("amindexd: ERROR recover program ask for auth=%s while amindexd is configured for '%s'"),
1352 g_options->auth, amandad_auth);
1356 /* send the REP packet */
1357 g_printf("CONNECT MESG %d\n", DATA_FD_OFFSET);
1364 cmdout = fdopen(DATA_FD_OFFSET + 0, "a");
1366 error(_("amindexd: Can't fdopen(%d): %s"), DATA_FD_OFFSET + 0, strerror(errno));
1370 cmdin = fdopen(DATA_FD_OFFSET + 1, "r");
1372 error(_("amindexd: Can't fdopen(%d): %s"), DATA_FD_OFFSET + 1, strerror(errno));
1377 /* clear these so we can detect when the have not been set by the client */
1378 amfree(dump_hostname);
1381 amfree(target_date);
1383 our_features = am_init_feature_set();
1384 their_features = am_set_default_feature_set();
1386 if (cfg_opt != NULL && check_and_load_config(cfg_opt) != -1) { /* load the config */
1390 reply(220, _("%s AMANDA index server (%s) ready."), local_hostname,
1393 user_validated = from_amandad;
1395 /* a real simple parser since there are only a few commands */
1398 /* get a line from the client */
1400 if((part = agets(cmdin)) == NULL) {
1402 dbprintf(_("? read error: %s\n"), strerror(errno));
1404 dbprintf(_("? unexpected EOF\n"));
1407 dbprintf(_("? unprocessed input:\n"));
1408 dbprintf("-----\n");
1409 dbprintf("? %s\n", line);
1410 dbprintf("-----\n");
1414 uncompress_remove = remove_files(uncompress_remove);
1416 return 1; /* they hung up? */
1418 strappend(line, part); /* Macro: line can be null */
1421 if(amindexd_debug) {
1422 break; /* we have a whole line */
1424 if((len = strlen(line)) > 0 && line[len-1] == '\r') {
1425 line[len-1] = '\0'; /* zap the '\r' */
1429 * Hmmm. We got a "line" from agets(), which means it saw
1430 * a '\n' (or EOF, etc), but there was not a '\r' before it.
1431 * Put a '\n' back in the buffer and loop for more.
1433 strappend(line, "\n");
1436 dbprintf("> %s\n", line);
1443 skip_whitespace(s, ch);
1445 reply(500, _("Command not recognised/incorrect: %s"), line);
1451 skip_non_whitespace(s, ch);
1452 cmd_undo = s-1; /* for error message */
1453 cmd_undo_ch = *cmd_undo;
1456 skip_whitespace(s, ch); /* find the argument */
1459 skip_quoted_string(s, ch);
1460 arg = unquote_string(arg);
1465 if (!user_validated && strcmp(cmd, "SECURITY") == 0 && arg) {
1466 user_validated = amindexd_debug ||
1468 (sockaddr_union *)&his_addr,
1470 if(user_validated) {
1471 reply(200, _("Access OK"));
1476 if (!user_validated) { /* don't tell client the reason, just log it to debug log */
1477 reply(500, _("Access not allowed"));
1479 dbprintf("%s\n", errstr);
1484 if (strcmp(cmd, "QUIT") == 0) {
1487 } else if (strcmp(cmd, "HOST") == 0 && arg) {
1489 /* set host we are restoring */
1491 if ((lhost = is_dump_host_valid(arg)) != NULL)
1493 dump_hostname = newstralloc(dump_hostname, lhost->hostname);
1494 reply(200, _("Dump host set to %s."), dump_hostname);
1495 amfree(qdisk_name); /* invalidate any value */
1496 amfree(disk_name); /* invalidate any value */
1499 } else if (strcmp(cmd, "LISTHOST") == 0) {
1505 if (get_config_name() == NULL) {
1506 reply(501, _("Must set config before listhost"));
1509 lreply(200, _(" List hosts for config %s"), get_config_name());
1510 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1512 for (diskdup = disk_list.head; diskdup!=disk; diskdup = diskdup->next) {
1513 if(strcmp(diskdup->host->hostname, disk->host->hostname) == 0) {
1519 fast_lreply(201, " %s", disk->host->hostname);
1524 reply(200, _(" List hosts for config %s"), get_config_name());
1527 reply(200, _("No hosts for config %s"), get_config_name());
1531 } else if (strcmp(cmd, "DISK") == 0 && arg) {
1533 if (is_disk_valid(arg) != -1) {
1534 disk_name = newstralloc(disk_name, arg);
1535 qdisk_name = quote_string(disk_name);
1536 if (build_disk_table() != -1) {
1537 reply(200, _("Disk set to %s."), qdisk_name);
1541 } else if (strcmp(cmd, "DLE") == 0) {
1547 dp = lookup_disk(dump_hostname, disk_name);
1548 if (dp->line == 0) {
1549 reply(200, "NODLE");
1551 GPtrArray *errarray;
1554 b64disk = amxml_format_tag("disk", dp->name);
1555 dp->host->features = their_features;
1556 errarray = validate_optionstr(dp);
1557 if (errarray->len > 0) {
1558 for (i=0; i < errarray->len; i++) {
1559 g_debug(_("ERROR: %s:%s %s"),
1560 dump_hostname, disk_name,
1561 (char *)g_ptr_array_index(errarray, i));
1563 g_ptr_array_free(errarray, TRUE);
1564 reply(200, "NODLE");
1566 optionstr = xml_optionstr(dp, 0);
1567 l = vstralloc("<dle>\n",
1568 " <program>", dp->program, "</program>\n", NULL);
1569 if (dp->application) {
1570 application_t *application;
1573 application = lookup_application(dp->application);
1574 g_assert(application != NULL);
1575 xml_app = xml_application(dp, application,
1577 vstrextend(&l, xml_app, NULL);
1580 vstrextend(&l, " ", b64disk, "\n", NULL);
1582 char *b64device = amxml_format_tag("diskdevice",
1584 vstrextend(&l, " ", b64device, "\n", NULL);
1587 vstrextend(&l, optionstr, "</dle>\n", NULL);
1588 ql = quote_string(l);
1589 reply(200, "%s", ql);
1596 } else if (strcmp(cmd, "LISTDISK") == 0) {
1601 if (get_config_name() == NULL) {
1602 reply(501, _("Must set config, host before listdisk"));
1604 else if (dump_hostname == NULL) {
1605 reply(501, _("Must set host before listdisk"));
1608 lreply(200, _(" List of disk for device %s on host %s"), arg,
1610 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1612 if (strcasecmp(disk->host->hostname, dump_hostname) == 0 &&
1613 ((disk->device && strcmp(disk->device, arg) == 0) ||
1614 (!disk->device && strcmp(disk->name, arg) == 0))) {
1615 qname = quote_string(disk->name);
1616 fast_lreply(201, " %s", qname);
1622 reply(200, _("List of disk for device %s on host %s"), arg,
1626 reply(200, _("No disk for device %s on host %s"), arg,
1631 lreply(200, _(" List of disk for host %s"), dump_hostname);
1632 for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1633 if(strcasecmp(disk->host->hostname, dump_hostname) == 0) {
1634 qname = quote_string(disk->name);
1635 fast_lreply(201, " %s", qname);
1641 reply(200, _("List of disk for host %s"), dump_hostname);
1644 reply(200, _("No disk for host %s"), dump_hostname);
1648 } else if (strcmp(cmd, "SCNF") == 0 && arg) {
1650 if (check_and_load_config(arg) != -1) { /* try to load the new config */
1651 amfree(dump_hostname); /* invalidate any value */
1652 amfree(qdisk_name); /* invalidate any value */
1653 amfree(disk_name); /* invalidate any value */
1654 reply(200, _("Config set to %s."), get_config_name());
1655 } /* check_and_load_config replies with any failure messages */
1657 } else if (strcmp(cmd, "FEATURES") == 0 && arg) {
1658 char *our_feature_string = NULL;
1659 char *their_feature_string = NULL;
1661 am_release_feature_set(our_features);
1662 am_release_feature_set(their_features);
1663 our_features = am_init_feature_set();
1664 our_feature_string = am_feature_to_string(our_features);
1665 their_feature_string = newstralloc(their_feature_string, arg);
1666 their_features = am_string_to_feature(their_feature_string);
1667 if (!their_features) {
1668 g_warning("Invalid client feature set '%s'", their_feature_string);
1669 their_features = am_set_default_feature_set();
1671 reply(200, "FEATURES %s", our_feature_string);
1672 amfree(our_feature_string);
1673 amfree(their_feature_string);
1675 } else if (strcmp(cmd, "DATE") == 0 && arg) {
1677 target_date = newstralloc(target_date, arg);
1678 reply(200, _("Working date set to %s."), target_date);
1680 } else if (strcmp(cmd, "DHST") == 0) {
1681 (void)disk_history_list();
1682 } else if (strcmp(cmd, "OISD") == 0 && arg) {
1683 if (is_dir_valid_opaque(arg) != -1) {
1684 reply(200, _("\"%s\" is a valid directory"), arg);
1686 } else if (strcmp(cmd, "OLSD") == 0 && arg) {
1687 (void)opaque_ls(arg,0);
1688 } else if (strcmp(cmd, "ORLD") == 0 && arg) {
1689 (void)opaque_ls(arg, 1);
1690 } else if (strcmp(cmd, "TAPE") == 0) {
1692 } else if (strcmp(cmd, "DCMP") == 0) {
1693 (void)are_dumps_compressed();
1695 *cmd_undo = cmd_undo_ch; /* restore the command line */
1696 reply(500, _("Command not recognised/incorrect: %s"), cmd);
1702 uncompress_remove = remove_files(uncompress_remove);
1703 free_find_result(&output_find);
1704 reply(200, _("Good bye."));
1713 static char nice[20];
1714 int year, month, day;
1715 int hours, minutes, seconds;
1716 char date[9], atime[7];
1717 int numdate, numtime;
1719 strncpy(date, datestamp, 8);
1721 numdate = atoi(date);
1722 year = numdate / 10000;
1723 month = (numdate / 100) % 100;
1724 day = numdate % 100;
1726 if(strlen(datestamp) <= 8) {
1727 g_snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d",
1731 strncpy(atime, &(datestamp[8]), 6);
1733 numtime = atoi(atime);
1734 hours = numtime / 10000;
1735 minutes = (numtime / 100) % 100;
1736 seconds = numtime % 100;
1738 g_snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d-%02d-%02d-%02d",
1739 year, month, day, hours, minutes, seconds);
1750 return strncmp(date1, date2, strlen(date2));
1755 char *dump_hostname,
1759 struct stat dir_stat;
1762 char *lower_hostname;
1764 lower_hostname = stralloc(dump_hostname);
1765 for(s=lower_hostname; *s != '\0'; s++)
1768 fn = getindexfname(dump_hostname, diskname, NULL, 0);
1769 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1770 amfree(lower_hostname);
1775 if (hostname != NULL) {
1776 fn = getindexfname(hostname, diskname, NULL, 0);
1777 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1778 amfree(lower_hostname);
1784 fn = getindexfname(lower_hostname, diskname, NULL, 0);
1785 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1786 amfree(lower_hostname);
1791 if(diskname != NULL) {
1792 fn = getoldindexfname(dump_hostname, diskname, NULL, 0);
1793 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1794 amfree(lower_hostname);
1799 if (hostname != NULL) {
1800 fn = getoldindexfname(hostname, diskname, NULL, 0);
1801 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1802 amfree(lower_hostname);
1808 fn = getoldindexfname(lower_hostname, diskname, NULL, 0);
1809 if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1810 amfree(lower_hostname);
1816 amfree(lower_hostname);
1822 char *dump_hostname,
1828 struct stat dir_stat;
1831 char *lower_hostname;
1833 lower_hostname = stralloc(dump_hostname);
1834 for(s=lower_hostname; *s != '\0'; s++)
1837 fn = getindexfname(dump_hostname, diskname, timestamps, level);
1838 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1839 amfree(lower_hostname);
1843 if(hostname != NULL) {
1844 fn = getindexfname(hostname, diskname, timestamps, level);
1845 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1846 amfree(lower_hostname);
1851 fn = getindexfname(lower_hostname, diskname, timestamps, level);
1852 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1853 amfree(lower_hostname);
1857 if(diskname != NULL) {
1858 fn = getoldindexfname(dump_hostname, diskname, timestamps, level);
1859 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1860 amfree(lower_hostname);
1864 if(hostname != NULL) {
1865 fn = getoldindexfname(hostname, diskname, timestamps, level);
1866 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1867 amfree(lower_hostname);
1872 fn = getoldindexfname(lower_hostname, diskname, timestamps, level);
1873 if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1874 amfree(lower_hostname);
1878 amfree(lower_hostname);