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