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