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