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: client_util.c,v 1.34 2006/05/25 01:47:11 johnfranks Exp $
29  *
30  */
31 
32 #include "amanda.h"
33 #include "conffile.h"
34 #include "client_util.h"
35 #include "getfsent.h"
36 #include "util.h"
37 #include "glib-util.h"
38 #include "timestamp.h"
39 #include "pipespawn.h"
40 #include "amxml.h"
41 #include "glob.h"
42 #include "clock.h"
43 #include "amandates.h"
44 #include "security-file.h"
45 
46 #define MAXMAXDUMPS 16
47 
48 static int add_exclude(FILE *file_exclude, char *aexc, int verbose);
49 static int add_include(char *disk, char *device, FILE *file_include, char *ainc, int verbose);
50 static char *build_name(char *disk, char *exin, int verbose);
51 static char *get_name(char *diskname, char *exin, time_t t, int n);
52 
53 
54 char *
fixup_relative(char * name,char * device)55 fixup_relative(
56     char *	name,
57     char *	device)
58 {
59     char *newname;
60     if(*name != '/') {
61 	char *dirname = amname_to_dirname(device);
62 	newname = vstralloc(dirname, "/", name , NULL);
63 	amfree(dirname);
64     }
65     else {
66 	newname = stralloc(name);
67     }
68     return newname;
69 }
70 
71 /* GDestroyFunc for a hash table whose values are GSLists contianing malloc'd
72  * strings */
73 static void
destroy_slist_free_full(gpointer list)74 destroy_slist_free_full(gpointer list) {
75     slist_free_full((GSList *)list, g_free);
76 }
77 
78 
79 static char *
get_name(char * diskname,char * exin,time_t t,int n)80 get_name(
81     char *	diskname,
82     char *	exin,
83     time_t	t,
84     int		n)
85 {
86     char number[NUM_STR_SIZE];
87     char *filename;
88     char *ts;
89 
90     ts = get_timestamp_from_time(t);
91     if(n == 0)
92 	number[0] = '\0';
93     else
94 	g_snprintf(number, SIZEOF(number), "%03d", n - 1);
95 
96     filename = vstralloc(get_pname(), ".", diskname, ".", ts, number, ".",
97 			 exin, NULL);
98     amfree(ts);
99     return filename;
100 }
101 
102 
103 static char *
build_name(char * disk,char * exin,int verbose)104 build_name(
105     char *	disk,
106     char *	exin,
107     int		verbose)
108 {
109     int n;
110     int fd;
111     char *filename = NULL;
112     char *afilename = NULL;
113     char *diskname;
114     time_t curtime;
115     char *dbgdir;
116     char *e = NULL;
117     DIR *d;
118     struct dirent *entry;
119     char *test_name;
120     size_t match_len, d_name_len;
121     char *quoted;
122 
123     time(&curtime);
124     diskname = sanitise_filename(disk);
125 
126     dbgdir = stralloc2(AMANDA_TMPDIR, "/");
127     if((d = opendir(AMANDA_TMPDIR)) == NULL) {
128 	error(_("open debug directory \"%s\": %s"),
129 		AMANDA_TMPDIR, strerror(errno));
130 	/*NOTREACHED*/
131     }
132     test_name = get_name(diskname, exin,
133 			 curtime - (getconf_int(CNF_DEBUG_DAYS) * 24 * 60 * 60), 0);
134     match_len = strlen(get_pname()) + strlen(diskname) + 2;
135     while((entry = readdir(d)) != NULL) {
136 	if(is_dot_or_dotdot(entry->d_name)) {
137 	    continue;
138 	}
139 	d_name_len = strlen(entry->d_name);
140 	if(strncmp(test_name, entry->d_name, match_len) != 0
141 	   || d_name_len < match_len + 14 + 8
142 	   || strcmp(entry->d_name+ d_name_len - 7, exin) != 0) {
143 	    continue;				/* not one of our files */
144 	}
145 	if(strcmp(entry->d_name, test_name) < 0) {
146 	    e = newvstralloc(e, dbgdir, entry->d_name, NULL);
147 	    (void) unlink(e);                   /* get rid of old file */
148 	}
149     }
150     amfree(test_name);
151     amfree(e);
152     closedir(d);
153 
154     n=0;
155     do {
156 	filename = get_name(diskname, exin, curtime, n);
157 	afilename = newvstralloc(afilename, dbgdir, filename, NULL);
158 	if((fd=open(afilename, O_WRONLY|O_CREAT|O_APPEND, 0600)) < 0){
159 	    amfree(afilename);
160 	    n++;
161 	}
162 	else {
163 	    close(fd);
164 	}
165 	amfree(filename);
166     } while(!afilename && n < 1000);
167 
168     if(afilename == NULL) {
169 	filename = get_name(diskname, exin, curtime, 0);
170 	afilename = newvstralloc(afilename, dbgdir, filename, NULL);
171 	quoted = quote_string(afilename);
172 	dbprintf(_("Cannot create %s (%s)\n"), quoted, strerror(errno));
173 	if(verbose) {
174 	    g_printf(_("ERROR [cannot create %s (%s)]\n"),
175 			quoted, strerror(errno));
176 	}
177 	amfree(quoted);
178 	amfree(afilename);
179 	amfree(filename);
180     }
181 
182     amfree(dbgdir);
183     amfree(diskname);
184 
185     return afilename;
186 }
187 
188 
189 static int
add_exclude(FILE * file_exclude,char * aexc,int verbose)190 add_exclude(
191     FILE *	file_exclude,
192     char *	aexc,
193     int		verbose)
194 {
195     size_t l;
196     char *quoted, *file;
197 
198     (void)verbose;	/* Quiet unused parameter warning */
199 
200     l = strlen(aexc);
201     if(aexc[l-1] == '\n') {
202 	aexc[l-1] = '\0';
203 	l--;
204     }
205     file = quoted = quote_string(aexc);
206     if (*file == '"') {
207 	file[strlen(file) - 1] = '\0';
208 	file++;
209     }
210     g_fprintf(file_exclude, "%s\n", file);
211     amfree(quoted);
212     return 1;
213 }
214 
215 static int
add_include(char * disk,char * device,FILE * file_include,char * ainc,int verbose)216 add_include(
217     char *	disk,
218     char *	device,
219     FILE *	file_include,
220     char *	ainc,
221     int		verbose)
222 {
223     size_t l;
224     int nb_exp=0;
225     char *quoted, *file;
226 
227     (void)disk;		/* Quiet unused parameter warning */
228     (void)device;	/* Quiet unused parameter warning */
229 
230     l = strlen(ainc);
231     if(ainc[l-1] == '\n') {
232 	ainc[l-1] = '\0';
233 	l--;
234     }
235     if (strncmp(ainc, "./", 2) != 0) {
236         quoted = quote_string(ainc);
237         dbprintf(_("include must start with './' (%s)\n"), quoted);
238 	if(verbose) {
239 	    g_printf(_("ERROR [include must start with './' (%s)]\n"), quoted);
240 	}
241 	amfree(quoted);
242     }
243     else {
244 	char *incname = ainc+2;
245 	int set_root;
246 
247         set_root = set_root_privs(1);
248 	/* Take as is if not root && many '/' */
249 	if(!set_root && strchr(incname, '/')) {
250             file = quoted = quote_string(ainc);
251 	    if (*file == '"') {
252 		file[strlen(file) - 1] = '\0';
253 		file++;
254 	    }
255 	    g_fprintf(file_include, "%s\n", file);
256 	    amfree(quoted);
257 	    nb_exp++;
258 	}
259 	else {
260 	    int nb;
261 	    glob_t globbuf;
262 	    char *cwd;
263 
264 	    globbuf.gl_offs = 0;
265 
266 	    cwd = g_get_current_dir();
267 	    if (chdir(device) != 0) {
268 		error(_("Failed to chdir(%s): %s\n"), device, strerror(errno));
269 	    }
270 	    glob(incname, 0, NULL, &globbuf);
271 	    if (chdir(cwd) != 0) {
272 		error(_("Failed to chdir(%s): %s\n"), cwd, strerror(errno));
273 	    }
274 	    if (set_root)
275 		set_root_privs(0);
276 	    nb_exp = globbuf.gl_pathc;
277 	    for (nb=0; nb < nb_exp; nb++) {
278 		file = stralloc2("./", globbuf.gl_pathv[nb]);
279 		quoted = quote_string(file);
280 		if (*file == '"') {
281 		    file[strlen(file) - 1] = '\0';
282 		    file++;
283 		}
284 		g_fprintf(file_include, "%s\n", file);
285 		amfree(quoted);
286 		amfree(file);
287 	    }
288 	}
289     }
290     return nb_exp;
291 }
292 
293 char *
build_exclude(dle_t * dle,int verbose)294 build_exclude(
295     dle_t   *dle,
296     int	     verbose)
297 {
298     char *filename;
299     FILE *file_exclude;
300     FILE *exclude;
301     char *aexc;
302     sle_t *excl;
303     int nb_exclude = 0;
304     char *quoted;
305 
306     if (dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
307     if (dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
308 
309     if (nb_exclude == 0) return NULL;
310 
311     if ((filename = build_name(dle->disk, "exclude", verbose)) != NULL) {
312 	if ((file_exclude = fopen(filename,"w")) != NULL) {
313 
314 	    if (dle->exclude_file) {
315 		for(excl = dle->exclude_file->first; excl != NULL;
316 		    excl = excl->next) {
317 		    add_exclude(file_exclude, excl->name,
318 				verbose && dle->exclude_optional == 0);
319 		}
320 	    }
321 
322 	    if (dle->exclude_list) {
323 		for(excl = dle->exclude_list->first; excl != NULL;
324 		    excl = excl->next) {
325 		    char *exclname = fixup_relative(excl->name, dle->device);
326 		    if((exclude = fopen(exclname, "r")) != NULL) {
327 			while ((aexc = agets(exclude)) != NULL) {
328 			    if (aexc[0] == '\0') {
329 				amfree(aexc);
330 				continue;
331 			    }
332 			    add_exclude(file_exclude, aexc,
333 				        verbose && dle->exclude_optional == 0);
334 			    amfree(aexc);
335 			}
336 			fclose(exclude);
337 		    }
338 		    else {
339 			quoted = quote_string(exclname);
340 			dbprintf(_("Can't open exclude file %s (%s)\n"),
341 				  quoted, strerror(errno));
342 			if(verbose && (dle->exclude_optional == 0 ||
343 				       errno != ENOENT)) {
344 			    g_printf(_("ERROR [Can't open exclude file %s (%s)]\n"),
345 				   quoted, strerror(errno));
346 			}
347 			amfree(quoted);
348 		    }
349 		    amfree(exclname);
350 		}
351 	    }
352             fclose(file_exclude);
353 	} else {
354 	    quoted = quote_string(filename);
355 	    dbprintf(_("Can't create exclude file %s (%s)\n"),
356 		      quoted, strerror(errno));
357 	    if (verbose) {
358 		g_printf(_("ERROR [Can't create exclude file %s (%s)]\n"),
359 			quoted, strerror(errno));
360 	    }
361 	    amfree(quoted);
362 	}
363     }
364 
365     return filename;
366 }
367 
368 char *
build_include(dle_t * dle,int verbose)369 build_include(
370     dle_t   *dle,
371     int	     verbose)
372 {
373     char *filename;
374     FILE *file_include;
375     FILE *include;
376     char *ainc = NULL;
377     sle_t *incl;
378     int nb_include = 0;
379     int nb_exp = 0;
380     char *quoted;
381 
382     if (dle->include_file) nb_include += dle->include_file->nb_element;
383     if (dle->include_list) nb_include += dle->include_list->nb_element;
384 
385     if (nb_include == 0) return NULL;
386 
387     if ((filename = build_name(dle->disk, "include", verbose)) != NULL) {
388 	if ((file_include = fopen(filename,"w")) != NULL) {
389 
390 	    if (dle->include_file) {
391 		for (incl = dle->include_file->first; incl != NULL;
392 		    incl = incl->next) {
393 		    nb_exp += add_include(dle->disk, dle->device, file_include,
394 				  incl->name,
395 				  verbose && dle->include_optional == 0);
396 		}
397 	    }
398 
399 	    if (dle->include_list) {
400 		for (incl = dle->include_list->first; incl != NULL;
401 		    incl = incl->next) {
402 		    char *inclname = fixup_relative(incl->name, dle->device);
403 		    if ((include = fopen(inclname, "r")) != NULL) {
404 			while ((ainc = agets(include)) != NULL) {
405 			    if (ainc[0] == '\0') {
406 				amfree(ainc);
407 				continue;
408 			    }
409 			    nb_exp += add_include(dle->disk, dle->device,
410 						  file_include, ainc,
411 						  verbose && dle->include_optional == 0);
412 			    amfree(ainc);
413 			}
414 			fclose(include);
415 		    }
416 		    else {
417 			quoted = quote_string(inclname);
418 			dbprintf(_("Can't open include file %s (%s)\n"),
419 				  quoted, strerror(errno));
420 			if (verbose && (dle->include_optional == 0 ||
421 				       errno != ENOENT)) {
422 			    g_printf(_("ERROR [Can't open include file %s (%s)]\n"),
423 				   quoted, strerror(errno));
424 			}
425 			amfree(quoted);
426 		   }
427 		   amfree(inclname);
428 		}
429 	    }
430             fclose(file_include);
431 	} else {
432 	    quoted = quote_string(filename);
433 	    dbprintf(_("Can't create include file %s (%s)\n"),
434 		      quoted, strerror(errno));
435 	    if (verbose) {
436 		g_printf(_("ERROR [Can't create include file %s (%s)]\n"),
437 			quoted, strerror(errno));
438 	    }
439 	    amfree(quoted);
440 	}
441     }
442 
443     if (nb_exp == 0) {
444 	quoted = quote_string(dle->disk);
445 	dbprintf(_("Nothing found to include for disk %s\n"), quoted);
446 	if (verbose && dle->include_optional == 0) {
447 	    g_printf(_("ERROR [Nothing found to include for disk %s]\n"), quoted);
448 	}
449 	amfree(quoted);
450     }
451 
452     return filename;
453 }
454 
455 
456 void
parse_options(char * str,dle_t * dle,am_feature_t * fs,int verbose)457 parse_options(
458     char         *str,
459     dle_t        *dle,
460     am_feature_t *fs,
461     int           verbose)
462 {
463     char *exc;
464     char *inc;
465     char *p, *tok;
466     char *quoted;
467 
468     p = stralloc(str);
469     tok = strtok(p,";");
470 
471     while (tok != NULL) {
472 	if(am_has_feature(fs, fe_options_auth)
473 	   && BSTRNCMP(tok,"auth=") == 0) {
474 	    if (dle->auth != NULL) {
475 		quoted = quote_string(tok + 5);
476 		dbprintf(_("multiple auth option %s\n"), quoted);
477 		if(verbose) {
478 		    g_printf(_("ERROR [multiple auth option %s]\n"), quoted);
479 		}
480 		amfree(quoted);
481 	    }
482 	    dle->auth = stralloc(&tok[5]);
483 	}
484 	else if(am_has_feature(fs, fe_options_bsd_auth)
485 	   && BSTRNCMP(tok, "bsd-auth") == 0) {
486 	    if (dle->auth != NULL) {
487 		dbprintf(_("multiple auth option\n"));
488 		if (verbose) {
489 		    g_printf(_("ERROR [multiple auth option]\n"));
490 		}
491 	    }
492 	    dle->auth = stralloc("bsd");
493 	}
494 	else if (BSTRNCMP(tok, "compress-fast") == 0) {
495 	    if (dle->compress != COMP_NONE) {
496 		dbprintf(_("multiple compress option\n"));
497 		if (verbose) {
498 		    g_printf(_("ERROR [multiple compress option]\n"));
499 		}
500 	    }
501 	    dle->compress = COMP_FAST;
502 	}
503 	else if (BSTRNCMP(tok, "compress-best") == 0) {
504 	    if (dle->compress != COMP_NONE) {
505 		dbprintf(_("multiple compress option\n"));
506 		if (verbose) {
507 		    g_printf(_("ERROR [multiple compress option]\n"));
508 		}
509 	    }
510 	    dle->compress = COMP_BEST;
511 	}
512 	else if (BSTRNCMP(tok, "srvcomp-fast") == 0) {
513 	    if (dle->compress != COMP_NONE) {
514 		dbprintf(_("multiple compress option\n"));
515 		if (verbose) {
516 		    g_printf(_("ERROR [multiple compress option]\n"));
517 		}
518 	    }
519 	    dle->compress = COMP_SERVER_FAST;
520 	}
521 	else if (BSTRNCMP(tok, "srvcomp-best") == 0) {
522 	    if (dle->compress != COMP_NONE) {
523 		dbprintf(_("multiple compress option\n"));
524 		if (verbose) {
525 		    g_printf(_("ERROR [multiple compress option]\n"));
526 		}
527 	    }
528 	    dle->compress = COMP_SERVER_BEST;
529 	}
530 	else if (BSTRNCMP(tok, "srvcomp-cust=") == 0) {
531 	    if (dle->compress != COMP_NONE) {
532 		dbprintf(_("multiple compress option\n"));
533 		if (verbose) {
534 		    g_printf(_("ERROR [multiple compress option]\n"));
535 		}
536 	    }
537 	    dle->compprog = stralloc(tok + SIZEOF("srvcomp-cust=") -1);
538 	    dle->compress = COMP_SERVER_CUST;
539 	}
540 	else if (BSTRNCMP(tok, "comp-cust=") == 0) {
541 	    if (dle->compress != COMP_NONE) {
542 		dbprintf(_("multiple compress option\n"));
543 		if (verbose) {
544 		    g_printf(_("ERROR [multiple compress option]\n"));
545 		}
546 	    }
547 	    dle->compprog = stralloc(tok + SIZEOF("comp-cust=") -1);
548 	    dle->compress = COMP_CUST;
549 	    /* parse encryption options */
550 	}
551 	else if (BSTRNCMP(tok, "encrypt-serv-cust=") == 0) {
552 	    if (dle->encrypt != ENCRYPT_NONE) {
553 		dbprintf(_("multiple encrypt option\n"));
554 		if (verbose) {
555 		    g_printf(_("ERROR [multiple encrypt option]\n"));
556 		}
557 	    }
558 	    dle->srv_encrypt = stralloc(tok + SIZEOF("encrypt-serv-cust=") -1);
559 	    dle->encrypt = ENCRYPT_SERV_CUST;
560 	}
561 	else if (BSTRNCMP(tok, "encrypt-cust=") == 0) {
562 	    if (dle->encrypt != ENCRYPT_NONE) {
563 		dbprintf(_("multiple encrypt option\n"));
564 		if (verbose) {
565 		    g_printf(_("ERROR [multiple encrypt option]\n"));
566 		}
567 	    }
568 	    dle->clnt_encrypt= stralloc(tok + SIZEOF("encrypt-cust=") -1);
569 	    dle->encrypt = ENCRYPT_CUST;
570 	}
571 	else if (BSTRNCMP(tok, "server-decrypt-option=") == 0) {
572 	  dle->srv_decrypt_opt = stralloc(tok + SIZEOF("server-decrypt-option=") -1);
573 	}
574 	else if (BSTRNCMP(tok, "client-decrypt-option=") == 0) {
575 	  dle->clnt_decrypt_opt = stralloc(tok + SIZEOF("client-decrypt-option=") -1);
576 	}
577 	else if (BSTRNCMP(tok, "no-record") == 0) {
578 	    if (dle->record != 1) {
579 		dbprintf(_("multiple no-record option\n"));
580 		if (verbose) {
581 		    g_printf(_("ERROR [multiple no-record option]\n"));
582 		}
583 	    }
584 	    dle->record = 0;
585 	}
586 	else if (BSTRNCMP(tok, "index") == 0) {
587 	    if (dle->create_index != 0) {
588 		dbprintf(_("multiple index option\n"));
589 		if (verbose) {
590 		    g_printf(_("ERROR [multiple index option]\n"));
591 		}
592 	    }
593 	    dle->create_index = 1;
594 	}
595 	else if (BSTRNCMP(tok, "exclude-optional") == 0) {
596 	    if (dle->exclude_optional != 0) {
597 		dbprintf(_("multiple exclude-optional option\n"));
598 		if (verbose) {
599 		    g_printf(_("ERROR [multiple exclude-optional option]\n"));
600 		}
601 	    }
602 	    dle->exclude_optional = 1;
603 	}
604 	else if (strcmp(tok, "include-optional") == 0) {
605 	    if (dle->include_optional != 0) {
606 		dbprintf(_("multiple include-optional option\n"));
607 		if (verbose) {
608 		    g_printf(_("ERROR [multiple include-optional option]\n"));
609 		}
610 	    }
611 	    dle->include_optional = 1;
612 	}
613 	else if (BSTRNCMP(tok,"exclude-file=") == 0) {
614 	    exc = unquote_string(&tok[13]);
615 	    dle->exclude_file = append_sl(dle->exclude_file, exc);
616 	    amfree(exc);
617 	}
618 	else if (BSTRNCMP(tok,"exclude-list=") == 0) {
619 	    exc = unquote_string(&tok[13]);
620 	    dle->exclude_list = append_sl(dle->exclude_list, exc);
621 	    amfree(exc);
622 	}
623 	else if (BSTRNCMP(tok,"include-file=") == 0) {
624 	    inc = unquote_string(&tok[13]);
625 	    dle->include_file = append_sl(dle->include_file, inc);
626 	    amfree(inc);
627 	}
628 	else if (BSTRNCMP(tok,"include-list=") == 0) {
629 	    inc = unquote_string(&tok[13]);
630 	    dle->include_list = append_sl(dle->include_list, inc);
631 	    amfree(inc);
632 	}
633 	else if (BSTRNCMP(tok,"kencrypt") == 0) {
634 	    dle->kencrypt = 1;
635 	}
636 	else if (strcmp(tok,"|") != 0) {
637 	    quoted = quote_string(tok);
638 	    dbprintf(_("unknown option %s\n"), quoted);
639 	    if (verbose) {
640 		g_printf(_("ERROR [unknown option: %s]\n"), quoted);
641 	    }
642 	    amfree(quoted);
643 	}
644 	tok = strtok(NULL, ";");
645     }
646     amfree(p);
647 }
648 
649 void
application_property_add_to_argv(GPtrArray * argv_ptr,dle_t * dle,backup_support_option_t * bsu,am_feature_t * amfeatures)650 application_property_add_to_argv(
651     GPtrArray *argv_ptr,
652     dle_t *dle,
653     backup_support_option_t *bsu,
654     am_feature_t *amfeatures)
655 {
656     sle_t *incl, *excl;
657 
658     if (bsu) {
659 	if (bsu->include_file && dle->include_file) {
660 	    for (incl = dle->include_file->first; incl != NULL;
661 		 incl = incl->next) {
662 		g_ptr_array_add(argv_ptr, stralloc("--include-file"));
663 		g_ptr_array_add(argv_ptr, stralloc(incl->name));
664 	    }
665 	}
666 	if (bsu->include_list && dle->include_list) {
667 	    for (incl = dle->include_list->first; incl != NULL;
668 		 incl = incl->next) {
669 		g_ptr_array_add(argv_ptr, stralloc("--include-list"));
670 		g_ptr_array_add(argv_ptr, stralloc(incl->name));
671 	    }
672 	}
673 	if (bsu->include_optional && dle->include_optional) {
674 	    g_ptr_array_add(argv_ptr, stralloc("--include-optional"));
675 	    g_ptr_array_add(argv_ptr, stralloc("yes"));
676 	}
677 
678 	if (bsu->exclude_file && dle->exclude_file) {
679 	    for (excl = dle->exclude_file->first; excl != NULL;
680 	 	 excl = excl->next) {
681 		g_ptr_array_add(argv_ptr, stralloc("--exclude-file"));
682 		g_ptr_array_add(argv_ptr, stralloc(excl->name));
683 	    }
684 	}
685 	if (bsu->exclude_list && dle->exclude_list) {
686 	    for (excl = dle->exclude_list->first; excl != NULL;
687 		excl = excl->next) {
688 		g_ptr_array_add(argv_ptr, stralloc("--exclude-list"));
689 		g_ptr_array_add(argv_ptr, stralloc(excl->name));
690 	    }
691 	}
692 	if (bsu->exclude_optional && dle->exclude_optional) {
693 	    g_ptr_array_add(argv_ptr, stralloc("--exclude-optional"));
694 	    g_ptr_array_add(argv_ptr, stralloc("yes"));
695 	}
696 
697 	if (bsu->features && amfeatures) {
698 	    char *feature_string = am_feature_to_string(amfeatures);
699 	    g_ptr_array_add(argv_ptr, stralloc("--amfeatures"));
700 	    g_ptr_array_add(argv_ptr, feature_string);
701 	}
702 
703 	if (dle->data_path == DATA_PATH_DIRECTTCP &&
704 	    bsu->data_path_set & DATA_PATH_DIRECTTCP) {
705 	    GSList *directtcp;
706 
707 	    g_ptr_array_add(argv_ptr, stralloc("--data-path"));
708 	    g_ptr_array_add(argv_ptr, stralloc("directtcp"));
709 	    for (directtcp = dle->directtcp_list; directtcp != NULL;
710 						  directtcp = directtcp->next) {
711 		g_ptr_array_add(argv_ptr, stralloc("--direct-tcp"));
712 		g_ptr_array_add(argv_ptr, stralloc(directtcp->data));
713 		break; /* XXX temporary; apps only support one ip:port pair */
714 	    }
715 	}
716     }
717 
718     property_add_to_argv(argv_ptr, dle->application_property);
719     return;
720 }
721 
722 typedef struct {
723     dle_t *dle;
724     char *name;
725     proplist_t dle_proplist;
726     int verbose;
727     int good;
728 } merge_property_t;
729 
730 static void
merge_property(gpointer key_p,gpointer value_p,gpointer user_data_p)731 merge_property(
732     gpointer key_p,
733     gpointer value_p,
734     gpointer user_data_p)
735 {
736     char *property_s = key_p;
737     property_t *conf_property = value_p;
738     merge_property_t *merge_p = user_data_p;
739     property_t *dle_property = g_hash_table_lookup(merge_p->dle_proplist,
740 						   property_s);
741     GSList *value;
742     char *qdisk = quote_string(merge_p->dle->disk);
743 
744     if (dle_property) {
745 	if (dle_property->priority && conf_property->priority) {
746 	    if (merge_p->verbose) {
747 		g_fprintf(stdout,
748 			 _("ERROR %s (%s) Both server client have priority for property '%s'.\n"),
749 			 qdisk, merge_p->name, property_s);
750 	    }
751 	    g_debug("ERROR %s (%s) Both server client have priority for property '%s'.", qdisk, merge_p->name, property_s);
752 	    merge_p->good = 0;
753 	    /* Use client property */
754 	    g_hash_table_remove(merge_p->dle_proplist, key_p);
755             g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
756 	} else if (dle_property->priority) {
757 	    if (merge_p->verbose) {
758 		g_fprintf(stdout,
759 			 _("ERROR %s (%s) Server set priority for property '%s' but client set the property.\n"),
760 			 qdisk, merge_p->name, property_s);
761 	    }
762 	    g_debug("%s (%s) Server set priority for property '%s' but client set the property.", qdisk, merge_p->name, property_s);
763 	    /* use server property */
764 	} else if (conf_property->priority) {
765 	    if (merge_p->verbose) {
766 		g_fprintf(stdout,
767 			 _("ERROR %s (%s) Client set priority for property '%s' but server set the property.\n"),
768 			 qdisk, merge_p->name, property_s);
769 	    }
770 	    g_debug("%s (%s) Client set priority for property '%s' but server set the property.", qdisk, merge_p->name, property_s);
771 	    /* Use client property */
772 	    g_hash_table_remove(merge_p->dle_proplist, key_p);
773             g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
774 	} else if (!conf_property->append) {
775 	    if (merge_p->verbose) {
776 		g_fprintf(stdout,
777 			 _("ERROR %s (%s) Both server and client set property '%s', using client value.\n"),
778 			 qdisk, merge_p->name, property_s);
779 	    }
780 	    g_debug("%s (%s) Both server and client set property '%s', using client value.", qdisk, merge_p->name, property_s);
781 	    /* Use client property */
782 	    g_hash_table_remove(merge_p->dle_proplist, key_p);
783             g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
784 	} else { /* merge */
785 	    for (value = conf_property->values; value != NULL;
786 		 value = value->next) {
787 		dle_property->values = g_slist_append(dle_property->values,
788 						      value->data);
789 	    }
790 	}
791     } else { /* take value from conf */
792         g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
793     }
794 }
795 
796 int
merge_properties(dle_t * dle,char * name,proplist_t dle_proplist,proplist_t conf_proplist,int verbose)797 merge_properties(
798     dle_t      *dle,
799     char       *name,
800     proplist_t  dle_proplist,
801     proplist_t  conf_proplist,
802     int         verbose)
803 {
804     merge_property_t merge_p = {dle, name, dle_proplist, verbose, 1};
805 
806     if (conf_proplist != NULL) {
807 	g_hash_table_foreach(conf_proplist,
808                              &merge_property,
809                              &merge_p);
810     }
811 
812     return merge_p.good;
813 }
814 
815 int
merge_dles_properties(dle_t * dles,int verbose)816 merge_dles_properties(
817     dle_t *dles,
818     int verbose)
819 {
820     dle_t         *dle;
821     application_t *app;
822     GSList        *scriptlist;
823     pp_script_t   *pp_script;
824     int            good = 1;
825 
826     for (dle=dles; dle != NULL; dle=dle->next) {
827         if (dle->program_is_application_api) {
828 	    app = NULL;
829 	    if (dle->application_client_name &&
830 		strlen(dle->application_client_name) > 0) {
831 		app = lookup_application(dle->application_client_name);
832 		if (!app) {
833 		    char *qamname = quote_string(dle->disk);
834 		    char *errmsg = vstrallocf("Application '%s' not found on client",
835 					      dle->application_client_name);
836 		    char *qerrmsg = quote_string(errmsg);
837 		    good = 0;
838 		    if (verbose) {
839 			g_fprintf(stdout, _("ERROR %s %s\n"), qamname, qerrmsg);
840 		    }
841 		    g_debug("%s: %s", qamname, qerrmsg);
842 		    amfree(qamname);
843 		    amfree(errmsg);
844 		    amfree(qerrmsg);
845 		}
846 	    } else {
847 		app = lookup_application(dle->program);
848 	    }
849             if (app) {
850                 merge_properties(dle, dle->program,
851 				 dle->application_property,
852 				 application_get_property(app),
853 				 verbose);
854             }
855         }
856         for (scriptlist = dle->scriptlist; scriptlist != NULL;
857              scriptlist = scriptlist->next) {
858             script_t *script =  scriptlist->data;
859 	    pp_script = NULL;
860 	    if (script->client_name && strlen(script->client_name) > 0) {
861 		pp_script = lookup_pp_script(script->client_name);
862 		if (!pp_script) {
863 		    char *qamname = quote_string(dle->disk);
864 		    char *errmsg = vstrallocf("Script '%s' not found on client",
865 					      script->client_name);
866 		    char *qerrmsg = quote_string(errmsg);
867 		    good = 0;
868 		    if (verbose) {
869 			g_fprintf(stderr, _("ERROR %s %s\n"), qamname, qerrmsg);
870 		    }
871 		    g_debug("%s: %s", qamname, qerrmsg);
872 		    amfree(qamname);
873 		    amfree(errmsg);
874 		    amfree(qerrmsg);
875 		}
876 	    } else {
877 		pp_script = lookup_pp_script(script->plugin);
878 	    }
879             if (pp_script) {
880 		merge_properties(dle, script->plugin,
881 				 script->property,
882 				 pp_script_get_property(pp_script),
883 				 verbose);
884 	    }
885         }
886     }
887     return good;
888 }
889 
890 backup_support_option_t *
backup_support_option(char * program,g_option_t * g_options,char * disk,char * amdevice,GPtrArray ** errarray)891 backup_support_option(
892     char       *program,
893     g_option_t *g_options,
894     char       *disk,
895     char       *amdevice,
896     GPtrArray **errarray)
897 {
898     pid_t   supportpid;
899     int     supportin, supportout, supporterr;
900     char   *cmd;
901     GPtrArray *argv_ptr = g_ptr_array_new();
902     FILE   *streamout;
903     FILE   *streamerr;
904     char   *line;
905     int     status;
906     char   *err = NULL;
907     backup_support_option_t *bsu;
908 
909     *errarray = g_ptr_array_new();
910     cmd = vstralloc(APPLICATION_DIR, "/", program, NULL);
911     g_ptr_array_add(argv_ptr, stralloc(program));
912     g_ptr_array_add(argv_ptr, stralloc("support"));
913     if (g_options->config) {
914 	g_ptr_array_add(argv_ptr, stralloc("--config"));
915 	g_ptr_array_add(argv_ptr, stralloc(g_options->config));
916     }
917     if (g_options->hostname) {
918 	g_ptr_array_add(argv_ptr, stralloc("--host"));
919 	g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
920     }
921     if (disk) {
922 	g_ptr_array_add(argv_ptr, stralloc("--disk"));
923 	g_ptr_array_add(argv_ptr, stralloc(disk));
924     }
925     if (amdevice) {
926 	g_ptr_array_add(argv_ptr, stralloc("--device"));
927 	g_ptr_array_add(argv_ptr, stralloc(amdevice));
928     }
929     g_ptr_array_add(argv_ptr, NULL);
930 
931     supporterr = fileno(stderr);
932     supportpid = pipespawnv(cmd, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
933 			    &supportin, &supportout, &supporterr,
934 			    (char **)argv_ptr->pdata);
935 
936     aclose(supportin);
937 
938     bsu = g_new0(backup_support_option_t, 1);
939     bsu->config = 1;
940     bsu->host = 1;
941     bsu->disk = 1;
942     streamout = fdopen(supportout, "r");
943     if (!streamout) {
944 	error(_("Error opening pipe to child: %s"), strerror(errno));
945 	/* NOTREACHED */
946     }
947     while((line = agets(streamout)) != NULL) {
948 	dbprintf(_("support line: %s\n"), line);
949 	if (strncmp(line,"CONFIG ", 7) == 0) {
950 	    if (strcmp(line+7, "YES") == 0)
951 		bsu->config = 1;
952 	} else if (strncmp(line,"HOST ", 5) == 0) {
953 	    if (strcmp(line+5, "YES") == 0)
954 	    bsu->host = 1;
955 	} else if (strncmp(line,"DISK ", 5) == 0) {
956 	    if (strcmp(line+5, "YES") == 0)
957 		bsu->disk = 1;
958 	} else if (strncmp(line,"INDEX-LINE ", 11) == 0) {
959 	    if (strcmp(line+11, "YES") == 0)
960 		bsu->index_line = 1;
961 	} else if (strncmp(line,"INDEX-XML ", 10) == 0) {
962 	    if (strcmp(line+10, "YES") == 0)
963 		bsu->index_xml = 1;
964 	} else if (strncmp(line,"MESSAGE-LINE ", 13) == 0) {
965 	    if (strcmp(line+13, "YES") == 0)
966 		bsu->message_line = 1;
967 	} else if (strncmp(line,"MESSAGE-XML ", 12) == 0) {
968 	    if (strcmp(line+12, "YES") == 0)
969 		bsu->message_xml = 1;
970 	} else if (strncmp(line,"RECORD ", 7) == 0) {
971 	    if (strcmp(line+7, "YES") == 0)
972 		bsu->record = 1;
973 	} else if (strncmp(line,"INCLUDE-FILE ", 13) == 0) {
974 	    if (strcmp(line+13, "YES") == 0)
975 		bsu->include_file = 1;
976 	} else if (strncmp(line,"INCLUDE-LIST ", 13) == 0) {
977 	    if (strcmp(line+13, "YES") == 0)
978 		bsu->include_list = 1;
979 	} else if (strncmp(line,"INCLUDE-LIST-GLOB ", 17) == 0) {
980 	    if (strcmp(line+17, "YES") == 0)
981 		bsu->include_list_glob = 1;
982 	} else if (strncmp(line,"INCLUDE-OPTIONAL ", 17) == 0) {
983 	    if (strcmp(line+17, "YES") == 0)
984 		bsu->include_optional = 1;
985 	} else if (strncmp(line,"EXCLUDE-FILE ", 13) == 0) {
986 	    if (strcmp(line+13, "YES") == 0)
987 		bsu->exclude_file = 1;
988 	} else if (strncmp(line,"EXCLUDE-LIST ", 13) == 0) {
989 	    if (strcmp(line+13, "YES") == 0)
990 		bsu->exclude_list = 1;
991 	} else if (strncmp(line,"EXCLUDE-LIST-GLOB ", 17) == 0) {
992 	    if (strcmp(line+17, "YES") == 0)
993 		bsu->exclude_list_glob = 1;
994 	} else if (strncmp(line,"EXCLUDE-OPTIONAL ", 17) == 0) {
995 	    if (strcmp(line+17, "YES") == 0)
996 		bsu->exclude_optional = 1;
997 	} else if (strncmp(line,"COLLECTION ", 11) == 0) {
998 	    if (strcmp(line+11, "YES") == 0)
999 		bsu->collection = 1;
1000 	} else if (strncmp(line,"CALCSIZE ", 9) == 0) {
1001 	    if (strcmp(line+9, "YES") == 0)
1002 		bsu->calcsize = 1;
1003 	} else if (strncmp(line,"CLIENT-ESTIMATE ", 16) == 0) {
1004 	    if (strcmp(line+16, "YES") == 0)
1005 		bsu->client_estimate = 1;
1006 	} else if (strncmp(line,"MULTI-ESTIMATE ", 15) == 0) {
1007 	    if (strcmp(line+15, "YES") == 0)
1008 		bsu->multi_estimate = 1;
1009 	} else if (strncmp(line,"MAX-LEVEL ", 10) == 0) {
1010 	    bsu->max_level  = atoi(line+10);
1011 	} else if (strncmp(line,"RECOVER-MODE ", 13) == 0) {
1012 	    if (strcasecmp(line+13, "SMB") == 0)
1013 		bsu->smb_recover_mode = 1;
1014 	} else if (strncmp(line,"DATA-PATH ", 10) == 0) {
1015 	    if (strcasecmp(line+10, "AMANDA") == 0)
1016 		bsu->data_path_set |= DATA_PATH_AMANDA;
1017 	    else if (strcasecmp(line+10, "DIRECTTCP") == 0)
1018 		bsu->data_path_set |= DATA_PATH_DIRECTTCP;
1019 	} else if (strncmp(line,"RECOVER-PATH ", 13) == 0) {
1020 	    if (strcasecmp(line+13, "CWD") == 0)
1021 		bsu->recover_path = RECOVER_PATH_CWD;
1022 	    else if (strcasecmp(line+13, "REMOTE") == 0)
1023 		bsu->recover_path = RECOVER_PATH_REMOTE;
1024 	} else if (strncmp(line,"AMFEATURES ", 11) == 0) {
1025 	    if (strcmp(line+11, "YES") == 0)
1026 		bsu->features = 1;
1027 	} else if (g_str_has_prefix(line, "RECOVER-DUMP-STATE-FILE ")) {
1028 	    if (g_str_equal(line + 19, "YES"))
1029 		bsu->features = 1;
1030 	} else {
1031 	    dbprintf(_("Invalid support line: %s\n"), line);
1032 	}
1033 	amfree(line);
1034     }
1035     fclose(streamout);
1036 
1037     if (bsu->data_path_set == 0)
1038 	bsu->data_path_set = DATA_PATH_AMANDA;
1039 
1040     streamerr = fdopen(supporterr, "r");
1041     if (!streamerr) {
1042 	error(_("Error opening pipe to child: %s"), strerror(errno));
1043 	/* NOTREACHED */
1044     }
1045     while((line = agets(streamerr)) != NULL) {
1046 	if (strlen(line) > 0) {
1047 	    g_ptr_array_add(*errarray, line);
1048 	    dbprintf("Application '%s': %s\n", program, line);
1049 	}
1050 	amfree(bsu);
1051     }
1052     fclose(streamerr);
1053 
1054     if (waitpid(supportpid, &status, 0) < 0) {
1055 	err = vstrallocf(_("waitpid failed: %s"), strerror(errno));
1056     } else if (!WIFEXITED(status)) {
1057 	err = vstrallocf(_("exited with signal %d"), WTERMSIG(status));
1058     } else if (WEXITSTATUS(status) != 0) {
1059 	err = vstrallocf(_("exited with status %d"), WEXITSTATUS(status));
1060     }
1061 
1062     if (err) {
1063 	g_ptr_array_add(*errarray, err);
1064 	dbprintf("Application '%s': %s\n", program, err);
1065 	amfree(bsu);
1066     }
1067     g_ptr_array_free_full(argv_ptr);
1068     amfree(cmd);
1069     return bsu;
1070 }
1071 
1072 void
run_client_script(script_t * script,execute_on_t execute_on,g_option_t * g_options,dle_t * dle)1073 run_client_script(
1074     script_t     *script,
1075     execute_on_t  execute_on,
1076     g_option_t   *g_options,
1077     dle_t	 *dle)
1078 {
1079     pid_t     scriptpid;
1080     int       scriptin, scriptout, scripterr;
1081     char     *cmd;
1082     GPtrArray *argv_ptr = g_ptr_array_new();
1083     FILE     *streamout;
1084     FILE     *streamerr;
1085     char     *line;
1086     amwait_t  wait_status;
1087     char     *command = NULL;
1088 
1089     if ((script->execute_on & execute_on) == 0)
1090 	return;
1091     if (script->execute_where != ES_CLIENT)
1092 	return;
1093 
1094     cmd = vstralloc(APPLICATION_DIR, "/", script->plugin, NULL);
1095     g_ptr_array_add(argv_ptr, stralloc(script->plugin));
1096 
1097     switch (execute_on) {
1098     case EXECUTE_ON_PRE_DLE_AMCHECK:
1099 	command = "PRE-DLE-AMCHECK";
1100 	break;
1101     case EXECUTE_ON_PRE_HOST_AMCHECK:
1102 	command = "PRE-HOST-AMCHECK";
1103 	break;
1104     case EXECUTE_ON_POST_DLE_AMCHECK:
1105 	command = "POST-DLE-AMCHECK";
1106 	break;
1107     case EXECUTE_ON_POST_HOST_AMCHECK:
1108 	command = "POST-HOST-AMCHECK";
1109 	break;
1110     case EXECUTE_ON_PRE_DLE_ESTIMATE:
1111 	command = "PRE-DLE-ESTIMATE";
1112 	break;
1113     case EXECUTE_ON_PRE_HOST_ESTIMATE:
1114 	command = "PRE-HOST-ESTIMATE";
1115 	break;
1116     case EXECUTE_ON_POST_DLE_ESTIMATE:
1117 	command = "POST-DLE-ESTIMATE";
1118 	break;
1119     case EXECUTE_ON_POST_HOST_ESTIMATE:
1120 	command = "POST-HOST-ESTIMATE";
1121 	break;
1122     case EXECUTE_ON_PRE_DLE_BACKUP:
1123 	command = "PRE-DLE-BACKUP";
1124 	break;
1125     case EXECUTE_ON_PRE_HOST_BACKUP:
1126 	command = "PRE-HOST-BACKUP";
1127 	break;
1128     case EXECUTE_ON_POST_DLE_BACKUP:
1129 	command = "POST-DLE-BACKUP";
1130 	break;
1131     case EXECUTE_ON_POST_HOST_BACKUP:
1132 	command = "POST-HOST-BACKUP";
1133 	break;
1134     case EXECUTE_ON_PRE_RECOVER:
1135 	command = "PRE-RECOVER";
1136 	break;
1137     case EXECUTE_ON_POST_RECOVER:
1138 	command = "POST-RECOVER";
1139 	break;
1140     case EXECUTE_ON_PRE_LEVEL_RECOVER:
1141 	command = "PRE-LEVEL-RECOVER";
1142 	break;
1143     case EXECUTE_ON_POST_LEVEL_RECOVER:
1144 	command = "POST-LEVEL-RECOVER";
1145 	break;
1146     case EXECUTE_ON_INTER_LEVEL_RECOVER:
1147 	command = "INTER-LEVEL-RECOVER";
1148 	break;
1149     }
1150     g_ptr_array_add(argv_ptr, stralloc(command));
1151     g_ptr_array_add(argv_ptr, stralloc("--execute-where"));
1152     g_ptr_array_add(argv_ptr, stralloc("client"));
1153 
1154     if (g_options->config) {
1155 	g_ptr_array_add(argv_ptr, stralloc("--config"));
1156 	g_ptr_array_add(argv_ptr, stralloc(g_options->config));
1157     }
1158     if (g_options->hostname) {
1159 	g_ptr_array_add(argv_ptr, stralloc("--host"));
1160 	g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
1161     }
1162     if (dle->disk) {
1163 	g_ptr_array_add(argv_ptr, stralloc("--disk"));
1164 	g_ptr_array_add(argv_ptr, stralloc(dle->disk));
1165     }
1166     if (dle->device) {
1167 	g_ptr_array_add(argv_ptr, stralloc("--device"));
1168 	g_ptr_array_add(argv_ptr, stralloc(dle->device));
1169     }
1170     if (dle->levellist) {
1171 	levellist_t levellist;
1172 	char number[NUM_STR_SIZE];
1173 	for (levellist=dle->levellist; levellist; levellist=levellist->next) {
1174 	    am_level_t *alevel = (am_level_t *)levellist->data;
1175 	    g_ptr_array_add(argv_ptr, stralloc("--level"));
1176 	    g_snprintf(number, SIZEOF(number), "%d", alevel->level);
1177 	    g_ptr_array_add(argv_ptr, stralloc(number));
1178 	}
1179     }
1180     property_add_to_argv(argv_ptr, script->property);
1181     g_ptr_array_add(argv_ptr, NULL);
1182 
1183     scriptpid = pipespawnv(cmd, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
1184 			   &scriptin, &scriptout, &scripterr,
1185 			   (char **)argv_ptr->pdata);
1186 
1187     close(scriptin);
1188 
1189     script->result = g_new0(client_script_result_t, 1);
1190     script->result->proplist =
1191 		  g_hash_table_new_full(g_str_hash, g_str_equal,
1192 					&g_free, &destroy_slist_free_full);
1193     script->result->output = g_ptr_array_new();
1194     script->result->err = g_ptr_array_new();
1195 
1196     streamout = fdopen(scriptout, "r");
1197     if (streamout) {
1198         while((line = agets(streamout)) != NULL) {
1199             dbprintf("script: %s\n", line);
1200             if (BSTRNCMP(line, "PROPERTY ") == 0) {
1201 		char *property_name, *property_value;
1202 		property_name = line + 9;
1203 		property_value = strchr(property_name,' ');
1204 		if (property_value == NULL) {
1205 		    char *msg = g_strdup_printf(
1206 					"ERROR %s: Bad output property: %s",
1207 					script->plugin, line);
1208 		    g_ptr_array_add(script->result->output, msg);
1209 		} else {
1210 		    property_t *property;
1211 
1212 		    *property_value++ = '\0';
1213 		    property_name = stralloc(property_name);
1214 		    property_value = stralloc(property_value);
1215 		    property = g_hash_table_lookup(script->result->proplist,
1216 						   property_name);
1217 		    if (!property) {
1218 			property = g_new0(property_t, 1);
1219 			g_hash_table_insert(script->result->proplist,
1220 					    property_name, property);
1221 		    }
1222 		    property->values = g_slist_append(property->values,
1223 						      property_value);
1224 		}
1225 		amfree(line);
1226             } else {
1227                 g_ptr_array_add(script->result->output, line);
1228             }
1229         }
1230     }
1231     fclose(streamout);
1232 
1233     streamerr = fdopen(scripterr, "r");
1234     if (streamerr) {
1235         while((line = agets(streamerr)) != NULL) {
1236 	    g_ptr_array_add(script->result->err,
1237 			    g_strdup_printf(_("Script '%s' command '%s': %s"),
1238 					    script->plugin, command, line));
1239 	    amfree(line);
1240 	}
1241     }
1242 
1243     waitpid(scriptpid, &wait_status, 0);
1244     if (WIFSIGNALED(wait_status)) {
1245 	g_ptr_array_add(script->result->err,
1246 			g_strdup_printf(_("Script '%s' command '%s' terminated with signal %d: see %s"),
1247 					script->plugin, command,
1248 					WTERMSIG(wait_status),
1249 					dbfn()));
1250     } else if (WIFEXITED(wait_status)) {
1251         if (WEXITSTATUS(wait_status) != 0) {
1252 	    g_ptr_array_add(script->result->err,
1253 			    g_strdup_printf(_("Script '%s' command '%s' exited with status %d: see %s"),
1254 					    script->plugin, command,
1255 					    WEXITSTATUS(wait_status),
1256 					    dbfn()));
1257         } else {
1258             /* Normal exit */
1259         }
1260     }
1261     amfree(cmd);
1262     g_ptr_array_free_full(argv_ptr);
1263 }
1264 
1265 void run_client_script_output(gpointer data, gpointer user_data);
1266 void run_client_script_output_backup(gpointer data, gpointer user_data);
1267 void run_client_script_err_amcheck(gpointer data, gpointer user_data);
1268 void run_client_script_err_estimate(gpointer data, gpointer user_data);
1269 void run_client_script_err_backup(gpointer data, gpointer user_data);
1270 void run_client_script_err_recover(gpointer data, gpointer user_data);
1271 
1272 typedef struct script_output_s {
1273     FILE  *stream;
1274     dle_t *dle;
1275 } script_output_t;
1276 
1277 void
run_client_script_output(gpointer data,gpointer user_data)1278 run_client_script_output(
1279     gpointer data,
1280     gpointer user_data)
1281 {
1282     char            *line = data;
1283     script_output_t *so   = user_data;
1284 
1285     if (line && so->stream) {
1286 	g_fprintf(so->stream, "%s\n", line);
1287     }
1288 }
1289 
1290 void
run_client_script_output_backup(gpointer data,gpointer user_data)1291 run_client_script_output_backup(
1292     gpointer data,
1293     gpointer user_data)
1294 {
1295     char            *line = data;
1296     script_output_t *so   = user_data;
1297 
1298     if (line && so->stream) {
1299 	g_fprintf(so->stream, "| %s\n", line);
1300     }
1301 }
1302 
1303 void
run_client_script_err_amcheck(gpointer data,gpointer user_data)1304 run_client_script_err_amcheck(
1305     gpointer data,
1306     gpointer user_data)
1307 {
1308     char            *line  = data;
1309     script_output_t *so    = user_data;
1310 
1311     if (line && so->stream) {
1312 	g_fprintf(so->stream, "ERROR %s\n", line);
1313     }
1314 }
1315 
1316 void
run_client_script_err_estimate(gpointer data,gpointer user_data)1317 run_client_script_err_estimate(
1318     gpointer data,
1319     gpointer user_data)
1320 {
1321     char            *line  = data;
1322     script_output_t *so    = user_data;
1323 
1324     if (line && so->stream) {
1325 	char *qdisk = quote_string(so->dle->disk);
1326 	g_fprintf(so->stream, "%s 0 WARNING \"%s\"\n", qdisk, line);
1327 	amfree(qdisk);
1328     }
1329 }
1330 
1331 void
run_client_script_err_backup(gpointer data,gpointer user_data)1332 run_client_script_err_backup(
1333     gpointer data,
1334     gpointer user_data)
1335 {
1336     char            *line  = data;
1337     script_output_t *so    = user_data;
1338 
1339     if (line && so->stream) {
1340 	g_fprintf(so->stream, "? %s\n", line);
1341     }
1342 }
1343 
1344 void
run_client_script_err_recover(gpointer data,gpointer user_data)1345 run_client_script_err_recover(
1346     gpointer data,
1347     gpointer user_data)
1348 {
1349     char            *line  = data;
1350     script_output_t *so    = user_data;
1351 
1352     if (line && so->stream) {
1353 	g_fprintf(so->stream, "%s\n", line);
1354     }
1355 }
1356 
1357 void
run_client_scripts(execute_on_t execute_on,g_option_t * g_options,dle_t * dle,FILE * streamout)1358 run_client_scripts(
1359     execute_on_t  execute_on,
1360     g_option_t   *g_options,
1361     dle_t	 *dle,
1362     FILE         *streamout)
1363 {
1364     GSList          *scriptlist;
1365     script_t        *script;
1366     GFunc            client_script_err = NULL;
1367     GFunc            client_script_out = NULL;
1368     script_output_t  so = { streamout, dle };
1369 
1370     for (scriptlist = dle->scriptlist; scriptlist != NULL;
1371 	 scriptlist = scriptlist->next) {
1372 	script = (script_t *)scriptlist->data;
1373 	run_client_script(script, execute_on, g_options, dle);
1374 	if (script->result) {
1375 	    switch (execute_on) {
1376 	    case EXECUTE_ON_PRE_DLE_AMCHECK:
1377 	    case EXECUTE_ON_PRE_HOST_AMCHECK:
1378 	    case EXECUTE_ON_POST_DLE_AMCHECK:
1379 	    case EXECUTE_ON_POST_HOST_AMCHECK:
1380 		 client_script_out = run_client_script_output;
1381 		 client_script_err = run_client_script_err_amcheck;
1382 		 break;
1383 	    case EXECUTE_ON_PRE_DLE_ESTIMATE:
1384 	    case EXECUTE_ON_PRE_HOST_ESTIMATE:
1385 	    case EXECUTE_ON_POST_DLE_ESTIMATE:
1386 	    case EXECUTE_ON_POST_HOST_ESTIMATE:
1387 		 client_script_out = run_client_script_output;
1388 		 if (am_has_feature(g_options->features,
1389 				    fe_sendsize_rep_warning)) {
1390 		     client_script_err = run_client_script_err_estimate;
1391 		 }
1392 		 break;
1393 	    case EXECUTE_ON_PRE_DLE_BACKUP:
1394 	    case EXECUTE_ON_PRE_HOST_BACKUP:
1395 	    case EXECUTE_ON_POST_DLE_BACKUP:
1396 	    case EXECUTE_ON_POST_HOST_BACKUP:
1397 		 client_script_out = run_client_script_output_backup;
1398 		 client_script_err = run_client_script_err_backup;
1399 		 break;
1400 	    case EXECUTE_ON_PRE_RECOVER:
1401 	    case EXECUTE_ON_POST_RECOVER:
1402 	    case EXECUTE_ON_PRE_LEVEL_RECOVER:
1403 	    case EXECUTE_ON_POST_LEVEL_RECOVER:
1404 	    case EXECUTE_ON_INTER_LEVEL_RECOVER:
1405 		 client_script_out = run_client_script_output;
1406 		 client_script_err = run_client_script_err_recover;
1407 	    }
1408 	    if (script->result->output) {
1409 		if (client_script_out) {
1410 		    g_ptr_array_foreach(script->result->output,
1411 					client_script_out,
1412 					&so);
1413 		}
1414 		g_ptr_array_free(script->result->output, TRUE);
1415 		script->result->output = NULL;
1416 	    }
1417 	    if (script->result->err) {
1418 		if (client_script_err != NULL) {
1419 		    g_ptr_array_foreach(script->result->err,
1420 					client_script_err,
1421 					&so);
1422 		}
1423 		g_ptr_array_free(script->result->err, TRUE);
1424 		script->result->err = NULL;
1425 	    }
1426 	}
1427     }
1428 }
1429 
1430 
1431 void
run_calcsize(char * config,char * program,char * disk,char * dirname,GSList * levels,char * file_exclude,char * file_include)1432 run_calcsize(
1433     char   *config,
1434     char   *program,
1435     char   *disk,
1436     char   *dirname,
1437     GSList *levels,
1438     char   *file_exclude,
1439     char   *file_include)
1440 {
1441     char        *cmd, *cmdline;
1442     char	*command;
1443     GPtrArray   *argv_ptr = g_ptr_array_new();
1444     char         tmppath[PATH_MAX];
1445     char         number[NUM_STR_SIZE];
1446     GSList      *alevel;
1447     guint        level;
1448     guint        i;
1449     char        *match_expr;
1450     int          pipefd = -1, nullfd = -1;
1451     pid_t        calcpid;
1452     times_t      start_time;
1453     FILE        *dumpout = NULL;
1454     int          dumpsince;
1455     char        *errmsg = NULL;
1456     char        *line = NULL;
1457     amwait_t     wait_status;
1458     int          len;
1459     char        *qdisk;
1460     amandates_t *amdp;
1461     char        *amandates_file;
1462 
1463     qdisk = quote_string(disk);
1464 
1465     amandates_file = getconf_str(CNF_AMANDATES);
1466     if(!start_amandates(amandates_file, 0)) {
1467 	char *errstr = strerror(errno);
1468 	char *errmsg = vstrallocf(_("could not open %s: %s"), amandates_file, errstr);
1469 	char *qerrmsg = quote_string(errmsg);
1470 	g_printf(_("ERROR %s\n"), qerrmsg);
1471 	amfree(qdisk);
1472 	amfree(errmsg);
1473 	amfree(qerrmsg);
1474 	return;
1475     }
1476 
1477     startclock();
1478     cmd = vstralloc(amlibexecdir, "/", "calcsize", NULL);
1479 
1480 
1481     g_ptr_array_add(argv_ptr, stralloc("calcsize"));
1482     if (config)
1483 	g_ptr_array_add(argv_ptr, stralloc(config));
1484     else
1485 	g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
1486 
1487     g_ptr_array_add(argv_ptr, stralloc(program));
1488 
1489     canonicalize_pathname(disk, tmppath);
1490     g_ptr_array_add(argv_ptr, stralloc(tmppath));
1491     canonicalize_pathname(dirname, tmppath);
1492     g_ptr_array_add(argv_ptr, stralloc(tmppath));
1493 
1494     if (file_exclude) {
1495 	g_ptr_array_add(argv_ptr, stralloc("-X"));
1496 	g_ptr_array_add(argv_ptr, stralloc(file_exclude));
1497     }
1498 
1499     if (file_include) {
1500 	g_ptr_array_add(argv_ptr, stralloc("-I"));
1501 	g_ptr_array_add(argv_ptr, stralloc(file_include));
1502     }
1503 
1504     for (alevel = levels; alevel != NULL; alevel = alevel->next) {
1505 	amdp = amandates_lookup(disk);
1506 	level = GPOINTER_TO_INT(alevel->data);
1507 	dbprintf("level: %d\n", level);
1508 	dumpsince = 0;
1509 	for (i=0; i < level; i++) {
1510 	    if (dumpsince < amdp->dates[i])
1511 		dumpsince = amdp->dates[i];
1512 	}
1513 	g_snprintf(number, SIZEOF(number), "%d", level);
1514 	g_ptr_array_add(argv_ptr, stralloc(number));
1515 	g_snprintf(number, SIZEOF(number), "%d", dumpsince);
1516 	g_ptr_array_add(argv_ptr, stralloc(number));
1517     }
1518 
1519     g_ptr_array_add(argv_ptr, NULL);
1520     command = (char *)g_ptr_array_index(argv_ptr, 0);
1521     cmdline = stralloc(command);
1522     for(i = 1; i < argv_ptr->len - 1; i++)
1523 	cmdline = vstrextend(&cmdline, " ",
1524 			     (char *)g_ptr_array_index(argv_ptr,i), NULL);
1525     dbprintf(_("running: \"%s\"\n"), cmdline);
1526     amfree(cmdline);
1527 
1528     start_time = curclock();
1529 
1530     fflush(stderr); fflush(stdout);
1531 
1532     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1533 	errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
1534 			    strerror(errno));
1535 	dbprintf("%s\n", errmsg);
1536 	goto common_exit;
1537     }
1538 
1539     calcpid = pipespawnv(cmd, STDERR_PIPE, 0,
1540 			 &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
1541     amfree(cmd);
1542 
1543     dumpout = fdopen(pipefd,"r");
1544     if (!dumpout) {
1545 	error(_("Can't fdopen: %s"), strerror(errno));
1546 	/*NOTREACHED*/
1547     }
1548 
1549     match_expr = vstralloc(" %d SIZE %lld", NULL);
1550     len = strlen(qdisk);
1551     for(; (line = agets(dumpout)) != NULL; free(line)) {
1552 	long long size_ = (long long)0;
1553 	if (line[0] == '\0' || (int)strlen(line) <= len)
1554 	    continue;
1555 	/* Don't use sscanf for qdisk because it can have a '%'. */
1556 	if (strncmp(line, qdisk, len) == 0 &&
1557 	    sscanf(line+len, match_expr, &level, &size_) == 2) {
1558 	    g_printf("%d %lld %d\n", level, size_, 1); /* write to sendsize */
1559 	    dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1560 		     qdisk, level, size_);
1561 	}
1562     }
1563     amfree(match_expr);
1564 
1565     dbprintf(_("waiting for %s %s child (pid=%d)\n"),
1566 	     command, qdisk, (int)calcpid);
1567     waitpid(calcpid, &wait_status, 0);
1568     if (WIFSIGNALED(wait_status)) {
1569 	errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1570 			    "calcsize", WTERMSIG(wait_status),
1571 			    dbfn());
1572     } else if (WIFEXITED(wait_status)) {
1573 	if (WEXITSTATUS(wait_status) != 0) {
1574 	    errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1575 				"calcsize", WEXITSTATUS(wait_status),
1576 				dbfn());
1577 	} else {
1578 	    /* Normal exit */
1579 	}
1580     } else {
1581 	errmsg = vstrallocf(_("%s got bad exit: see %s"),
1582 			    "calcsize", dbfn());
1583     }
1584 
1585     dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
1586 	     command, qdisk,
1587 	     (int)calcpid, WEXITSTATUS(wait_status));
1588 
1589     dbprintf(_(".....\n"));
1590     dbprintf(_("estimate time for %s: %s\n"),
1591 	     qdisk,
1592 	     walltime_str(timessub(curclock(), start_time)));
1593 
1594 common_exit:
1595     if (errmsg && errmsg[0] != '\0') {
1596 	char *qerrmsg = quote_string(errmsg);
1597 	dbprintf(_("errmsg is %s\n"), errmsg);
1598 	g_printf("ERROR %s\n", qerrmsg);
1599 	amfree(qerrmsg);
1600     }
1601     amfree(qdisk);
1602     amfree(errmsg);
1603     g_ptr_array_free_full(argv_ptr);
1604     amfree(cmd);
1605 }
1606 
1607 
1608 gboolean
check_access(char * filename,int mode)1609 check_access(
1610     char *	filename,
1611     int		mode)
1612 {
1613     char *noun, *adjective;
1614     char *quoted = quote_string(filename);
1615     gboolean result;
1616 
1617     if(mode == F_OK)
1618         noun = "find", adjective = "exists";
1619     else if((mode & X_OK) == X_OK)
1620 	noun = "execute", adjective = "executable";
1621     else if((mode & (W_OK|R_OK)) == (W_OK|R_OK))
1622 	noun = "read/write", adjective = "read/writable";
1623     else
1624 	noun = "access", adjective = "accessible";
1625 
1626     if(EUIDACCESS(filename, mode) == -1) {
1627 	g_printf(_("ERROR [can not %s %s: %s (ruid:%d euid:%d)\n"), noun, quoted, strerror(errno),
1628 	    (int)getuid(), (int)geteuid());
1629 	result = FALSE;
1630     } else {
1631 	g_printf(_("OK %s %s (ruid:%d euid:%d)\n"), quoted, adjective,
1632 	    (int)getuid(), (int)geteuid());
1633 	result = TRUE;
1634     }
1635     amfree(quoted);
1636     return result;
1637 }
1638 
1639 gboolean
check_file(char * filename,int mode)1640 check_file(
1641     char *	filename,
1642     int		mode)
1643 {
1644     struct stat stat_buf;
1645     char *quoted;
1646 
1647     if(!stat(filename, &stat_buf)) {
1648 	if(!S_ISREG(stat_buf.st_mode)) {
1649 	    quoted = quote_string(filename);
1650 	    g_printf(_("ERROR [%s is not a file]\n"), quoted);
1651 	    amfree(quoted);
1652 	    return FALSE;
1653 	}
1654     } else {
1655 	int save_errno = errno;
1656 	quoted = quote_string(filename);
1657 	g_printf(_("ERROR [can not stat %s: %s]\n"), quoted,
1658 		 strerror(save_errno));
1659 	amfree(quoted);
1660 	return FALSE;
1661     }
1662 
1663     return check_access(filename, mode);
1664 }
1665 
1666 gboolean
check_dir(char * dirname,int mode)1667 check_dir(
1668     char *	dirname,
1669     int		mode)
1670 {
1671     struct stat stat_buf;
1672     char *quoted;
1673     char *dir;
1674     gboolean result;
1675 
1676     if(!stat(dirname, &stat_buf)) {
1677 	if(!S_ISDIR(stat_buf.st_mode)) {
1678 	    quoted = quote_string(dirname);
1679 	    g_printf(_("ERROR [%s is not a directory]\n"), quoted);
1680 	    amfree(quoted);
1681 	    return FALSE;
1682 	}
1683     } else {
1684 	int save_errno = errno;
1685 	quoted = quote_string(dirname);
1686 	g_printf(_("ERROR [can not stat %s: %s]\n"), quoted,
1687 		 strerror(save_errno));
1688 	amfree(quoted);
1689 	return FALSE;
1690     }
1691 
1692     dir = g_strconcat(dirname, "/.", NULL);
1693     result = check_access(dir, mode);
1694     amfree(dir);
1695     return result;
1696 }
1697 
1698 gboolean
check_suid(char * filename)1699 check_suid(
1700     char *	filename)
1701 {
1702 #ifndef SINGLE_USERID
1703     struct stat stat_buf;
1704     char *quoted = quote_string(filename);
1705 
1706     if(!stat(filename, &stat_buf)) {
1707 	if(stat_buf.st_uid != 0 ) {
1708 	    g_printf(_("ERROR [%s is not owned by root]\n"), quoted);
1709 	    amfree(quoted);
1710 	    return FALSE;
1711 	}
1712 	if((stat_buf.st_mode & S_ISUID) != S_ISUID) {
1713 	    g_printf(_("ERROR [%s is not SUID root]\n"), quoted);
1714 	    amfree(quoted);
1715 	    return FALSE;
1716 	}
1717     }
1718     else {
1719 	g_printf(_("ERROR [can not stat %s: %s]\n"), quoted, strerror(errno));
1720 	amfree(quoted);
1721 	return FALSE;
1722     }
1723     amfree(quoted);
1724 #else
1725     (void)filename;	/* Quiet unused parameter warning */
1726 #endif
1727     return TRUE;
1728 }
1729 
1730 #ifndef SINGLE_USERID
1731 gboolean check_exec_for_suid_recursive(char *filename, FILE *verbose);
1732 #endif
1733 
1734 gboolean
check_exec_for_suid(char * type,char * filename,FILE * verbose,char ** my_realpath)1735 check_exec_for_suid(
1736     char *type,
1737     char *filename,
1738     FILE *verbose,
1739     char **my_realpath)
1740 {
1741 
1742 #ifndef SINGLE_USERID
1743     char tmp_realpath[PATH_MAX];
1744     *my_realpath = realpath(filename, tmp_realpath);
1745     if (!*my_realpath) {
1746 	int saved_errno = errno;
1747 	char *quoted = quote_string(filename);
1748 	if (verbose)
1749 	     g_fprintf(verbose, "ERROR [Can't find realpath for '%s': %s\n", quoted, strerror(saved_errno));
1750 	g_debug("ERROR [Can't find realpath for '%s': %s", quoted, strerror(saved_errno));
1751 	amfree(quoted);
1752 	return FALSE;
1753     }
1754     *my_realpath = g_strdup(tmp_realpath);
1755     if (!security_allow_program_as_root(type, *my_realpath, verbose)) {
1756 	return FALSE;
1757     }
1758     return check_exec_for_suid_recursive(*my_realpath, verbose);
1759 #else
1760     (void)type;
1761     *my_realpath = g_strdup(filename);
1762     (void)verbose;
1763     return TRUE;
1764 #endif
1765 }
1766 
1767 #ifndef SINGLE_USERID
1768 gboolean
check_exec_for_suid_recursive(char * filename,FILE * verbose)1769 check_exec_for_suid_recursive(
1770     char *filename,
1771     FILE *verbose)
1772 {
1773     struct stat stat_buf;
1774     char *quoted = quote_string(filename);
1775 
1776     if (lstat(filename, &stat_buf) == 0) {
1777 	char *copy_filename;
1778 	char *s;
1779 
1780 	if (stat_buf.st_uid != 0 ) {
1781 	    if (verbose)
1782 		g_fprintf(verbose, "ERROR [%s is not owned by root]\n", quoted);
1783 	    g_debug("Error: %s is not owned by root", quoted);
1784 	    amfree(quoted);
1785 	    return FALSE;
1786 	}
1787 	if (stat_buf.st_mode & S_IWOTH) {
1788 	    if (verbose)
1789 		g_fprintf(verbose, "ERROR [%s is writable by everyone]\n", quoted);
1790 	    g_debug("Error: %s is writable by everyone", quoted);
1791 	    amfree(quoted);
1792 	    return FALSE;
1793 	}
1794 	if (stat_buf.st_mode & S_IWGRP) {
1795 	    if (verbose)
1796 		g_fprintf(verbose, "ERROR [%s is writable by the group]\n", quoted);
1797 	    g_debug("Error: %s is writable by the group", quoted);
1798 	    amfree(quoted);
1799 	    return FALSE;
1800 	}
1801 	copy_filename = g_strdup(filename);
1802 	if ((s = strchr(copy_filename, '/'))) {
1803 	    *s = '\0';
1804 	    if (*copy_filename && !check_exec_for_suid_recursive(copy_filename, verbose)) {
1805 		amfree(quoted);
1806 		amfree(copy_filename);
1807 		return FALSE;
1808 	    }
1809 	}
1810 	amfree(copy_filename);
1811     }
1812     else {
1813 	if (verbose)
1814 	    g_fprintf(verbose, "ERROR [can not stat %s: %s]\n", quoted, strerror(errno));
1815 	g_debug("Error: can not stat %s: %s", quoted, strerror(errno));
1816 	amfree(quoted);
1817 	return FALSE;
1818     }
1819     amfree(quoted);
1820     return TRUE;
1821 }
1822 #endif
1823 
1824 /*
1825  * Returns the value of the first integer in a string.
1826  */
1827 
1828 double
the_num(char * str,int pos)1829 the_num(
1830     char *      str,
1831     int         pos)
1832 {
1833     char *num;
1834     int ch;
1835     double d;
1836 
1837     do {
1838 	ch = *str++;
1839 	while(ch && !isdigit(ch)) ch = *str++;
1840 	if (pos == 1) break;
1841 	pos--;
1842 	while(ch && (isdigit(ch) || ch == '.')) ch = *str++;
1843     } while (ch);
1844     num = str - 1;
1845     while(isdigit(ch) || ch == '.') ch = *str++;
1846     str[-1] = '\0';
1847     d = atof(num);
1848     str[-1] = (char)ch;
1849     return d;
1850 }
1851 
1852 char *
config_errors_to_error_string(GSList * errlist)1853 config_errors_to_error_string(
1854     GSList *errlist)
1855 {
1856     char *errmsg;
1857     gboolean multiple_errors = FALSE;
1858 
1859     if (errlist) {
1860 	errmsg = (char *)errlist->data;
1861 	if (errlist->next)
1862 	    multiple_errors = TRUE;
1863     } else {
1864 	errmsg = _("(no error message)");
1865     }
1866 
1867     return vstrallocf("ERROR %s%s", errmsg,
1868 	multiple_errors? _(" (additional errors not displayed)"):"");
1869 }
1870 
1871 
1872 void
add_type_table(dmpline_t typ,amregex_t ** re_table,amregex_t * orig_re_table,GSList * normal_message,GSList * ignore_message,GSList * strange_message)1873 add_type_table(
1874     dmpline_t   typ,
1875     amregex_t **re_table,
1876     amregex_t  *orig_re_table,
1877     GSList     *normal_message,
1878     GSList     *ignore_message,
1879     GSList     *strange_message)
1880 {
1881     amregex_t *rp;
1882 
1883     for(rp = orig_re_table; rp->regex != NULL; rp++) {
1884 	if (rp->typ == typ) {
1885 	    int     found = 0;
1886 	    GSList *mes;
1887 
1888 	    for (mes = normal_message; mes != NULL; mes = mes->next) {
1889 		if (strcmp(rp->regex, (char *)mes->data) == 0)
1890 		    found = 1;
1891 	    }
1892 	    for (mes = ignore_message; mes != NULL; mes = mes->next) {
1893 		if (strcmp(rp->regex, (char *)mes->data) == 0)
1894 		    found = 1;
1895 	    }
1896 	    for (mes = strange_message; mes != NULL; mes = mes->next) {
1897 		if (strcmp(rp->regex, (char *)mes->data) == 0)
1898 		    found = 1;
1899 	    }
1900 	    if (found == 0) {
1901 		(*re_table)->regex   = rp->regex;
1902 		(*re_table)->srcline = rp->srcline;
1903 		(*re_table)->scale   = rp->scale;
1904 		(*re_table)->field   = rp->field;
1905 		(*re_table)->typ     = rp->typ;
1906 		(*re_table)++;
1907 	    }
1908 	}
1909     }
1910 }
1911 
1912 void
add_list_table(dmpline_t typ,amregex_t ** re_table,GSList * message)1913 add_list_table(
1914     dmpline_t   typ,
1915     amregex_t **re_table,
1916     GSList     *message)
1917 {
1918     GSList *mes;
1919 
1920     for (mes = message; mes != NULL; mes = mes->next) {
1921 	(*re_table)->regex = (char *)mes->data;
1922 	(*re_table)->srcline = 0;
1923 	(*re_table)->scale   = 0;
1924 	(*re_table)->field   = 0;
1925 	(*re_table)->typ     = typ;
1926 	(*re_table)++;
1927     }
1928 }
1929 
1930 amregex_t *
build_re_table(amregex_t * orig_re_table,GSList * normal_message,GSList * ignore_message,GSList * strange_message)1931 build_re_table(
1932     amregex_t *orig_re_table,
1933     GSList    *normal_message,
1934     GSList    *ignore_message,
1935     GSList    *strange_message)
1936 {
1937     int        nb = 0;
1938     amregex_t *rp;
1939     amregex_t *re_table, *new_re_table;
1940 
1941     for(rp = orig_re_table; rp->regex != NULL; rp++) {
1942 	nb++;
1943     }
1944     nb += g_slist_length(normal_message);
1945     nb += g_slist_length(ignore_message);
1946     nb += g_slist_length(strange_message);
1947     nb ++;
1948 
1949     re_table =  new_re_table = malloc(nb * sizeof(amregex_t));
1950 
1951     /* add SIZE from orig_re_table */
1952     add_type_table(DMP_SIZE, &re_table, orig_re_table,
1953 		   normal_message, ignore_message, strange_message);
1954 
1955     /* add ignore_message */
1956     add_list_table(DMP_IGNORE, &re_table, ignore_message);
1957 
1958     /* add IGNORE from orig_re_table */
1959     add_type_table(DMP_IGNORE, &re_table, orig_re_table,
1960 		   normal_message, ignore_message, strange_message);
1961 
1962     /* add normal_message */
1963     add_list_table(DMP_NORMAL, &re_table, normal_message);
1964 
1965     /* add NORMAL from orig_re_table */
1966     add_type_table(DMP_NORMAL, &re_table, orig_re_table,
1967 		   normal_message, ignore_message, strange_message);
1968 
1969     /* add strange_message */
1970     add_list_table(DMP_STRANGE, &re_table, strange_message);
1971 
1972     /* add STRANGE from orig_re_table */
1973     add_type_table(DMP_STRANGE, &re_table, orig_re_table,
1974 		   normal_message, ignore_message, strange_message);
1975 
1976     /* Add DMP_STRANGE with NULL regex,       */
1977     /* it is not copied by previous statement */
1978     re_table->regex = NULL;
1979     re_table->srcline = 0;
1980     re_table->scale = 0;
1981     re_table->field = 0;
1982     re_table->typ = DMP_STRANGE;
1983 
1984     return new_re_table;
1985 }
1986 
1987