1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * Copyright (c) 2007-2013 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27 /*
28  * $Id: amindexd.c,v 1.106 2006/07/25 18:27:57 martinea Exp $
29  *
30  * This is the server daemon part of the index client/server system.
31  * It is assumed that this is launched from inetd instead of being
32  * started as a daemon because it is not often used
33  */
34 
35 /*
36 ** Notes:
37 ** - this server will do very little until it knows what Amanda config it
38 **   is to use.  Setting the config has the side effect of changing to the
39 **   index directory.
40 ** - XXX - I'm pretty sure the config directory name should have '/'s stripped
41 **   from it.  It is given to us by an unknown person via the network.
42 */
43 
44 #include "amanda.h"
45 #include "conffile.h"
46 #include "diskfile.h"
47 #include "arglist.h"
48 #include "clock.h"
49 #include "match.h"
50 #include "amindex.h"
51 #include "disk_history.h"
52 #include "list_dir.h"
53 #include "logfile.h"
54 #include "find.h"
55 #include "tapefile.h"
56 #include "util.h"
57 #include "amandad.h"
58 #include "pipespawn.h"
59 #include "sockaddr-util.h"
60 #include "amxml.h"
61 
62 #include <grp.h>
63 
64 #define DBG(i, ...) do {		\
65 	if ((i) <= debug_amindexd) {	\
66 	    g_debug(__VA_ARGS__);	\
67 	}				\
68 } while (0)
69 
70 typedef struct REMOVE_ITEM
71 {
72     char *filename;
73     struct REMOVE_ITEM *next;
74 } REMOVE_ITEM;
75 
76 /* state */
77 static int from_amandad;
78 static char local_hostname[MAX_HOSTNAME_LENGTH+1];	/* me! */
79 static char *dump_hostname = NULL;		/* machine we are restoring */
80 static char *disk_name;				/* disk we are restoring */
81 char *qdisk_name = NULL;			/* disk we are restoring */
82 static char *target_date = NULL;
83 static disklist_t disk_list;			/* all disks in cur config */
84 static find_result_t *output_find = NULL;
85 static g_option_t *g_options = NULL;
86 
87 static int amindexd_debug = 0;
88 
89 static REMOVE_ITEM *uncompress_remove = NULL;
90 					/* uncompressed files to remove */
91 
92 static am_feature_t *our_features = NULL;
93 static am_feature_t *their_features = NULL;
94 
95 static int get_pid_status(int pid, char *program, GPtrArray **emsg);
96 static REMOVE_ITEM *remove_files(REMOVE_ITEM *);
97 static char *uncompress_file(char *, GPtrArray **);
98 static int process_ls_dump(char *, DUMP_ITEM *, int, GPtrArray **);
99 
100 static size_t reply_buffer_size = 1;
101 static char *reply_buffer = NULL;
102 static char *amandad_auth = NULL;
103 static FILE *cmdin;
104 static FILE *cmdout;
105 
106 static void reply_ptr_array(int, GPtrArray *);
107 static void reply(int, char *, ...) G_GNUC_PRINTF(2, 3);
108 static void lreply(int, char *, ...) G_GNUC_PRINTF(2, 3);
109 static void fast_lreply(int, char *, ...) G_GNUC_PRINTF(2, 3);
110 static am_host_t *is_dump_host_valid(char *);
111 static int is_disk_valid(char *);
112 static int check_and_load_config(char *);
113 static int build_disk_table(void);
114 static int disk_history_list(void);
115 static int is_dir_valid_opaque(char *);
116 static int opaque_ls(char *, int);
117 static void opaque_ls_one (DIR_ITEM *dir_item, am_feature_e marshall_feature,
118 			     int recursive);
119 static int tapedev_is(void);
120 static int are_dumps_compressed(void);
121 static char *amindexd_nicedate (char *datestamp);
122 static int cmp_date (const char *date1, const char *date2);
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);
126 
127 int main(int, char **);
128 
129 
130 static int
get_pid_status(int pid,char * program,GPtrArray ** emsg)131 get_pid_status(
132     int         pid,
133     char       *program,
134     GPtrArray **emsg)
135 {
136     int       status;
137     amwait_t  wait_status;
138     char     *msg;
139     int       result = 1;
140 
141     status = waitpid(pid, &wait_status, 0);
142     if (status < 0) {
143 	msg = vstrallocf(
144 		_("%s (%d) returned negative value: %s"),
145 		program, pid, strerror(errno));
146 	dbprintf("%s\n", msg);
147 	g_ptr_array_add(*emsg, msg);
148 	result = 0;
149     } else {
150 	if (!WIFEXITED(wait_status)) {
151 	    msg = vstrallocf(
152 			_("%s exited with signal %d"),
153 			program, WTERMSIG(wait_status));
154 	    dbprintf("%s\n", msg);
155 	    g_ptr_array_add(*emsg, msg);
156 	    result = 0;
157 	} else if (WEXITSTATUS(wait_status) != 0) {
158 	    msg = vstrallocf(
159 			_("%s exited with status %d"),
160 			program, WEXITSTATUS(wait_status));
161 	    dbprintf("%s\n", msg);
162 	    g_ptr_array_add(*emsg, msg);
163 	    result = 0;
164 	}
165     }
166     return result;
167 }
168 
169 static REMOVE_ITEM *
remove_files(REMOVE_ITEM * remove)170 remove_files(
171     REMOVE_ITEM *remove)
172 {
173     REMOVE_ITEM *prev;
174 
175     while(remove) {
176 	dbprintf(_("removing index file: %s\n"), remove->filename);
177 	unlink(remove->filename);
178 	amfree(remove->filename);
179 	prev = remove;
180 	remove = remove->next;
181 	amfree(prev);
182     }
183     return remove;
184 }
185 
186 static char *
uncompress_file(char * filename_gz,GPtrArray ** emsg)187 uncompress_file(
188     char       *filename_gz,
189     GPtrArray **emsg)
190 {
191     char *cmd = NULL;
192     char *filename = NULL;
193     struct stat stat_filename;
194     int result;
195     size_t len;
196     int pipe_from_gzip;
197     int pipe_to_sort;
198     int indexfd;
199     int nullfd;
200     int uncompress_errfd;
201     int sort_errfd;
202     char line[STR_SIZE];
203     FILE *pipe_stream;
204     pid_t pid_gzip;
205     pid_t pid_sort;
206     pid_t pid_index;
207     int        status;
208     char      *msg;
209     gpointer  *p;
210     gpointer  *p_last;
211     GPtrArray *uncompress_err;
212     GPtrArray *sort_err;
213     FILE      *uncompress_err_stream;
214     FILE      *sort_err_stream;
215 
216     filename = stralloc(filename_gz);
217     len = strlen(filename);
218     if(len > 3 && strcmp(&(filename[len-3]),".gz")==0) {
219 	filename[len-3]='\0';
220     } else if(len > 2 && strcmp(&(filename[len-2]),".Z")==0) {
221 	filename[len-2]='\0';
222     }
223 
224     /* uncompress the file */
225     result=stat(filename,&stat_filename);
226     if(result==-1 && errno==ENOENT) {		/* file does not exist */
227 	struct stat statbuf;
228 	REMOVE_ITEM *remove_file;
229 
230 	/*
231 	 * Check that compressed file exists manually.
232 	 */
233 	if (stat(filename_gz, &statbuf) < 0) {
234 	    msg = vstrallocf(_("Compressed file '%s' is inaccessable: %s"),
235 			     filename_gz, strerror(errno));
236 	    dbprintf("%s\n", msg);
237 	    g_ptr_array_add(*emsg, msg);
238 	    amfree(filename);
239 	    return NULL;
240  	}
241 
242 #ifdef UNCOMPRESS_OPT
243 #  define PARAM_UNCOMPRESS_OPT UNCOMPRESS_OPT
244 #else
245 #  define PARAM_UNCOMPRESS_OPT skip_argument
246 #endif
247 
248 	nullfd = open("/dev/null", O_RDONLY);
249 
250 	indexfd = open(filename,O_WRONLY|O_CREAT, 0600);
251 	if (indexfd == -1) {
252 	    msg = vstrallocf(_("Can't open '%s' for writing: %s"),
253 			      filename, strerror(errno));
254 	    dbprintf("%s\n", msg);
255 	    g_ptr_array_add(*emsg, msg);
256 	    amfree(filename);
257 	    aclose(nullfd);
258 	    return NULL;
259 	}
260 
261 	/* just use our stderr directly for the pipe's stderr; in
262 	 * main() we send stderr to the debug file, or /dev/null
263 	 * if debugging is disabled */
264 
265 	/* start the uncompress process */
266 	putenv(stralloc("LC_ALL=C"));
267 	pid_gzip = pipespawn(UNCOMPRESS_PATH, STDOUT_PIPE|STDERR_PIPE, 0,
268 			     &nullfd, &pipe_from_gzip, &uncompress_errfd,
269 			     UNCOMPRESS_PATH, PARAM_UNCOMPRESS_OPT,
270 			     filename_gz, NULL);
271 	aclose(nullfd);
272 
273 	pipe_stream = fdopen(pipe_from_gzip,"r");
274 	if(pipe_stream == NULL) {
275 	    msg = vstrallocf(_("Can't fdopen pipe from gzip: %s"),
276 			     strerror(errno));
277 	    dbprintf("%s\n", msg);
278 	    g_ptr_array_add(*emsg, msg);
279 	    amfree(filename);
280 	    aclose(indexfd);
281 	    return NULL;
282 	}
283 
284 	/* start the sort process */
285 	putenv(stralloc("LC_ALL=C"));
286 	if (getconf_seen(CNF_TMPDIR)) {
287 	    gchar *tmpdir = getconf_str(CNF_TMPDIR);
288 	    pid_sort = pipespawn(SORT_PATH, STDIN_PIPE|STDERR_PIPE, 0,
289 				 &pipe_to_sort, &indexfd, &sort_errfd,
290 				 SORT_PATH, "-T", tmpdir, NULL);
291 	} else {
292 	    pid_sort = pipespawn(SORT_PATH, STDIN_PIPE|STDERR_PIPE, 0,
293 				 &pipe_to_sort, &indexfd, &sort_errfd,
294 				 SORT_PATH, NULL);
295 	}
296 	aclose(indexfd);
297 
298 	/* start a subprocess */
299 	/* send all ouput from uncompress process to sort process */
300 	pid_index = fork();
301 	switch (pid_index) {
302 	case -1:
303 	    msg = vstrallocf(
304 			_("fork error: %s"),
305 			strerror(errno));
306 	    dbprintf("%s\n", msg);
307 	    g_ptr_array_add(*emsg, msg);
308 	    unlink(filename);
309 	    amfree(filename);
310 	default: break;
311 	case 0:
312 	    while (fgets(line, STR_SIZE, pipe_stream) != NULL) {
313 		if (line[0] != '\0') {
314 		    if (strchr(line,'/')) {
315 			full_write(pipe_to_sort,line,strlen(line));
316 		    }
317 		}
318 	    }
319 	    exit(0);
320 	}
321 
322 	fclose(pipe_stream);
323 	aclose(pipe_to_sort);
324 
325 	uncompress_err_stream = fdopen(uncompress_errfd, "r");
326 	uncompress_err = g_ptr_array_new();
327 	while (fgets(line, sizeof(line), uncompress_err_stream) != NULL) {
328 	    if (line[strlen(line)-1] == '\n')
329 		line[strlen(line)-1] = '\0';
330 	    g_ptr_array_add(uncompress_err, vstrallocf("  %s", line));
331 	    dbprintf("Uncompress: %s\n", line);
332 	}
333 	fclose(uncompress_err_stream);
334 
335 	sort_err_stream = fdopen(sort_errfd, "r");
336 	sort_err = g_ptr_array_new();
337 	while (fgets(line, sizeof(line), sort_err_stream) != NULL) {
338 	    if (line[strlen(line)-1] == '\n')
339 		line[strlen(line)-1] = '\0';
340 	    g_ptr_array_add(sort_err, vstrallocf("  %s", line));
341 	    dbprintf("Sort: %s\n", line);
342 	}
343 	fclose(sort_err_stream);
344 
345 	status = get_pid_status(pid_gzip, UNCOMPRESS_PATH, emsg);
346 	if (status == 0 && filename) {
347 	    unlink(filename);
348 	    amfree(filename);
349 	}
350 	if (uncompress_err->len > 0) {
351 	    p_last = uncompress_err->pdata + uncompress_err->len;
352 	    for (p = uncompress_err->pdata; p < p_last ;p++) {
353 		g_ptr_array_add(*emsg, (char *)*p);
354 	    }
355 	}
356 	g_ptr_array_free(uncompress_err, TRUE);
357 
358 	status = get_pid_status(pid_index, "index", emsg);
359 	if (status == 0 && filename) {
360 	    unlink(filename);
361 	    amfree(filename);
362 	}
363 
364 	status = get_pid_status(pid_sort, SORT_PATH, emsg);
365 	if (status == 0 && filename) {
366 	    unlink(filename);
367 	    amfree(filename);
368 	}
369 	if (sort_err->len > 0) {
370 	    p_last = sort_err->pdata + sort_err->len;
371 	    for (p = sort_err->pdata; p < p_last ;p++) {
372 		g_ptr_array_add(*emsg, (char *)*p);
373 	    }
374 	}
375 	g_ptr_array_free(sort_err, TRUE);
376 
377 	/* add at beginning */
378 	if (filename) {
379 	    remove_file = (REMOVE_ITEM *)alloc(SIZEOF(REMOVE_ITEM));
380 	    remove_file->filename = stralloc(filename);
381 	    remove_file->next = uncompress_remove;
382 	    uncompress_remove = remove_file;
383 	}
384 
385     } else if(!S_ISREG((stat_filename.st_mode))) {
386 	    msg = vstrallocf(_("\"%s\" is not a regular file"), filename);
387 	    dbprintf("%s\n", msg);
388 	    g_ptr_array_add(*emsg, msg);
389 	    errno = -1;
390 	    amfree(filename);
391 	    amfree(cmd);
392 	    return NULL;
393     }
394     amfree(cmd);
395     return filename;
396 }
397 
398 /* find all matching entries in a dump listing */
399 /* return -1 if error */
400 static int
process_ls_dump(char * dir,DUMP_ITEM * dump_item,int recursive,GPtrArray ** emsg)401 process_ls_dump(
402     char *	dir,
403     DUMP_ITEM *	dump_item,
404     int		recursive,
405     GPtrArray **emsg)
406 {
407     char line[STR_SIZE], old_line[STR_SIZE];
408     char *filename = NULL;
409     char *filename_gz;
410     char *dir_slash = NULL;
411     FILE *fp;
412     char *s;
413     int ch;
414     size_t len_dir_slash;
415 
416     old_line[0] = '\0';
417     if (strcmp(dir, "/") == 0) {
418 	dir_slash = stralloc(dir);
419     } else {
420 	dir_slash = stralloc2(dir, "/");
421     }
422 
423     filename_gz = get_index_name(dump_hostname, dump_item->hostname, disk_name,
424 				 dump_item->date, dump_item->level);
425     if (filename_gz == NULL) {
426 	g_ptr_array_add(*emsg, stralloc(_("index file not found")));
427 	amfree(filename_gz);
428 	return -1;
429     }
430     filename = uncompress_file(filename_gz, emsg);
431     if(filename == NULL) {
432 	amfree(filename_gz);
433 	amfree(dir_slash);
434 	return -1;
435     }
436     amfree(filename_gz);
437 
438     if((fp = fopen(filename,"r"))==0) {
439 	g_ptr_array_add(*emsg, vstrallocf("%s", strerror(errno)));
440 	amfree(dir_slash);
441         amfree(filename);
442 	return -1;
443     }
444 
445     len_dir_slash=strlen(dir_slash);
446 
447     while (fgets(line, STR_SIZE, fp) != NULL) {
448 	if (line[0] != '\0') {
449 	    if(line[strlen(line)-1] == '\n')
450 		line[strlen(line)-1] = '\0';
451 	    if(strncmp(dir_slash, line, len_dir_slash) == 0) {
452 		if(!recursive) {
453 		    s = line + len_dir_slash;
454 		    ch = *s++;
455 		    while(ch && ch != '/')
456 			ch = *s++;/* find end of the file name */
457 		    if(ch == '/') {
458 			ch = *s++;
459 		    }
460 		    s[-1] = '\0';
461 		}
462 		if(strcmp(line, old_line) != 0) {
463 		    add_dir_list_item(dump_item, line);
464 		    strcpy(old_line, line);
465 		}
466 	    }
467 	}
468     }
469     afclose(fp);
470     amfree(filename);
471     amfree(dir_slash);
472     return 0;
473 }
474 
475 static void
reply_ptr_array(int n,GPtrArray * emsg)476 reply_ptr_array(
477     int n,
478     GPtrArray *emsg)
479 {
480     gpointer *p;
481 
482     if (emsg->len == 0)
483 	return;
484 
485     p = emsg->pdata;
486     while (p != emsg->pdata + emsg->len -1) {
487 	fast_lreply(n, "%s", (char *)*p);
488 	p++;
489     }
490     reply(n, "%s", (char *)*p);
491 }
492 
493 /* send a 1 line reply to the client */
printf_arglist_function1(static void reply,int,n,char *,fmt)494 printf_arglist_function1(static void reply, int, n, char *, fmt)
495 {
496     va_list args;
497     int len;
498 
499     if(!reply_buffer)
500 	reply_buffer = alloc(reply_buffer_size);
501 
502     while(1) {
503 	arglist_start(args, fmt);
504 	len = g_vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
505 	arglist_end(args);
506 
507 	if (len > -1 && (size_t)len < reply_buffer_size-1)
508 	    break;
509 
510 	reply_buffer_size *= 2;
511 	amfree(reply_buffer);
512 	reply_buffer = alloc(reply_buffer_size);
513     }
514 
515     if (g_fprintf(cmdout,"%03d %s\r\n", n, reply_buffer) < 0)
516     {
517 	dbprintf(_("! error %d (%s) in printf\n"), errno, strerror(errno));
518 	uncompress_remove = remove_files(uncompress_remove);
519 	exit(1);
520     }
521     if (fflush(cmdout) != 0)
522     {
523 	dbprintf(_("! error %d (%s) in fflush\n"), errno, strerror(errno));
524 	uncompress_remove = remove_files(uncompress_remove);
525 	exit(1);
526     }
527     dbprintf(_("< %03d %s\n"), n, reply_buffer);
528 }
529 
530 /* send one line of a multi-line response */
printf_arglist_function1(static void lreply,int,n,char *,fmt)531 printf_arglist_function1(static void lreply, int, n, char *, fmt)
532 {
533     va_list args;
534     int len;
535 
536     if(!reply_buffer)
537 	reply_buffer = alloc(reply_buffer_size);
538 
539     while(1) {
540 	arglist_start(args, fmt);
541 	len = g_vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
542 	arglist_end(args);
543 
544 	if (len > -1 && (size_t)len < reply_buffer_size-1)
545 	    break;
546 
547 	reply_buffer_size *= 2;
548 	amfree(reply_buffer);
549 	reply_buffer = alloc(reply_buffer_size);
550     }
551 
552     if (g_fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0)
553     {
554 	dbprintf(_("! error %d (%s) in printf\n"),
555 		  errno, strerror(errno));
556 	uncompress_remove = remove_files(uncompress_remove);
557 	exit(1);
558     }
559     if (fflush(cmdout) != 0)
560     {
561 	dbprintf(_("! error %d (%s) in fflush\n"),
562 		  errno, strerror(errno));
563 	uncompress_remove = remove_files(uncompress_remove);
564 	exit(1);
565     }
566 
567     dbprintf("< %03d-%s\n", n, reply_buffer);
568 
569 }
570 
571 /* send one line of a multi-line response */
printf_arglist_function1(static void fast_lreply,int,n,char *,fmt)572 printf_arglist_function1(static void fast_lreply, int, n, char *, fmt)
573 {
574     va_list args;
575     int len;
576 
577     if(!reply_buffer)
578 	reply_buffer = alloc(reply_buffer_size);
579 
580     while(1) {
581 	arglist_start(args, fmt);
582 	len = g_vsnprintf(reply_buffer, reply_buffer_size, fmt, args);
583 	arglist_end(args);
584 
585 	if (len > -1 && (size_t)len < reply_buffer_size-1)
586 	    break;
587 
588 	reply_buffer_size *= 2;
589 	amfree(reply_buffer);
590 	reply_buffer = alloc(reply_buffer_size);
591     }
592 
593     if (g_fprintf(cmdout,"%03d-%s\r\n", n, reply_buffer) < 0)
594     {
595 	dbprintf(_("! error %d (%s) in printf\n"),
596 		  errno, strerror(errno));
597 	uncompress_remove = remove_files(uncompress_remove);
598 	exit(1);
599     }
600 
601     DBG(2, "< %03d-%s", n, reply_buffer);
602 }
603 
604 /* see if hostname is valid */
605 /* valid is defined to be that there is an index directory for it */
606 /* also do a security check on the requested dump hostname */
607 /* to restrict access to index records if required */
608 /* return -1 if not okay */
609 static am_host_t *
is_dump_host_valid(char * host)610 is_dump_host_valid(
611     char *	host)
612 {
613     am_host_t   *ihost;
614     disk_t      *diskp;
615 
616     if (get_config_name() == NULL) {
617 	reply(501, _("Must set config before setting host."));
618 	return NULL;
619     }
620 
621     /* check that the config actually handles that host */
622     ihost = lookup_host(host);
623     if(ihost == NULL) {
624 	reply(501, _("Host %s is not in your disklist."), host);
625 	return NULL;
626     }
627 
628     /* check if an index dir exist */
629     if(get_index_dir(host, ihost->hostname, NULL)) {
630 	return ihost;
631     }
632 
633     /* check if an index dir exist for at least one DLE */
634     for(diskp = ihost->disks; diskp != NULL; diskp = diskp->hostnext) {
635 	if (get_index_dir(diskp->hostname, NULL, NULL)) {
636 	    return ihost;
637 	}
638     }
639 
640     reply(501, _("No index records for host: %s. Have you enabled indexing?"),
641 	  host);
642     return NULL;
643 }
644 
645 
646 static gboolean
is_disk_allowed(disk_t * disk)647 is_disk_allowed(
648     disk_t *disk)
649 {
650     dumptype_t *dt = disk->config;
651     host_limit_t *rl = NULL;
652     char *peer;
653     char *dle_hostname;
654     GSList *iter;
655 
656     /* get the config: either for the DLE or the global config */
657     if (dt) {
658 	if (dumptype_seen(dt, DUMPTYPE_RECOVERY_LIMIT)) {
659 	    g_debug("using recovery limit from DLE");
660 	    rl = dumptype_get_recovery_limit(dt);
661 	}
662     }
663     if (!rl) {
664 	if (getconf_seen(CNF_RECOVERY_LIMIT)) {
665 	    g_debug("using global recovery limit");
666 	    rl = getconf_recovery_limit(CNF_RECOVERY_LIMIT);
667 	}
668     }
669     if (!rl) {
670 	g_debug("no recovery limit found; allowing access");
671 	return TRUE;
672     }
673 
674     peer = getenv("AMANDA_AUTHENTICATED_PEER");
675     if (!peer || !*peer) {
676 	g_warning("DLE has a recovery-limit, but no authenticated peer name is "
677 		  "available; rejecting");
678 	return FALSE;
679     }
680 
681     /* check same-host */
682     dle_hostname = disk->host? disk->host->hostname : NULL;
683     if (rl->same_host && dle_hostname) {
684 	if (0 == g_ascii_strcasecmp(peer, dle_hostname)) {
685 	    g_debug("peer matched same-host ('%s')", dle_hostname);
686 	    return TRUE;
687 	}
688     }
689 
690     /* check server */
691     if (rl->server) {
692 	char myhostname[MAX_HOSTNAME_LENGTH+1];
693 	if (gethostname(myhostname, MAX_HOSTNAME_LENGTH) == 0) {
694 	    myhostname[MAX_HOSTNAME_LENGTH] = '\0';
695 	    g_debug("server hostname: %s", myhostname);
696 	    if (0 == g_ascii_strcasecmp(peer, myhostname)) {
697 		g_debug("peer matched server ('%s')", myhostname);
698 		return TRUE;
699 	    }
700 	}
701     }
702 
703     /* check the match list */
704     for (iter = rl->match_pats; iter; iter = iter->next) {
705 	char *pat = iter->data;
706 	if (match_host(pat, peer))
707 	    return TRUE;
708     }
709 
710     g_warning("peer '%s' does not match any of the recovery-limit restrictions; rejecting",
711 	    peer);
712 
713     return FALSE;
714 }
715 
716 static int
is_disk_valid(char * disk)717 is_disk_valid(
718     char *disk)
719 {
720     disk_t *idisk;
721     char *qdisk;
722 
723     if (get_config_name() == NULL) {
724 	reply(501, _("Must set config,host before setting disk."));
725 	return -1;
726     }
727     else if (dump_hostname == NULL) {
728 	reply(501, _("Must set host before setting disk."));
729 	return -1;
730     }
731 
732     /* check that the config actually handles that disk, and that recovery-limit allows it */
733     idisk = lookup_disk(dump_hostname, disk);
734     if(idisk == NULL || !is_disk_allowed(idisk)) {
735 	qdisk = quote_string(disk);
736 	reply(501, _("Disk %s:%s is not in the server's disklist."), dump_hostname, qdisk);
737 	amfree(qdisk);
738 	return -1;
739     }
740 
741     /* assume an index dir already */
742     if (get_index_dir(dump_hostname, idisk->hostname, disk) == 0) {
743 	qdisk = quote_string(disk);
744 	reply(501, _("No index records for disk: %s. Invalid?"), qdisk);
745 	amfree(qdisk);
746 	return -1;
747     }
748 
749     return 0;
750 }
751 
752 
753 static int
check_and_load_config(char * config)754 check_and_load_config(
755     char *	config)
756 {
757     char *conf_diskfile;
758     char *conf_tapelist;
759     char *conf_indexdir;
760     struct stat dir_stat;
761 
762     /* check that the config actually exists */
763     if (config == NULL) {
764 	reply(501, _("Must set config first."));
765 	return -1;
766     }
767 
768     /* (re-)initialize configuration with the new config name */
769     config_init(CONFIG_INIT_EXPLICIT_NAME, config);
770     if (config_errors(NULL) >= CFGERR_ERRORS) {
771 	reply(501, _("Could not read config file for %s!"), config);
772 	return -1;
773     }
774 
775     check_running_as(RUNNING_AS_DUMPUSER_PREFERRED);
776 
777     conf_diskfile = config_dir_relative(getconf_str(CNF_DISKFILE));
778     read_diskfile(conf_diskfile, &disk_list);
779     amfree(conf_diskfile);
780     if (config_errors(NULL) >= CFGERR_ERRORS) {
781 	reply(501, _("Could not read disk file %s!"), conf_diskfile);
782 	return -1;
783     }
784 
785     conf_tapelist = config_dir_relative(getconf_str(CNF_TAPELIST));
786     if(read_tapelist(conf_tapelist)) {
787 	reply(501, _("Could not read tapelist file %s!"), conf_tapelist);
788 	amfree(conf_tapelist);
789 	return -1;
790     }
791     amfree(conf_tapelist);
792 
793     dbrename(get_config_name(), DBG_SUBDIR_SERVER);
794 
795     output_find = find_dump(&disk_list);
796     /* the 'w' here sorts by write timestamp, so that the first instance of
797      * any particular datestamp/host/disk/level/part that we see is the one
798      * written earlier */
799     sort_find_result("DLKHpwB", &output_find);
800 
801     conf_indexdir = config_dir_relative(getconf_str(CNF_INDEXDIR));
802     if (stat (conf_indexdir, &dir_stat) != 0 || !S_ISDIR(dir_stat.st_mode)) {
803 	reply(501, _("Index directory %s does not exist"), conf_indexdir);
804 	amfree(conf_indexdir);
805 	return -1;
806     }
807     amfree(conf_indexdir);
808 
809     return 0;
810 }
811 
812 
813 static int
build_disk_table(void)814 build_disk_table(void)
815 {
816     char *date;
817     char *last_timestamp;
818     off_t last_filenum;
819     int last_level;
820     int last_partnum;
821     find_result_t *find_output;
822 
823     if (get_config_name() == NULL) {
824 	reply(590, _("Must set config,host,disk before building disk table"));
825 	return -1;
826     }
827     else if (dump_hostname == NULL) {
828 	reply(590, _("Must set host,disk before building disk table"));
829 	return -1;
830     }
831     else if (disk_name == NULL) {
832 	reply(590, _("Must set disk before building disk table"));
833 	return -1;
834     }
835 
836     clear_list();
837     last_timestamp = NULL;
838     last_filenum = (off_t)-1;
839     last_level = -1;
840     last_partnum = -1;
841     for(find_output = output_find;
842 	find_output != NULL;
843 	find_output = find_output->next) {
844 	if(strcasecmp(dump_hostname, find_output->hostname) == 0 &&
845 	   strcmp(disk_name    , find_output->diskname)     == 0 &&
846 	   strcmp("OK"         , find_output->status)       == 0 &&
847 	   strcmp("OK"         , find_output->dump_status)  == 0) {
848 	    /*
849 	     * The sort order puts holding disk entries first.  We want to
850 	     * use them if at all possible, so ignore any other entries
851 	     * for the same datestamp after we see a holding disk entry
852 	     * (as indicated by a filenum of zero).
853 	     */
854 	    if(last_timestamp &&
855 	       strcmp(find_output->timestamp, last_timestamp) == 0 &&
856 	       find_output->level == last_level &&
857 	       last_filenum == 0) {
858 		continue;
859 	    }
860 	    /* ignore duplicate partnum */
861 	    if(last_timestamp &&
862 	       strcmp(find_output->timestamp, last_timestamp) == 0 &&
863 	       find_output->level == last_level &&
864 	       find_output->partnum == last_partnum) {
865 		continue;
866 	    }
867 	    last_timestamp = find_output->timestamp;
868 	    last_filenum = find_output->filenum;
869 	    last_level = find_output->level;
870 	    last_partnum = find_output->partnum;
871 	    date = amindexd_nicedate(find_output->timestamp);
872 	    add_dump(find_output->hostname, date, find_output->level,
873 		     find_output->label, find_output->filenum,
874 		     find_output->partnum, find_output->totalparts);
875 	    dbprintf("- %s %d %s %lld %d %d\n",
876 		     date, find_output->level,
877 		     find_output->label,
878 		     (long long)find_output->filenum,
879 		     find_output->partnum, find_output->totalparts);
880 	}
881     }
882 
883     clean_dump();
884 
885     return 0;
886 }
887 
888 
889 static int
disk_history_list(void)890 disk_history_list(void)
891 {
892     DUMP_ITEM *item;
893     char date[20];
894 
895     if (get_config_name() == NULL) {
896 	reply(502, _("Must set config,host,disk before listing history"));
897 	return -1;
898     }
899     else if (dump_hostname == NULL) {
900 	reply(502, _("Must set host,disk before listing history"));
901 	return -1;
902     }
903     else if (disk_name == NULL) {
904 	reply(502, _("Must set disk before listing history"));
905 	return -1;
906     }
907 
908     lreply(200, _(" Dump history for config \"%s\" host \"%s\" disk %s"),
909 	  get_config_name(), dump_hostname, qdisk_name);
910 
911     for (item=first_dump(); item!=NULL; item=next_dump(item)){
912         char *tapelist_str = marshal_tapelist(item->tapes, 1);
913 
914 	strncpy(date, item->date, 20);
915 	date[19] = '\0';
916 	if(!am_has_feature(their_features,fe_amrecover_timestamp))
917 	    date[10] = '\0';
918 
919 	if(am_has_feature(their_features, fe_amindexd_marshall_in_DHST)){
920 	    lreply(201, " %s %d %s", date, item->level, tapelist_str);
921 	}
922 	else{
923 	    lreply(201, " %s %d %s %lld", date, item->level,
924 		tapelist_str, (long long)item->file);
925 	}
926 	amfree(tapelist_str);
927     }
928 
929     reply(200, _("Dump history for config \"%s\" host \"%s\" disk %s"),
930 	  get_config_name(), dump_hostname, qdisk_name);
931 
932     return 0;
933 }
934 
935 
936 /*
937  * is the directory dir backed up - dir assumed complete relative to
938  * disk mount point
939  */
940 /* opaque version of command */
941 static int
is_dir_valid_opaque(char * dir)942 is_dir_valid_opaque(
943     char *dir)
944 {
945     DUMP_ITEM *item;
946     char line[STR_SIZE];
947     FILE *fp;
948     int last_level;
949     char *ldir = NULL;
950     char *filename_gz = NULL;
951     char *filename = NULL;
952     size_t ldir_len;
953     GPtrArray *emsg = NULL;
954 
955     if (get_config_name() == NULL || dump_hostname == NULL || disk_name == NULL) {
956 	reply(502, _("Must set config,host,disk before asking about directories"));
957 	return -1;
958     }
959     else if (dump_hostname == NULL) {
960 	reply(502, _("Must set host,disk before asking about directories"));
961 	return -1;
962     }
963     else if (disk_name == NULL) {
964 	reply(502, _("Must set disk before asking about directories"));
965 	return -1;
966     }
967     else if (target_date == NULL) {
968 	reply(502, _("Must set date before asking about directories"));
969 	return -1;
970     }
971     /* scan through till we find first dump on or before date */
972     for (item=first_dump(); item!=NULL; item=next_dump(item))
973 	if (cmp_date(item->date, target_date) <= 0)
974 	    break;
975 
976     if (item == NULL)
977     {
978 	/* no dump for given date */
979 	reply(500, _("No dumps available on or before date \"%s\""), target_date);
980 	return -1;
981     }
982 
983     if(strcmp(dir, "/") == 0) {
984 	ldir = stralloc(dir);
985     } else {
986 	ldir = stralloc2(dir, "/");
987     }
988     ldir_len = strlen(ldir);
989 
990     /* go back till we hit a level 0 dump */
991     do
992     {
993 	amfree(filename);
994 	filename_gz = get_index_name(dump_hostname, item->hostname, disk_name,
995 				     item->date, item->level);
996 	if (filename_gz == NULL) {
997 	    reply(599, "index not found");
998 	    amfree(ldir);
999 	    return -1;
1000 	}
1001 	emsg = g_ptr_array_new();
1002 	if((filename = uncompress_file(filename_gz, &emsg)) == NULL) {
1003 	    reply_ptr_array(599, emsg);
1004 	    amfree(filename_gz);
1005 	    g_ptr_array_free_full(emsg);
1006 	    amfree(ldir);
1007 	    return -1;
1008 	}
1009 	g_ptr_array_free_full(emsg);
1010 	amfree(filename_gz);
1011 	dbprintf("f %s\n", filename);
1012 	if ((fp = fopen(filename, "r")) == NULL) {
1013 	    reply(599, _("System error: %s"), strerror(errno));
1014 	    amfree(filename);
1015 	    amfree(ldir);
1016 	    return -1;
1017 	}
1018 	while (fgets(line, STR_SIZE, fp) != NULL) {
1019 	    if (line[0] == '\0')
1020 		continue;
1021 	    if(line[strlen(line)-1] == '\n')
1022 		line[strlen(line)-1] = '\0';
1023 	    if (strncmp(line, ldir, ldir_len) != 0) {
1024 		continue;			/* not found yet */
1025 	    }
1026 	    amfree(filename);
1027 	    amfree(ldir);
1028 	    afclose(fp);
1029 	    return 0;
1030 	}
1031 	afclose(fp);
1032 
1033 	last_level = item->level;
1034 	do
1035 	{
1036 	    item=next_dump(item);
1037 	} while ((item != NULL) && (item->level >= last_level));
1038     } while (item != NULL);
1039 
1040     amfree(filename);
1041     amfree(ldir);
1042     reply(500, _("\"%s\" is an invalid directory"), dir);
1043     return -1;
1044 }
1045 
1046 static int
opaque_ls(char * dir,int recursive)1047 opaque_ls(
1048     char *	dir,
1049     int		recursive)
1050 {
1051     DUMP_ITEM *dump_item;
1052     DIR_ITEM *dir_item;
1053     int level, last_level;
1054     GPtrArray *emsg = NULL;
1055     am_feature_e marshall_feature;
1056 
1057     if (recursive) {
1058         marshall_feature = fe_amindexd_marshall_in_ORLD;
1059     } else {
1060         marshall_feature = fe_amindexd_marshall_in_OLSD;
1061     }
1062 
1063     clear_dir_list();
1064 
1065     if (get_config_name() == NULL) {
1066 	reply(502, _("Must set config,host,disk before listing a directory"));
1067 	return -1;
1068     }
1069     else if (dump_hostname == NULL) {
1070 	reply(502, _("Must set host,disk before listing a directory"));
1071 	return -1;
1072     }
1073     else if (disk_name == NULL) {
1074 	reply(502, _("Must set disk before listing a directory"));
1075 	return -1;
1076     }
1077     else if (target_date == NULL) {
1078 	reply(502, _("Must set date before listing a directory"));
1079 	return -1;
1080     }
1081 
1082     /* scan through till we find first dump on or before date */
1083     for (dump_item=first_dump(); dump_item!=NULL; dump_item=next_dump(dump_item))
1084 	if (cmp_date(dump_item->date, target_date) <= 0)
1085 	    break;
1086 
1087     if (dump_item == NULL)
1088     {
1089 	/* no dump for given date */
1090 	reply(500, _("No dumps available on or before date \"%s\""), target_date);
1091 	return -1;
1092     }
1093 
1094     /* get data from that dump */
1095     emsg = g_ptr_array_new();
1096     if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
1097 	reply_ptr_array(599, emsg);
1098 	g_ptr_array_free_full(emsg);
1099 	return -1;
1100     }
1101 
1102     /* go back processing higher level dumps till we hit a level 0 dump */
1103     last_level = dump_item->level;
1104     while ((last_level != 0) && ((dump_item=next_dump(dump_item)) != NULL))
1105     {
1106 	if (dump_item->level < last_level)
1107 	{
1108 	    last_level = dump_item->level;
1109 	    if (process_ls_dump(dir, dump_item, recursive, &emsg) == -1) {
1110 		reply_ptr_array(599, emsg);
1111 		g_ptr_array_free_full(emsg);
1112 		return -1;
1113 	    }
1114 	}
1115     }
1116     g_ptr_array_free_full(emsg);
1117 
1118     /* return the information to the caller */
1119     lreply(200, _(" Opaque list of %s"), dir);
1120     for(level=0; level < DUMP_LEVELS; level++) {
1121 	for (dir_item = get_dir_list(); dir_item != NULL;
1122 	     dir_item = dir_item->next) {
1123 
1124 	    if(dir_item->dump->level == level) {
1125 		if (!am_has_feature(their_features, marshall_feature) &&
1126 	            (num_entries(dir_item->dump->tapes) > 1 ||
1127 	            dir_item->dump->tapes->numfiles > 1)) {
1128 	            fast_lreply(501, _(" ERROR: Split dumps not supported"
1129 				" with old version of amrecover."));
1130 		    break;
1131 		}
1132 		else {
1133 		    opaque_ls_one(dir_item, marshall_feature, recursive);
1134 		}
1135 	    }
1136 	}
1137     }
1138     reply(200, _(" Opaque list of %s"), dir);
1139 
1140     clear_dir_list();
1141     return 0;
1142 }
1143 
opaque_ls_one(DIR_ITEM * dir_item,am_feature_e marshall_feature,int recursive)1144 void opaque_ls_one(
1145     DIR_ITEM *	 dir_item,
1146     am_feature_e marshall_feature,
1147     int		 recursive)
1148 {
1149     char date[20];
1150     char *tapelist_str;
1151     char *qtapelist_str;
1152     char *qpath;
1153 
1154     if (am_has_feature(their_features, marshall_feature)) {
1155 	tapelist_str = marshal_tapelist(dir_item->dump->tapes, 1);
1156     } else {
1157 	tapelist_str = dir_item->dump->tapes->label;
1158     }
1159 
1160     if (am_has_feature(their_features, fe_amindexd_quote_label)) {
1161 	qtapelist_str = quote_string(tapelist_str);
1162     } else {
1163 	qtapelist_str = stralloc(tapelist_str);
1164     }
1165     strncpy(date, dir_item->dump->date, 20);
1166     date[19] = '\0';
1167     if(!am_has_feature(their_features,fe_amrecover_timestamp))
1168 	date[10] = '\0';
1169 
1170     qpath = quote_string(dir_item->path);
1171     if((!recursive && am_has_feature(their_features,
1172 				     fe_amindexd_fileno_in_OLSD)) ||
1173        (recursive && am_has_feature(their_features,
1174 				    fe_amindexd_fileno_in_ORLD))) {
1175 	fast_lreply(201, " %s %d %s %lld %s",
1176 		    date,
1177 		    dir_item->dump->level,
1178 		    qtapelist_str,
1179 		    (long long)dir_item->dump->file,
1180 		    qpath);
1181     }
1182     else {
1183 
1184 	fast_lreply(201, " %s %d %s %s",
1185 		    date, dir_item->dump->level,
1186 		    qtapelist_str, qpath);
1187     }
1188     amfree(qpath);
1189     if(am_has_feature(their_features, marshall_feature)) {
1190 	amfree(tapelist_str);
1191     }
1192     amfree(qtapelist_str);
1193 }
1194 
1195 /*
1196  * returns the value of changer or tapedev from the amanda.conf file if set,
1197  * otherwise reports an error
1198  */
1199 
1200 static int
tapedev_is(void)1201 tapedev_is(void)
1202 {
1203     char *result;
1204 
1205     /* check state okay to do this */
1206     if (get_config_name() == NULL) {
1207 	reply(501, _("Must set config before asking about tapedev."));
1208 	return -1;
1209     }
1210 
1211     /* use amrecover_changer if possible */
1212     if ((result = getconf_str(CNF_AMRECOVER_CHANGER)) != NULL  &&
1213         *result != '\0') {
1214 	dbprintf(_("tapedev_is amrecover_changer: %s\n"), result);
1215 	reply(200, "%s", result);
1216 	return 0;
1217     }
1218 
1219     /* use changer if possible */
1220     if ((result = getconf_str(CNF_TPCHANGER)) != NULL  &&  *result != '\0') {
1221 	dbprintf(_("tapedev_is tpchanger: %s\n"), result);
1222 	reply(200, "%s", result);
1223 	return 0;
1224     }
1225 
1226     /* get tapedev value */
1227     if ((result = getconf_str(CNF_TAPEDEV)) != NULL  &&  *result != '\0') {
1228 	dbprintf(_("tapedev_is tapedev: %s\n"), result);
1229 	reply(200, "%s", result);
1230 	return 0;
1231     }
1232 
1233     dbprintf(_("No tapedev or tpchanger in config site.\n"));
1234 
1235     reply(501, _("Tapedev or tpchanger not set in config file."));
1236     return -1;
1237 }
1238 
1239 
1240 /* returns YES if dumps for disk are compressed, NO if not */
1241 static int
are_dumps_compressed(void)1242 are_dumps_compressed(void)
1243 {
1244     disk_t *diskp;
1245 
1246     /* check state okay to do this */
1247     if (get_config_name() == NULL) {
1248 	reply(501, _("Must set config,host,disk name before asking about dumps."));
1249 	return -1;
1250     }
1251     else if (dump_hostname == NULL) {
1252 	reply(501, _("Must set host,disk name before asking about dumps."));
1253 	return -1;
1254     }
1255     else if (disk_name == NULL) {
1256 	reply(501, _("Must set disk name before asking about dumps."));
1257 	return -1;
1258     }
1259 
1260     /* now go through the list of disks and find which have indexes */
1261     for (diskp = disk_list.head; diskp != NULL; diskp = diskp->next) {
1262 	if ((strcasecmp(diskp->host->hostname, dump_hostname) == 0)
1263 		&& (strcmp(diskp->name, disk_name) == 0)) {
1264 	    break;
1265 	}
1266     }
1267 
1268     if (diskp == NULL) {
1269 	reply(501, _("Couldn't find host/disk in disk file."));
1270 	return -1;
1271     }
1272 
1273     /* send data to caller */
1274     if (diskp->compress == COMP_NONE)
1275 	reply(200, "NO");
1276     else
1277 	reply(200, "YES");
1278 
1279     return 0;
1280 }
1281 
1282 int
main(int argc,char ** argv)1283 main(
1284     int		argc,
1285     char **	argv)
1286 {
1287     char *line = NULL, *part = NULL;
1288     char *s;
1289     int ch;
1290     char *cmd_undo, cmd_undo_ch;
1291     socklen_t_equiv socklen;
1292     sockaddr_union his_addr;
1293     char *arg = NULL;
1294     char *cmd;
1295     size_t len;
1296     int user_validated = 0;
1297     char *errstr = NULL;
1298     char *pgm = "amindexd";		/* in case argv[0] is not set */
1299     char his_hostname[MAX_HOSTNAME_LENGTH];
1300     char *cfg_opt = NULL;
1301 
1302     if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
1303 	printf("amindexd-%s\n", VERSION);
1304 	return (0);
1305     }
1306 
1307     /*
1308      * Configure program for internationalization:
1309      *   1) Only set the message locale for now.
1310      *   2) Set textdomain for all amanda related programs to "amanda"
1311      *      We don't want to be forced to support dozens of message catalogs.
1312      */
1313     setlocale(LC_MESSAGES, "C");
1314     textdomain("amanda");
1315 
1316     safe_fd(DATA_FD_OFFSET, 2);
1317     openbsd_fd_inform();
1318     safe_cd();
1319 
1320     /*
1321      * When called via inetd, it is not uncommon to forget to put the
1322      * argv[0] value on the config line.  On some systems (e.g. Solaris)
1323      * this causes argv and/or argv[0] to be NULL, so we have to be
1324      * careful getting our name.
1325      */
1326     if (argc >= 1 && argv != NULL && argv[0] != NULL) {
1327 	if((pgm = strrchr(argv[0], '/')) != NULL) {
1328 	    pgm++;
1329 	} else {
1330 	    pgm = argv[0];
1331 	}
1332     }
1333 
1334     set_pname(pgm);
1335 
1336     /* Don't die when child closes pipe */
1337     signal(SIGPIPE, SIG_IGN);
1338 
1339     dbopen(DBG_SUBDIR_SERVER);
1340     dbprintf(_("version %s\n"), VERSION);
1341 
1342     if(argv == NULL) {
1343 	error("argv == NULL\n");
1344     }
1345 
1346     if (! (argc >= 1 && argv[0] != NULL)) {
1347 	dbprintf(_("WARNING: argv[0] not defined: check inetd.conf\n"));
1348     }
1349 
1350     debug_dup_stderr_to_debug();
1351 
1352     /* initialize */
1353 
1354     argc--;
1355     argv++;
1356 
1357     if(argc > 0 && strcmp(*argv, "-t") == 0) {
1358 	amindexd_debug = 1;
1359 	argc--;
1360 	argv++;
1361     }
1362 
1363     if(argc > 0 && strcmp(*argv, "amandad") == 0) {
1364 	from_amandad = 1;
1365 	argc--;
1366 	argv++;
1367 	if(argc > 0) {
1368 	    amandad_auth = *argv;
1369 	    argc--;
1370 	    argv++;
1371 	}
1372     }
1373     else {
1374 	from_amandad = 0;
1375 	safe_fd(dbfd(), 1);
1376     }
1377 
1378     if (argc > 0) {
1379 	cfg_opt = *argv;
1380 	argc--;
1381 	argv++;
1382     }
1383 
1384     if(gethostname(local_hostname, SIZEOF(local_hostname)-1) == -1) {
1385 	error(_("gethostname: %s"), strerror(errno));
1386 	/*NOTREACHED*/
1387     }
1388     local_hostname[SIZEOF(local_hostname)-1] = '\0';
1389 
1390     /* now trim domain off name */
1391     s = local_hostname;
1392     ch = *s++;
1393     while(ch && ch != '.') ch = *s++;
1394     s[-1] = '\0';
1395 
1396 
1397     if(from_amandad == 0) {
1398 	if(!amindexd_debug) {
1399 	    /* who are we talking to? */
1400 	    socklen = sizeof (his_addr);
1401 	    if (getpeername(0, (struct sockaddr *)&his_addr, &socklen) == -1)
1402 		error(_("getpeername: %s"), strerror(errno));
1403 
1404 	    /* Try a reverse (IP->hostname) resolution, and fail if it does
1405 	     * not work -- this is a basic security check */
1406 	    if (getnameinfo((struct sockaddr *)&his_addr, SS_LEN(&his_addr),
1407 			    his_hostname, sizeof(his_hostname),
1408 			    NULL, 0,
1409 			    0)) {
1410 		error(_("getnameinfo(%s): hostname lookup failed"),
1411 		      str_sockaddr(&his_addr));
1412 		/*NOTREACHED*/
1413 	    }
1414 	}
1415 
1416 	/* Set up the input and output FILEs */
1417 	cmdout = stdout;
1418 	cmdin = stdin;
1419     }
1420     else {
1421 	/* read the REQ packet */
1422 	for(; (line = agets(stdin)) != NULL; free(line)) {
1423 	    if(strncmp_const(line, "OPTIONS ") == 0) {
1424                 if (g_options != NULL) {
1425 		    dbprintf(_("REQ packet specified multiple OPTIONS.\n"));
1426                     free_g_options(g_options);
1427                 }
1428 		g_options = parse_g_options(line+8, 1);
1429 		if(!g_options->hostname) {
1430 		    g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
1431 		    gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
1432 		    g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
1433 		}
1434 	    }
1435 	}
1436 	amfree(line);
1437 
1438 	if(amandad_auth && g_options->auth) {
1439 	    if(strcasecmp(amandad_auth, g_options->auth) != 0) {
1440 		g_printf(_("ERROR recover program ask for auth=%s while amindexd is configured for '%s'\n"),
1441 		       g_options->auth, amandad_auth);
1442 		error(_("amindexd: ERROR recover program ask for auth=%s while amindexd is configured for '%s'"),
1443 		      g_options->auth, amandad_auth);
1444 		/*NOTREACHED*/
1445 	    }
1446 	}
1447 	/* send the REP packet */
1448 	g_printf("CONNECT MESG %d\n", DATA_FD_OFFSET);
1449 	g_printf("\n");
1450 	fflush(stdin);
1451 	fflush(stdout);
1452 	fclose(stdin);
1453 	fclose(stdout);
1454 
1455 	cmdout = fdopen(DATA_FD_OFFSET + 0, "a");
1456 	if (!cmdout) {
1457 	    error(_("amindexd: Can't fdopen(%d): %s"), DATA_FD_OFFSET + 0, strerror(errno));
1458 	    /*NOTREACHED*/
1459 	}
1460 
1461 	cmdin = fdopen(DATA_FD_OFFSET + 1, "r");
1462 	if (!cmdin) {
1463 	    error(_("amindexd: Can't fdopen(%d): %s"), DATA_FD_OFFSET + 1, strerror(errno));
1464 	    /*NOTREACHED*/
1465 	}
1466     }
1467 
1468     /* clear these so we can detect when the have not been set by the client */
1469     amfree(dump_hostname);
1470     amfree(qdisk_name);
1471     amfree(disk_name);
1472     amfree(target_date);
1473 
1474     our_features = am_init_feature_set();
1475     their_features = am_set_default_feature_set();
1476 
1477     if (cfg_opt != NULL && check_and_load_config(cfg_opt) != -1) { /* load the config */
1478 	return 1;
1479     }
1480 
1481     reply(220, _("%s AMANDA index server (%s) ready."), local_hostname,
1482 	  VERSION);
1483 
1484     user_validated = from_amandad;
1485 
1486     /* a real simple parser since there are only a few commands */
1487     while (1)
1488     {
1489 	/* get a line from the client */
1490 	while(1) {
1491 	    if((part = agets(cmdin)) == NULL) {
1492 		if(errno != 0) {
1493 		    dbprintf(_("? read error: %s\n"), strerror(errno));
1494 		} else {
1495 		    dbprintf(_("? unexpected EOF\n"));
1496 		}
1497 		if(line) {
1498 		    dbprintf(_("? unprocessed input:\n"));
1499 		    dbprintf("-----\n");
1500 		    dbprintf("? %s\n", line);
1501 		    dbprintf("-----\n");
1502 		}
1503 		amfree(line);
1504 		amfree(part);
1505 		uncompress_remove = remove_files(uncompress_remove);
1506 		dbclose();
1507 		return 1;		/* they hung up? */
1508 	    }
1509 	    strappend(line, part);	/* Macro: line can be null */
1510 	    amfree(part);
1511 
1512 	    if(amindexd_debug) {
1513 		break;			/* we have a whole line */
1514 	    }
1515 	    if((len = strlen(line)) > 0 && line[len-1] == '\r') {
1516 		line[len-1] = '\0';	/* zap the '\r' */
1517 		break;
1518 	    }
1519 	    /*
1520 	     * Hmmm.  We got a "line" from agets(), which means it saw
1521 	     * a '\n' (or EOF, etc), but there was not a '\r' before it.
1522 	     * Put a '\n' back in the buffer and loop for more.
1523 	     */
1524 	    strappend(line, "\n");
1525 	}
1526 
1527 	dbprintf("> %s\n", line);
1528 
1529 	if (arg != NULL)
1530 	    amfree(arg);
1531 	s = line;
1532 	ch = *s++;
1533 
1534 	skip_whitespace(s, ch);
1535 	if(ch == '\0') {
1536 	    reply(500, _("Command not recognised/incorrect: %s"), line);
1537 	    amfree(line);
1538 	    continue;
1539 	}
1540 	cmd = s - 1;
1541 
1542 	skip_non_whitespace(s, ch);
1543 	cmd_undo = s-1;				/* for error message */
1544 	cmd_undo_ch = *cmd_undo;
1545 	*cmd_undo = '\0';
1546 	if (ch) {
1547 	    skip_whitespace(s, ch);		/* find the argument */
1548 	    if (ch) {
1549 		arg = s-1;
1550 		skip_quoted_string(s, ch);
1551 		arg = unquote_string(arg);
1552 	    }
1553 	}
1554 
1555 	amfree(errstr);
1556 	if (!user_validated && strcmp(cmd, "SECURITY") == 0 && arg) {
1557 	    user_validated = amindexd_debug ||
1558 				check_security(
1559 					(sockaddr_union *)&his_addr,
1560 					arg, 0, &errstr, "amindexd");
1561 	    if(user_validated) {
1562 		reply(200, _("Access OK"));
1563 		amfree(line);
1564 		continue;
1565 	    }
1566 	}
1567 	if (!user_validated) {  /* don't tell client the reason, just log it to debug log */
1568 	    reply(500, _("Access not allowed"));
1569 	    if (errstr) {
1570 		dbprintf("%s\n", errstr);
1571 	    }
1572 	    break;
1573 	}
1574 
1575 	if (strcmp(cmd, "QUIT") == 0) {
1576 	    amfree(line);
1577 	    break;
1578 	} else if (strcmp(cmd, "HOST") == 0 && arg) {
1579 	    am_host_t *lhost;
1580 	    /* set host we are restoring */
1581 	    s[-1] = '\0';
1582 	    if ((lhost = is_dump_host_valid(arg)) != NULL)
1583 	    {
1584 		dump_hostname = newstralloc(dump_hostname, lhost->hostname);
1585 		reply(200, _("Dump host set to %s."), dump_hostname);
1586 		amfree(qdisk_name);		/* invalidate any value */
1587 		amfree(disk_name);		/* invalidate any value */
1588 	    }
1589 	    s[-1] = (char)ch;
1590 	} else if (strcmp(cmd, "LISTHOST") == 0) {
1591 	    disk_t *disk,
1592                    *diskdup;
1593 	    int nbhost = 0,
1594                 found = 0;
1595 	    s[-1] = '\0';
1596 	    if (get_config_name() == NULL) {
1597 		reply(501, _("Must set config before listhost"));
1598 	    }
1599 	    else {
1600 		lreply(200, _(" List hosts for config %s"), get_config_name());
1601 		for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1602                     found = 0;
1603 		    for (diskdup = disk_list.head; diskdup!=disk; diskdup = diskdup->next) {
1604 		        if(strcmp(diskdup->host->hostname, disk->host->hostname) == 0) {
1605                           found = 1;
1606                           break;
1607 		        }
1608                     }
1609                     if(!found){
1610 	                fast_lreply(201, " %s", disk->host->hostname);
1611                         nbhost++;
1612                     }
1613 		}
1614 		if(nbhost > 0) {
1615 		    reply(200, _(" List hosts for config %s"), get_config_name());
1616 		}
1617 		else {
1618 		    reply(200, _("No hosts for config %s"), get_config_name());
1619 		}
1620 	    }
1621 	    s[-1] = (char)ch;
1622 	} else if (strcmp(cmd, "DISK") == 0 && arg) {
1623 	    s[-1] = '\0';
1624 	    if (is_disk_valid(arg) != -1) {
1625 		disk_name = newstralloc(disk_name, arg);
1626 		qdisk_name = quote_string(disk_name);
1627 		if (build_disk_table() != -1) {
1628 		    reply(200, _("Disk set to %s."), qdisk_name);
1629 		}
1630 	    }
1631 	    s[-1] = (char)ch;
1632 	} else if (strcmp(cmd, "DLE") == 0) {
1633 	    disk_t *dp;
1634 	    char *optionstr;
1635 	    char *b64disk;
1636 	    char *l, *ql;
1637 
1638 	    dp = lookup_disk(dump_hostname, disk_name);
1639 	    if (dp->line == 0) {
1640 		reply(200, "NODLE");
1641 	    } else {
1642 		GPtrArray *errarray;
1643 		guint      i;
1644 
1645 		b64disk = amxml_format_tag("disk", dp->name);
1646 		dp->host->features = their_features;
1647 		errarray = validate_optionstr(dp);
1648 		if (errarray->len > 0) {
1649 		    for (i=0; i < errarray->len; i++) {
1650 			g_debug(_("ERROR: %s:%s %s"),
1651 				dump_hostname, disk_name,
1652 				(char *)g_ptr_array_index(errarray, i));
1653 		    }
1654 		    g_ptr_array_free(errarray, TRUE);
1655 		    reply(200, "NODLE");
1656 		} else {
1657 		    optionstr = xml_optionstr(dp, 0);
1658 		    l = vstralloc("<dle>\n",
1659 			      "  <program>", dp->program, "</program>\n", NULL);
1660 		    if (dp->application) {
1661 			application_t *application;
1662 			char *xml_app;
1663 
1664 			application = lookup_application(dp->application);
1665 			g_assert(application != NULL);
1666 			xml_app = xml_application(dp, application,
1667 						  their_features);
1668 			vstrextend(&l, xml_app, NULL);
1669 			amfree(xml_app);
1670 		    }
1671 		    vstrextend(&l, "  ", b64disk, "\n", NULL);
1672 		    if (dp->device) {
1673 			char *b64device = amxml_format_tag("diskdevice",
1674 							   dp->device);
1675 			vstrextend(&l, "  ", b64device, "\n", NULL);
1676 			amfree(b64device);
1677 		    }
1678 		    vstrextend(&l, optionstr, "</dle>\n", NULL);
1679 		    ql = quote_string(l);
1680 		    reply(200, "%s", ql);
1681 		    amfree(optionstr);
1682 		    amfree(l);
1683 		    amfree(ql);
1684 		    amfree(b64disk);
1685 		}
1686 	    }
1687 	} else if (strcmp(cmd, "LISTDISK") == 0) {
1688 	    char *qname;
1689 	    disk_t *disk;
1690 	    int nbdisk = 0;
1691 	    s[-1] = '\0';
1692 	    if (get_config_name() == NULL) {
1693 		reply(501, _("Must set config, host before listdisk"));
1694 	    }
1695 	    else if (dump_hostname == NULL) {
1696 		reply(501, _("Must set host before listdisk"));
1697 	    }
1698 	    else if(arg) {
1699 		lreply(200, _(" List of disk for device %s on host %s"), arg,
1700 		       dump_hostname);
1701 		for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1702 
1703 		    if (strcasecmp(disk->host->hostname, dump_hostname) == 0 &&
1704 		      ((disk->device && strcmp(disk->device, arg) == 0) ||
1705 		      (!disk->device && strcmp(disk->name, arg) == 0))) {
1706 			qname = quote_string(disk->name);
1707 			fast_lreply(201, " %s", qname);
1708 			amfree(qname);
1709 			nbdisk++;
1710 		    }
1711 		}
1712 		if(nbdisk > 0) {
1713 		    reply(200, _("List of disk for device %s on host %s"), arg,
1714 			  dump_hostname);
1715 		}
1716 		else {
1717 		    reply(200, _("No disk for device %s on host %s"), arg,
1718 			  dump_hostname);
1719 		}
1720 	    }
1721 	    else {
1722 		lreply(200, _(" List of disk for host %s"), dump_hostname);
1723 		for (disk = disk_list.head; disk!=NULL; disk = disk->next) {
1724 		    if(strcasecmp(disk->host->hostname, dump_hostname) == 0) {
1725 			qname = quote_string(disk->name);
1726 			fast_lreply(201, " %s", qname);
1727 			amfree(qname);
1728 			nbdisk++;
1729 		    }
1730 		}
1731 		if(nbdisk > 0) {
1732 		    reply(200, _("List of disk for host %s"), dump_hostname);
1733 		}
1734 		else {
1735 		    reply(200, _("No disk for host %s"), dump_hostname);
1736 		}
1737 	    }
1738 	    s[-1] = (char)ch;
1739 	} else if (strcmp(cmd, "SCNF") == 0 && arg) {
1740 	    s[-1] = '\0';
1741 	    if (check_and_load_config(arg) != -1) {    /* try to load the new config */
1742 		amfree(dump_hostname);		/* invalidate any value */
1743 		amfree(qdisk_name);		/* invalidate any value */
1744 		amfree(disk_name);		/* invalidate any value */
1745 		reply(200, _("Config set to %s."), get_config_name());
1746 	    } /* check_and_load_config replies with any failure messages */
1747 	    s[-1] = (char)ch;
1748 	} else if (strcmp(cmd, "FEATURES") == 0 && arg) {
1749 	    char *our_feature_string = NULL;
1750 	    char *their_feature_string = NULL;
1751 	    s[-1] = '\0';
1752 	    am_release_feature_set(our_features);
1753 	    am_release_feature_set(their_features);
1754 	    our_features = am_init_feature_set();
1755 	    our_feature_string = am_feature_to_string(our_features);
1756 	    their_feature_string = newstralloc(their_feature_string, arg);
1757 	    their_features = am_string_to_feature(their_feature_string);
1758 	    if (!their_features) {
1759 		g_warning("Invalid client feature set '%s'", their_feature_string);
1760 		their_features = am_set_default_feature_set();
1761 	    }
1762 	    reply(200, "FEATURES %s", our_feature_string);
1763 	    amfree(our_feature_string);
1764 	    amfree(their_feature_string);
1765 	    s[-1] = (char)ch;
1766 	} else if (strcmp(cmd, "DATE") == 0 && arg) {
1767 	    s[-1] = '\0';
1768 	    target_date = newstralloc(target_date, arg);
1769 	    reply(200, _("Working date set to %s."), target_date);
1770 	    s[-1] = (char)ch;
1771 	} else if (strcmp(cmd, "DHST") == 0) {
1772 	    (void)disk_history_list();
1773 	} else if (strcmp(cmd, "OISD") == 0 && arg) {
1774 	    if (is_dir_valid_opaque(arg) != -1) {
1775 		reply(200, _("\"%s\" is a valid directory"), arg);
1776 	    }
1777 	} else if (strcmp(cmd, "OLSD") == 0 && arg) {
1778 	    (void)opaque_ls(arg,0);
1779 	} else if (strcmp(cmd, "ORLD") == 0 && arg) {
1780 	    (void)opaque_ls(arg, 1);
1781 	} else if (strcmp(cmd, "TAPE") == 0) {
1782 	    (void)tapedev_is();
1783 	} else if (strcmp(cmd, "DCMP") == 0) {
1784 	    (void)are_dumps_compressed();
1785 	} else {
1786 	    *cmd_undo = cmd_undo_ch;	/* restore the command line */
1787 	    reply(500, _("Command not recognised/incorrect: %s"), cmd);
1788 	}
1789 	amfree(line);
1790     }
1791     amfree(arg);
1792 
1793     uncompress_remove = remove_files(uncompress_remove);
1794     free_find_result(&output_find);
1795     reply(200, _("Good bye."));
1796     dbclose();
1797     return 0;
1798 }
1799 
1800 static char *
amindexd_nicedate(char * datestamp)1801 amindexd_nicedate(
1802     char *	datestamp)
1803 {
1804     static char nice[20];
1805     int year, month, day;
1806     int hours, minutes, seconds;
1807     char date[9], atime[7];
1808     int  numdate, numtime;
1809 
1810     strncpy(date, datestamp, 8);
1811     date[8] = '\0';
1812     numdate = atoi(date);
1813     year  = numdate / 10000;
1814     month = (numdate / 100) % 100;
1815     day   = numdate % 100;
1816 
1817     if(strlen(datestamp) <= 8) {
1818 	g_snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d",
1819 		year, month, day);
1820     }
1821     else {
1822 	strncpy(atime, &(datestamp[8]), 6);
1823 	atime[6] = '\0';
1824 	numtime = atoi(atime);
1825 	hours = numtime / 10000;
1826 	minutes = (numtime / 100) % 100;
1827 	seconds = numtime % 100;
1828 
1829 	g_snprintf(nice, SIZEOF(nice), "%4d-%02d-%02d-%02d-%02d-%02d",
1830 		year, month, day, hours, minutes, seconds);
1831     }
1832 
1833     return nice;
1834 }
1835 
1836 static int
cmp_date(const char * date1,const char * date2)1837 cmp_date(
1838     const char *	date1,
1839     const char *	date2)
1840 {
1841     return strncmp(date1, date2, strlen(date2));
1842 }
1843 
1844 static int
get_index_dir(char * dump_hostname,char * hostname,char * diskname)1845 get_index_dir(
1846     char *dump_hostname,
1847     char *hostname,
1848     char *diskname)
1849 {
1850     struct stat  dir_stat;
1851     char        *fn;
1852     char        *s;
1853     char        *lower_hostname;
1854 
1855     lower_hostname = stralloc(dump_hostname);
1856     for(s=lower_hostname; *s != '\0'; s++)
1857 	*s = tolower(*s);
1858 
1859     fn = getindexfname(dump_hostname, diskname, NULL, 0);
1860     if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1861 	amfree(lower_hostname);
1862 	amfree(fn);
1863 	return 1;
1864     }
1865     amfree(fn);
1866     if (hostname != NULL) {
1867 	fn = getindexfname(hostname, diskname, NULL, 0);
1868 	if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1869 	    amfree(lower_hostname);
1870 	    amfree(fn);
1871 	    return 1;
1872 	}
1873     }
1874     amfree(fn);
1875     fn = getindexfname(lower_hostname, diskname, NULL, 0);
1876     if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1877 	amfree(lower_hostname);
1878 	amfree(fn);
1879 	return 1;
1880     }
1881     amfree(fn);
1882     if(diskname != NULL) {
1883 	fn = getoldindexfname(dump_hostname, diskname, NULL, 0);
1884 	if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1885 	    amfree(lower_hostname);
1886 	    amfree(fn);
1887 	    return 1;
1888 	}
1889 	amfree(fn);
1890 	if (hostname != NULL) {
1891 	    fn = getoldindexfname(hostname, diskname, NULL, 0);
1892 	    if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1893 		amfree(lower_hostname);
1894 		amfree(fn);
1895 		return 1;
1896 	    }
1897 	}
1898 	amfree(fn);
1899 	fn = getoldindexfname(lower_hostname, diskname, NULL, 0);
1900 	if (stat(fn, &dir_stat) == 0 && S_ISDIR(dir_stat.st_mode)) {
1901 	    amfree(lower_hostname);
1902 	    amfree(fn);
1903 	    return 1;
1904 	}
1905 	amfree(fn);
1906     }
1907     amfree(lower_hostname);
1908     return -1;
1909 }
1910 
1911 static char *
get_index_name(char * dump_hostname,char * hostname,char * diskname,char * timestamps,int level)1912 get_index_name(
1913     char *dump_hostname,
1914     char *hostname,
1915     char *diskname,
1916     char *timestamps,
1917     int   level)
1918 {
1919     struct stat  dir_stat;
1920     char        *fn;
1921     char        *s;
1922     char        *lower_hostname;
1923 
1924     lower_hostname = stralloc(dump_hostname);
1925     for(s=lower_hostname; *s != '\0'; s++)
1926 	*s = tolower(*s);
1927 
1928     fn = getindexfname(dump_hostname, diskname, timestamps, level);
1929     if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1930 	amfree(lower_hostname);
1931 	return fn;
1932     }
1933     amfree(fn);
1934     if(hostname != NULL) {
1935 	fn = getindexfname(hostname, diskname, timestamps, level);
1936 	if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1937 	    amfree(lower_hostname);
1938 	    return fn;
1939 	}
1940     }
1941     amfree(fn);
1942     fn = getindexfname(lower_hostname, diskname, timestamps, level);
1943     if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1944 	amfree(lower_hostname);
1945 	return fn;
1946     }
1947     amfree(fn);
1948     if(diskname != NULL) {
1949 	fn = getoldindexfname(dump_hostname, diskname, timestamps, level);
1950 	if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1951 	    amfree(lower_hostname);
1952 	    return fn;
1953 	}
1954 	amfree(fn);
1955 	if(hostname != NULL) {
1956 	    fn = getoldindexfname(hostname, diskname, timestamps, level);
1957 	    if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1958 		amfree(lower_hostname);
1959 		return fn;
1960 	    }
1961 	}
1962 	amfree(fn);
1963 	fn = getoldindexfname(lower_hostname, diskname, timestamps, level);
1964 	if (stat(fn, &dir_stat) == 0 && S_ISREG(dir_stat.st_mode)) {
1965 	    amfree(lower_hostname);
1966 	    return fn;
1967 	}
1968     }
1969     amfree(lower_hostname);
1970     return NULL;
1971 }
1972