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