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