513d9ba7402fcd180fd71d30ad5cc31b6906479b
[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(_("Nothing found to include for disk %s\n"), quoted);
444         if (verbose && dle->include_optional == 0) {
445             g_printf(_("ERROR [Nothing found to include for disk %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 typedef struct {
721     dle_t *dle;
722     char *name;
723     proplist_t dle_proplist;
724     int verbose;
725     int good;
726 } merge_property_t;
727
728 static void
729 merge_property(
730     gpointer key_p,
731     gpointer value_p,
732     gpointer user_data_p)
733 {
734     char *property_s = key_p;
735     property_t *conf_property = value_p;
736     merge_property_t *merge_p = user_data_p;
737     property_t *dle_property = g_hash_table_lookup(merge_p->dle_proplist,
738                                                    property_s);
739     GSList *value;
740     char *qdisk = quote_string(merge_p->dle->disk);
741
742     if (dle_property) {
743         if (dle_property->priority && conf_property->priority) {
744             if (merge_p->verbose) {
745                 g_fprintf(stdout,
746                          _("ERROR %s (%s) Both server client have priority for property '%s'.\n"),
747                          qdisk, merge_p->name, property_s);
748             }
749             g_debug("ERROR %s (%s) Both server client have priority for property '%s'.", qdisk, merge_p->name, property_s);
750             merge_p->good = 0;
751             /* Use client property */
752             g_hash_table_remove(merge_p->dle_proplist, key_p);
753             g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
754         } else if (dle_property->priority) {
755             if (merge_p->verbose) {
756                 g_fprintf(stdout,
757                          _("ERROR %s (%s) Server set priority for property '%s' but client set the property.\n"),
758                          qdisk, merge_p->name, property_s);
759             }
760             g_debug("%s (%s) Server set priority for property '%s' but client set the property.", qdisk, merge_p->name, property_s);
761             /* use server property */
762         } else if (conf_property->priority) {
763             if (merge_p->verbose) {
764                 g_fprintf(stdout,
765                          _("ERROR %s (%s) Client set priority for property '%s' but server set the property.\n"),
766                          qdisk, merge_p->name, property_s);
767             }
768             g_debug("%s (%s) Client set priority for property '%s' but server set the property.", qdisk, merge_p->name, property_s);
769             /* Use client property */
770             g_hash_table_remove(merge_p->dle_proplist, key_p);
771             g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
772         } else if (!conf_property->append) {
773             if (merge_p->verbose) {
774                 g_fprintf(stdout,
775                          _("ERROR %s (%s) Both server and client set property '%s', using client value.\n"),
776                          qdisk, merge_p->name, property_s);
777             }
778             g_debug("%s (%s) Both server and client set property '%s', using client value.", qdisk, merge_p->name, property_s);
779             /* Use client property */
780             g_hash_table_remove(merge_p->dle_proplist, key_p);
781             g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
782         } else { /* merge */
783             for (value = conf_property->values; value != NULL;
784                  value = value->next) {
785                 dle_property->values = g_slist_append(dle_property->values,
786                                                       value->data);
787             }
788         }
789     } else { /* take value from conf */
790         g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
791     }
792 }
793
794 int
795 merge_properties(
796     dle_t      *dle,
797     char       *name,
798     proplist_t  dle_proplist,
799     proplist_t  conf_proplist,
800     int         verbose)
801 {
802     merge_property_t merge_p = {dle, name, dle_proplist, verbose, 1};
803
804     if (conf_proplist != NULL) {
805         g_hash_table_foreach(conf_proplist,
806                              &merge_property,
807                              &merge_p);
808     }
809
810     return merge_p.good;
811 }
812
813 int
814 merge_dles_properties(
815     dle_t *dles,
816     int verbose)
817 {
818     dle_t         *dle;
819     application_t *app;
820     GSList        *scriptlist;
821     pp_script_t   *pp_script;
822     int            good = 1;
823
824     for (dle=dles; dle != NULL; dle=dle->next) {
825         if (dle->program_is_application_api) {
826             app = NULL;
827             if (dle->application_client_name &&
828                 strlen(dle->application_client_name) > 0) {
829                 app = lookup_application(dle->application_client_name);
830                 if (!app) {
831                     char *qamname = quote_string(dle->disk);
832                     char *errmsg = vstrallocf("Application '%s' not found on client",
833                                               dle->application_client_name);
834                     char *qerrmsg = quote_string(errmsg);
835                     good = 0;
836                     if (verbose) {
837                         g_fprintf(stdout, _("ERROR %s %s\n"), qamname, qerrmsg);
838                     }
839                     g_debug("%s: %s", qamname, qerrmsg);
840                     amfree(qamname);
841                     amfree(errmsg);
842                     amfree(qerrmsg);
843                 }
844             } else {
845                 app = lookup_application(dle->program);
846             }
847             if (app) {
848                 merge_properties(dle, dle->program,
849                                  dle->application_property,
850                                  application_get_property(app),
851                                  verbose);
852             }
853         }
854         for (scriptlist = dle->scriptlist; scriptlist != NULL;
855              scriptlist = scriptlist->next) {
856             script_t *script =  scriptlist->data;
857             pp_script = NULL;
858             if (script->client_name && strlen(script->client_name) > 0) {
859                 pp_script = lookup_pp_script(script->client_name);
860                 if (!pp_script) {
861                     char *qamname = quote_string(dle->disk);
862                     char *errmsg = vstrallocf("Script '%s' not found on client",
863                                               script->client_name);
864                     char *qerrmsg = quote_string(errmsg);
865                     good = 0;
866                     if (verbose) {
867                         g_fprintf(stderr, _("ERROR %s %s\n"), qamname, qerrmsg);
868                     }
869                     g_debug("%s: %s", qamname, qerrmsg);
870                     amfree(qamname);
871                     amfree(errmsg);
872                     amfree(qerrmsg);
873                 }
874             } else {
875                 pp_script = lookup_pp_script(script->plugin);
876             }
877             if (pp_script) {
878                 merge_properties(dle, script->plugin,
879                                  script->property,
880                                  pp_script_get_property(pp_script),
881                                  verbose);
882             }
883         }
884     }
885     return good;
886 }
887
888 backup_support_option_t *
889 backup_support_option(
890     char       *program,
891     g_option_t *g_options,
892     char       *disk,
893     char       *amdevice,
894     GPtrArray **errarray)
895 {
896     pid_t   supportpid;
897     int     supportin, supportout, supporterr;
898     char   *cmd;
899     GPtrArray *argv_ptr = g_ptr_array_new();
900     FILE   *streamout;
901     FILE   *streamerr;
902     char   *line;
903     int     status;
904     char   *err = NULL;
905     backup_support_option_t *bsu;
906
907     *errarray = g_ptr_array_new();
908     cmd = vstralloc(APPLICATION_DIR, "/", program, NULL);
909     g_ptr_array_add(argv_ptr, stralloc(program));
910     g_ptr_array_add(argv_ptr, stralloc("support"));
911     if (g_options->config) {
912         g_ptr_array_add(argv_ptr, stralloc("--config"));
913         g_ptr_array_add(argv_ptr, stralloc(g_options->config));
914     }
915     if (g_options->hostname) {
916         g_ptr_array_add(argv_ptr, stralloc("--host"));
917         g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
918     }
919     if (disk) {
920         g_ptr_array_add(argv_ptr, stralloc("--disk"));
921         g_ptr_array_add(argv_ptr, stralloc(disk));
922     }
923     if (amdevice) {
924         g_ptr_array_add(argv_ptr, stralloc("--device"));
925         g_ptr_array_add(argv_ptr, stralloc(amdevice));
926     }
927     g_ptr_array_add(argv_ptr, NULL);
928
929     supporterr = fileno(stderr);
930     supportpid = pipespawnv(cmd, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
931                             &supportin, &supportout, &supporterr,
932                             (char **)argv_ptr->pdata);
933
934     aclose(supportin);
935
936     bsu = g_new0(backup_support_option_t, 1);
937     bsu->config = 1;
938     bsu->host = 1;
939     bsu->disk = 1;
940     streamout = fdopen(supportout, "r");
941     if (!streamout) {
942         error(_("Error opening pipe to child: %s"), strerror(errno));
943         /* NOTREACHED */
944     }
945     while((line = agets(streamout)) != NULL) {
946         dbprintf(_("support line: %s\n"), line);
947         if (strncmp(line,"CONFIG ", 7) == 0) {
948             if (strcmp(line+7, "YES") == 0)
949                 bsu->config = 1;
950         } else if (strncmp(line,"HOST ", 5) == 0) {
951             if (strcmp(line+5, "YES") == 0)
952             bsu->host = 1;
953         } else if (strncmp(line,"DISK ", 5) == 0) {
954             if (strcmp(line+5, "YES") == 0)
955                 bsu->disk = 1;
956         } else if (strncmp(line,"INDEX-LINE ", 11) == 0) {
957             if (strcmp(line+11, "YES") == 0)
958                 bsu->index_line = 1;
959         } else if (strncmp(line,"INDEX-XML ", 10) == 0) {
960             if (strcmp(line+10, "YES") == 0)
961                 bsu->index_xml = 1;
962         } else if (strncmp(line,"MESSAGE-LINE ", 13) == 0) {
963             if (strcmp(line+13, "YES") == 0)
964                 bsu->message_line = 1;
965         } else if (strncmp(line,"MESSAGE-XML ", 12) == 0) {
966             if (strcmp(line+12, "YES") == 0)
967                 bsu->message_xml = 1;
968         } else if (strncmp(line,"RECORD ", 7) == 0) {
969             if (strcmp(line+7, "YES") == 0)
970                 bsu->record = 1;
971         } else if (strncmp(line,"INCLUDE-FILE ", 13) == 0) {
972             if (strcmp(line+13, "YES") == 0)
973                 bsu->include_file = 1;
974         } else if (strncmp(line,"INCLUDE-LIST ", 13) == 0) {
975             if (strcmp(line+13, "YES") == 0)
976                 bsu->include_list = 1;
977         } else if (strncmp(line,"INCLUDE-LIST-GLOB ", 17) == 0) {
978             if (strcmp(line+17, "YES") == 0)
979                 bsu->include_list_glob = 1;
980         } else if (strncmp(line,"INCLUDE-OPTIONAL ", 17) == 0) {
981             if (strcmp(line+17, "YES") == 0)
982                 bsu->include_optional = 1;
983         } else if (strncmp(line,"EXCLUDE-FILE ", 13) == 0) {
984             if (strcmp(line+13, "YES") == 0)
985                 bsu->exclude_file = 1;
986         } else if (strncmp(line,"EXCLUDE-LIST ", 13) == 0) {
987             if (strcmp(line+13, "YES") == 0)
988                 bsu->exclude_list = 1;
989         } else if (strncmp(line,"EXCLUDE-LIST-GLOB ", 17) == 0) {
990             if (strcmp(line+17, "YES") == 0)
991                 bsu->exclude_list_glob = 1;
992         } else if (strncmp(line,"EXCLUDE-OPTIONAL ", 17) == 0) {
993             if (strcmp(line+17, "YES") == 0)
994                 bsu->exclude_optional = 1;
995         } else if (strncmp(line,"COLLECTION ", 11) == 0) {
996             if (strcmp(line+11, "YES") == 0)
997                 bsu->collection = 1;
998         } else if (strncmp(line,"CALCSIZE ", 9) == 0) {
999             if (strcmp(line+9, "YES") == 0)
1000                 bsu->calcsize = 1;
1001         } else if (strncmp(line,"CLIENT-ESTIMATE ", 16) == 0) {
1002             if (strcmp(line+16, "YES") == 0)
1003                 bsu->client_estimate = 1;
1004         } else if (strncmp(line,"MULTI-ESTIMATE ", 15) == 0) {
1005             if (strcmp(line+15, "YES") == 0)
1006                 bsu->multi_estimate = 1;
1007         } else if (strncmp(line,"MAX-LEVEL ", 10) == 0) {
1008             bsu->max_level  = atoi(line+10);
1009         } else if (strncmp(line,"RECOVER-MODE ", 13) == 0) {
1010             if (strcasecmp(line+13, "SMB") == 0)
1011                 bsu->smb_recover_mode = 1;
1012         } else if (strncmp(line,"DATA-PATH ", 10) == 0) {
1013             if (strcasecmp(line+10, "AMANDA") == 0)
1014                 bsu->data_path_set |= DATA_PATH_AMANDA;
1015             else if (strcasecmp(line+10, "DIRECTTCP") == 0)
1016                 bsu->data_path_set |= DATA_PATH_DIRECTTCP;
1017         } else if (strncmp(line,"RECOVER-PATH ", 13) == 0) {
1018             if (strcasecmp(line+13, "CWD") == 0)
1019                 bsu->recover_path = RECOVER_PATH_CWD;
1020             else if (strcasecmp(line+13, "REMOTE") == 0)
1021                 bsu->recover_path = RECOVER_PATH_REMOTE;
1022         } else if (strncmp(line,"AMFEATURES ", 11) == 0) {
1023             if (strcmp(line+11, "YES") == 0)
1024                 bsu->features = 1;
1025         } else {
1026             dbprintf(_("Invalid support line: %s\n"), line);
1027         }
1028         amfree(line);
1029     }
1030     fclose(streamout);
1031
1032     if (bsu->data_path_set == 0)
1033         bsu->data_path_set = DATA_PATH_AMANDA;
1034
1035     streamerr = fdopen(supporterr, "r");
1036     if (!streamerr) {
1037         error(_("Error opening pipe to child: %s"), strerror(errno));
1038         /* NOTREACHED */
1039     }
1040     while((line = agets(streamerr)) != NULL) {
1041         if (strlen(line) > 0) {
1042             g_ptr_array_add(*errarray, line);
1043             dbprintf("Application '%s': %s\n", program, line);
1044         }
1045         amfree(bsu);
1046     }
1047     fclose(streamerr);
1048
1049     if (waitpid(supportpid, &status, 0) < 0) {
1050         err = vstrallocf(_("waitpid failed: %s"), strerror(errno));
1051     } else if (!WIFEXITED(status)) {
1052         err = vstrallocf(_("exited with signal %d"), WTERMSIG(status));
1053     } else if (WEXITSTATUS(status) != 0) {
1054         err = vstrallocf(_("exited with status %d"), WEXITSTATUS(status));
1055     }
1056
1057     if (err) {
1058         g_ptr_array_add(*errarray, err);
1059         dbprintf("Application '%s': %s\n", program, err);
1060         amfree(bsu);
1061     }
1062     g_ptr_array_free_full(argv_ptr);
1063     amfree(cmd);
1064     return bsu;
1065 }
1066
1067 void
1068 run_client_script(
1069     script_t     *script,
1070     execute_on_t  execute_on,
1071     g_option_t   *g_options,
1072     dle_t        *dle)
1073 {
1074     pid_t     scriptpid;
1075     int       scriptin, scriptout, scripterr;
1076     char     *cmd;
1077     GPtrArray *argv_ptr = g_ptr_array_new();
1078     FILE     *streamout;
1079     FILE     *streamerr;
1080     char     *line;
1081     amwait_t  wait_status;
1082     char     *command = NULL;
1083
1084     if ((script->execute_on & execute_on) == 0)
1085         return;
1086     if (script->execute_where != ES_CLIENT)
1087         return;
1088
1089     cmd = vstralloc(APPLICATION_DIR, "/", script->plugin, NULL);
1090     g_ptr_array_add(argv_ptr, stralloc(script->plugin));
1091
1092     switch (execute_on) {
1093     case EXECUTE_ON_PRE_DLE_AMCHECK:
1094         command = "PRE-DLE-AMCHECK";
1095         break;
1096     case EXECUTE_ON_PRE_HOST_AMCHECK:
1097         command = "PRE-HOST-AMCHECK";
1098         break;
1099     case EXECUTE_ON_POST_DLE_AMCHECK:
1100         command = "POST-DLE-AMCHECK";
1101         break;
1102     case EXECUTE_ON_POST_HOST_AMCHECK:
1103         command = "POST-HOST-AMCHECK";
1104         break;
1105     case EXECUTE_ON_PRE_DLE_ESTIMATE:
1106         command = "PRE-DLE-ESTIMATE";
1107         break;
1108     case EXECUTE_ON_PRE_HOST_ESTIMATE:
1109         command = "PRE-HOST-ESTIMATE";
1110         break;
1111     case EXECUTE_ON_POST_DLE_ESTIMATE:
1112         command = "POST-DLE-ESTIMATE";
1113         break;
1114     case EXECUTE_ON_POST_HOST_ESTIMATE:
1115         command = "POST-HOST-ESTIMATE";
1116         break;
1117     case EXECUTE_ON_PRE_DLE_BACKUP:
1118         command = "PRE-DLE-BACKUP";
1119         break;
1120     case EXECUTE_ON_PRE_HOST_BACKUP:
1121         command = "PRE-HOST-BACKUP";
1122         break;
1123     case EXECUTE_ON_POST_DLE_BACKUP:
1124         command = "POST-DLE-BACKUP";
1125         break;
1126     case EXECUTE_ON_POST_HOST_BACKUP:
1127         command = "POST-HOST-BACKUP";
1128         break;
1129     case EXECUTE_ON_PRE_RECOVER:
1130         command = "PRE-RECOVER";
1131         break;
1132     case EXECUTE_ON_POST_RECOVER:
1133         command = "POST-RECOVER";
1134         break;
1135     case EXECUTE_ON_PRE_LEVEL_RECOVER:
1136         command = "PRE-LEVEL-RECOVER";
1137         break;
1138     case EXECUTE_ON_POST_LEVEL_RECOVER:
1139         command = "POST-LEVEL-RECOVER";
1140         break;
1141     case EXECUTE_ON_INTER_LEVEL_RECOVER:
1142         command = "INTER-LEVEL-RECOVER";
1143         break;
1144     }
1145     g_ptr_array_add(argv_ptr, stralloc(command));
1146     g_ptr_array_add(argv_ptr, stralloc("--execute-where"));
1147     g_ptr_array_add(argv_ptr, stralloc("client"));
1148
1149     if (g_options->config) {
1150         g_ptr_array_add(argv_ptr, stralloc("--config"));
1151         g_ptr_array_add(argv_ptr, stralloc(g_options->config));
1152     }
1153     if (g_options->hostname) {
1154         g_ptr_array_add(argv_ptr, stralloc("--host"));
1155         g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
1156     }
1157     if (dle->disk) {
1158         g_ptr_array_add(argv_ptr, stralloc("--disk"));
1159         g_ptr_array_add(argv_ptr, stralloc(dle->disk));
1160     }
1161     if (dle->device) {
1162         g_ptr_array_add(argv_ptr, stralloc("--device"));
1163         g_ptr_array_add(argv_ptr, stralloc(dle->device));
1164     }
1165     if (dle->levellist) {
1166         levellist_t levellist;
1167         char number[NUM_STR_SIZE];
1168         for (levellist=dle->levellist; levellist; levellist=levellist->next) {
1169             level_t *alevel = (level_t *)levellist->data;
1170             g_ptr_array_add(argv_ptr, stralloc("--level"));
1171             g_snprintf(number, SIZEOF(number), "%d", alevel->level);
1172             g_ptr_array_add(argv_ptr, stralloc(number));
1173         }
1174     }
1175     property_add_to_argv(argv_ptr, script->property);
1176     g_ptr_array_add(argv_ptr, NULL);
1177
1178     scriptpid = pipespawnv(cmd, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
1179                            &scriptin, &scriptout, &scripterr,
1180                            (char **)argv_ptr->pdata);
1181
1182     close(scriptin);
1183
1184     script->result = g_new0(client_script_result_t, 1);
1185     script->result->proplist =
1186                   g_hash_table_new_full(g_str_hash, g_str_equal,
1187                                         &g_free, &destroy_slist_free_full);
1188     script->result->output = g_ptr_array_new();
1189     script->result->err = g_ptr_array_new();
1190
1191     streamout = fdopen(scriptout, "r");
1192     if (streamout) {
1193         while((line = agets(streamout)) != NULL) {
1194             dbprintf("script: %s\n", line);
1195             if (BSTRNCMP(line, "PROPERTY ") == 0) {
1196                 char *property_name, *property_value;
1197                 property_name = line + 9;
1198                 property_value = strchr(property_name,' ');
1199                 if (property_value == NULL) {
1200                     char *msg = g_strdup_printf(
1201                                         "ERROR %s: Bad output property: %s",
1202                                         script->plugin, line);
1203                     g_ptr_array_add(script->result->output, msg);
1204                 } else {
1205                     property_t *property;
1206
1207                     *property_value++ = '\0';
1208                     property_name = stralloc(property_name);
1209                     property_value = stralloc(property_value);
1210                     property = g_hash_table_lookup(script->result->proplist,
1211                                                    property_name);
1212                     if (!property) {
1213                         property = g_new0(property_t, 1);
1214                         g_hash_table_insert(script->result->proplist,
1215                                             property_name, property);
1216                     }
1217                     property->values = g_slist_append(property->values,
1218                                                       property_value);
1219                 }
1220                 amfree(line);
1221             } else {
1222                 g_ptr_array_add(script->result->output, line);
1223             }
1224         }
1225     }
1226     fclose(streamout);
1227
1228     streamerr = fdopen(scripterr, "r");
1229     if (streamerr) {
1230         while((line = agets(streamerr)) != NULL) {
1231             g_ptr_array_add(script->result->err,
1232                             g_strdup_printf(_("Script '%s' command '%s': %s"),
1233                                             script->plugin, command, line));
1234             amfree(line);
1235         }
1236     }
1237
1238     waitpid(scriptpid, &wait_status, 0);
1239     if (WIFSIGNALED(wait_status)) {
1240         g_ptr_array_add(script->result->err,
1241                         g_strdup_printf(_("Script '%s' command '%s' terminated with signal %d: see %s"),
1242                                         script->plugin, command,
1243                                         WTERMSIG(wait_status),
1244                                         dbfn()));
1245     } else if (WIFEXITED(wait_status)) {
1246         if (WEXITSTATUS(wait_status) != 0) {
1247             g_ptr_array_add(script->result->err,
1248                             g_strdup_printf(_("Script '%s' command '%s' exited with status %d: see %s"),
1249                                             script->plugin, command,
1250                                             WEXITSTATUS(wait_status),
1251                                             dbfn()));
1252         } else {
1253             /* Normal exit */
1254         }
1255     }
1256     amfree(cmd);
1257     g_ptr_array_free_full(argv_ptr);
1258 }
1259
1260 void run_client_script_output(gpointer data, gpointer user_data);
1261 void run_client_script_err_amcheck(gpointer data, gpointer user_data);
1262 void run_client_script_err_estimate(gpointer data, gpointer user_data);
1263 void run_client_script_err_backup(gpointer data, gpointer user_data);
1264 void run_client_script_err_recover(gpointer data, gpointer user_data);
1265
1266 typedef struct script_output_s {
1267     FILE  *stream;
1268     dle_t *dle;
1269 } script_output_t;
1270
1271 void
1272 run_client_script_output(
1273     gpointer data,
1274     gpointer user_data)
1275 {
1276     char            *line = data;
1277     script_output_t *so   = user_data;
1278
1279     if (line && so->stream) {
1280         g_fprintf(so->stream, "%s\n", line);
1281     }
1282 }
1283
1284 void
1285 run_client_script_err_amcheck(
1286     gpointer data,
1287     gpointer user_data)
1288 {
1289     char            *line  = data;
1290     script_output_t *so    = user_data;
1291
1292     if (line && so->stream) {
1293         g_fprintf(so->stream, "ERROR %s\n", line);
1294     }
1295 }
1296
1297 void
1298 run_client_script_err_estimate(
1299     gpointer data,
1300     gpointer user_data)
1301 {
1302     char            *line  = data;
1303     script_output_t *so    = user_data;
1304
1305     if (line && so->stream) {
1306         char *qdisk = quote_string(so->dle->disk);
1307         g_fprintf(so->stream, "%s 0 WARNING \"%s\"\n", qdisk, line);
1308         amfree(qdisk);
1309     }
1310 }
1311
1312 void
1313 run_client_script_err_backup(
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         g_fprintf(so->stream, "? %s\n", line);
1322     }
1323 }
1324
1325 void
1326 run_client_script_err_recover(
1327     gpointer data,
1328     gpointer user_data)
1329 {
1330     char            *line  = data;
1331     script_output_t *so    = user_data;
1332
1333     if (line && so->stream) {
1334         g_fprintf(so->stream, "%s\n", line);
1335     }
1336 }
1337
1338 void
1339 run_client_scripts(
1340     execute_on_t  execute_on,
1341     g_option_t   *g_options,
1342     dle_t        *dle,
1343     FILE         *streamout)
1344 {
1345     GSList          *scriptlist;
1346     script_t        *script;
1347     GFunc            client_script_err = NULL;
1348     script_output_t  so = { streamout, dle };
1349
1350     for (scriptlist = dle->scriptlist; scriptlist != NULL;
1351          scriptlist = scriptlist->next) {
1352         script = (script_t *)scriptlist->data;
1353         run_client_script(script, execute_on, g_options, dle);
1354         if (script->result && script->result->output) {
1355             g_ptr_array_foreach(script->result->output,
1356                                 run_client_script_output,
1357                                 &so);
1358             g_ptr_array_free(script->result->output, TRUE);
1359             script->result->output = NULL;
1360         }
1361         if (script->result && script->result->err) {
1362             switch (execute_on) {
1363             case EXECUTE_ON_PRE_DLE_AMCHECK:
1364             case EXECUTE_ON_PRE_HOST_AMCHECK:
1365             case EXECUTE_ON_POST_DLE_AMCHECK:
1366             case EXECUTE_ON_POST_HOST_AMCHECK:
1367                  client_script_err = run_client_script_err_amcheck;
1368                  break;
1369             case EXECUTE_ON_PRE_DLE_ESTIMATE:
1370             case EXECUTE_ON_PRE_HOST_ESTIMATE:
1371             case EXECUTE_ON_POST_DLE_ESTIMATE:
1372             case EXECUTE_ON_POST_HOST_ESTIMATE:
1373                  if (am_has_feature(g_options->features,
1374                                     fe_sendsize_rep_warning)) {
1375                      client_script_err = run_client_script_err_estimate;
1376                  }
1377                  break;
1378             case EXECUTE_ON_PRE_DLE_BACKUP:
1379             case EXECUTE_ON_PRE_HOST_BACKUP:
1380             case EXECUTE_ON_POST_DLE_BACKUP:
1381             case EXECUTE_ON_POST_HOST_BACKUP:
1382                  client_script_err = run_client_script_err_backup;
1383                  break;
1384             case EXECUTE_ON_PRE_RECOVER:
1385             case EXECUTE_ON_POST_RECOVER:
1386             case EXECUTE_ON_PRE_LEVEL_RECOVER:
1387             case EXECUTE_ON_POST_LEVEL_RECOVER:
1388             case EXECUTE_ON_INTER_LEVEL_RECOVER:
1389                  client_script_err = run_client_script_err_recover;
1390             }
1391             if (client_script_err != NULL) {
1392                 g_ptr_array_foreach(script->result->err,
1393                                     client_script_err,
1394                                     &so);
1395             }
1396             g_ptr_array_free(script->result->err, TRUE);
1397             script->result->err = NULL;
1398         }
1399     }
1400 }
1401
1402
1403 void
1404 run_calcsize(
1405     char   *config,
1406     char   *program,
1407     char   *disk,
1408     char   *dirname,
1409     GSList *levels,
1410     char   *file_exclude,
1411     char   *file_include)
1412 {
1413     char        *cmd, *cmdline;
1414     char        *command;
1415     GPtrArray   *argv_ptr = g_ptr_array_new();
1416     char         tmppath[PATH_MAX];
1417     char         number[NUM_STR_SIZE];
1418     GSList      *alevel;
1419     guint        level;
1420     guint        i;
1421     char        *match_expr;
1422     int          pipefd = -1, nullfd = -1;
1423     pid_t        calcpid;
1424     times_t      start_time;
1425     FILE        *dumpout = NULL;
1426     int          dumpsince;
1427     char        *errmsg = NULL;
1428     char        *line = NULL;
1429     amwait_t     wait_status;
1430     int          len;
1431     char        *qdisk;
1432     amandates_t *amdp;
1433     char        *amandates_file;
1434
1435     qdisk = quote_string(disk);
1436
1437     amandates_file = getconf_str(CNF_AMANDATES);
1438     if(!start_amandates(amandates_file, 0)) {
1439         char *errstr = strerror(errno);
1440         char *errmsg = vstrallocf(_("could not open %s: %s"), amandates_file, errstr);
1441         char *qerrmsg = quote_string(errmsg);
1442         g_printf(_("ERROR %s\n"), qerrmsg);
1443         amfree(qdisk);
1444         amfree(errmsg);
1445         amfree(qerrmsg);
1446         return;
1447     }
1448
1449     startclock();
1450     cmd = vstralloc(amlibexecdir, "/", "calcsize", NULL);
1451
1452
1453     g_ptr_array_add(argv_ptr, stralloc("calcsize"));
1454     if (config)
1455         g_ptr_array_add(argv_ptr, stralloc(config));
1456     else
1457         g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
1458
1459     g_ptr_array_add(argv_ptr, stralloc(program));
1460
1461     canonicalize_pathname(disk, tmppath);
1462     g_ptr_array_add(argv_ptr, stralloc(tmppath));
1463     canonicalize_pathname(dirname, tmppath);
1464     g_ptr_array_add(argv_ptr, stralloc(tmppath));
1465
1466     if (file_exclude) {
1467         g_ptr_array_add(argv_ptr, stralloc("-X"));
1468         g_ptr_array_add(argv_ptr, stralloc(file_exclude));
1469     }
1470
1471     if (file_include) {
1472         g_ptr_array_add(argv_ptr, stralloc("-I"));
1473         g_ptr_array_add(argv_ptr, stralloc(file_include));
1474     }
1475
1476     for (alevel = levels; alevel != NULL; alevel = alevel->next) {
1477         amdp = amandates_lookup(disk);
1478         level = GPOINTER_TO_INT(alevel->data);
1479         dbprintf("level: %d\n", level);
1480         dumpsince = 0;
1481         for (i=0; i < level; i++) {
1482             if (dumpsince < amdp->dates[i])
1483                 dumpsince = amdp->dates[i];
1484         }
1485         g_snprintf(number, SIZEOF(number), "%d", level);
1486         g_ptr_array_add(argv_ptr, stralloc(number));
1487         g_snprintf(number, SIZEOF(number), "%d", dumpsince);
1488         g_ptr_array_add(argv_ptr, stralloc(number));
1489     }
1490
1491     g_ptr_array_add(argv_ptr, NULL);
1492     command = (char *)g_ptr_array_index(argv_ptr, 0);
1493     cmdline = stralloc(command);
1494     for(i = 1; i < argv_ptr->len - 1; i++)
1495         cmdline = vstrextend(&cmdline, " ",
1496                              (char *)g_ptr_array_index(argv_ptr,i), NULL);
1497     dbprintf(_("running: \"%s\"\n"), cmdline);
1498     amfree(cmdline);
1499
1500     start_time = curclock();
1501
1502     fflush(stderr); fflush(stdout);
1503
1504     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1505         errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
1506                             strerror(errno));
1507         dbprintf("%s\n", errmsg);
1508         goto common_exit;
1509     }
1510
1511     calcpid = pipespawnv(cmd, STDERR_PIPE, 0,
1512                          &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
1513     amfree(cmd);
1514
1515     dumpout = fdopen(pipefd,"r");
1516     if (!dumpout) {
1517         error(_("Can't fdopen: %s"), strerror(errno));
1518         /*NOTREACHED*/
1519     }
1520
1521     match_expr = vstralloc(" %d SIZE %lld", NULL);
1522     len = strlen(qdisk);
1523     for(; (line = agets(dumpout)) != NULL; free(line)) {
1524         long long size_ = (long long)0;
1525         if (line[0] == '\0' || (int)strlen(line) <= len)
1526             continue;
1527         /* Don't use sscanf for qdisk because it can have a '%'. */
1528         if (strncmp(line, qdisk, len) == 0 &&
1529             sscanf(line+len, match_expr, &level, &size_) == 2) {
1530             g_printf("%d %lld %d\n", level, size_, 1); /* write to sendsize */
1531             dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1532                      qdisk, level, size_);
1533         }
1534     }
1535     amfree(match_expr);
1536
1537     dbprintf(_("waiting for %s %s child (pid=%d)\n"),
1538              command, qdisk, (int)calcpid);
1539     waitpid(calcpid, &wait_status, 0);
1540     if (WIFSIGNALED(wait_status)) {
1541         errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1542                             "calcsize", WTERMSIG(wait_status),
1543                             dbfn());
1544     } else if (WIFEXITED(wait_status)) {
1545         if (WEXITSTATUS(wait_status) != 0) {
1546             errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1547                                 "calcsize", WEXITSTATUS(wait_status),
1548                                 dbfn());
1549         } else {
1550             /* Normal exit */
1551         }
1552     } else {
1553         errmsg = vstrallocf(_("%s got bad exit: see %s"),
1554                             "calcsize", dbfn());
1555     }
1556
1557     dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
1558              command, qdisk,
1559              (int)calcpid, WEXITSTATUS(wait_status));
1560
1561     dbprintf(_(".....\n"));
1562     dbprintf(_("estimate time for %s: %s\n"),
1563              qdisk,
1564              walltime_str(timessub(curclock(), start_time)));
1565
1566 common_exit:
1567     if (errmsg && errmsg[0] != '\0') {
1568         char *qerrmsg = quote_string(errmsg);
1569         dbprintf(_("errmsg is %s\n"), errmsg);
1570         g_printf("ERROR %s\n", qerrmsg);
1571         amfree(qerrmsg);
1572     }
1573     amfree(qdisk);
1574     amfree(errmsg);
1575     g_ptr_array_free_full(argv_ptr);
1576     amfree(cmd);
1577 }
1578
1579
1580 gboolean
1581 check_access(
1582     char *      filename,
1583     int         mode)
1584 {
1585     char *noun, *adjective;
1586     char *quoted = quote_string(filename);
1587
1588     if(mode == F_OK)
1589         noun = "find", adjective = "exists";
1590     else if((mode & X_OK) == X_OK)
1591         noun = "execute", adjective = "executable";
1592     else if((mode & (W_OK|R_OK)) == (W_OK|R_OK))
1593         noun = "read/write", adjective = "read/writable";
1594     else 
1595         noun = "access", adjective = "accessible";
1596
1597     if(access(filename, mode) == -1) {
1598         g_printf(_("ERROR [can not %s %s: %s]\n"), noun, quoted, strerror(errno));
1599         amfree(quoted);
1600         return FALSE;
1601     } else {
1602         g_printf(_("OK %s %s\n"), quoted, adjective);
1603     }
1604     amfree(quoted);
1605     return TRUE;
1606 }
1607
1608 gboolean
1609 check_file(
1610     char *      filename,
1611     int         mode)
1612 {
1613     struct stat stat_buf;
1614     char *quoted;
1615
1616     if(!stat(filename, &stat_buf)) {
1617         if(!S_ISREG(stat_buf.st_mode)) {
1618             quoted = quote_string(filename);
1619             g_printf(_("ERROR [%s is not a file]\n"), quoted);
1620             amfree(quoted);
1621             return FALSE;
1622         }
1623     } else {
1624         int save_errno = errno;
1625         quoted = quote_string(filename);
1626         g_printf(_("ERROR [can not stat %s: %s]\n"), quoted,
1627                  strerror(save_errno));
1628         amfree(quoted);
1629         return FALSE;
1630     }
1631     if (getuid() == geteuid()) {
1632         return check_access(filename, mode);
1633     } else {
1634         quoted = quote_string(filename);
1635         g_printf("OK %s\n", quoted);
1636         amfree(quoted);
1637     }
1638     return TRUE;
1639 }
1640
1641 gboolean
1642 check_dir(
1643     char *      dirname,
1644     int         mode)
1645 {
1646     struct stat stat_buf;
1647     char *quoted;
1648     char *dir;
1649
1650     if(!stat(dirname, &stat_buf)) {
1651         if(!S_ISDIR(stat_buf.st_mode)) {
1652             quoted = quote_string(dirname);
1653             g_printf(_("ERROR [%s is not a directory]\n"), quoted);
1654             amfree(quoted);
1655             return FALSE;
1656         }
1657     } else {
1658         int save_errno = errno;
1659         quoted = quote_string(dirname);
1660         g_printf(_("ERROR [can not stat %s: %s]\n"), quoted,
1661                  strerror(save_errno));
1662         amfree(quoted);
1663         return FALSE;
1664     }
1665     if (getuid() == geteuid()) {
1666         gboolean  result;
1667         dir = stralloc2(dirname, "/.");
1668         result = check_access(dir, mode);
1669         amfree(dir);
1670         return result;
1671     } else {
1672         quoted = quote_string(dirname);
1673         g_printf("OK %s\n", quoted);
1674         amfree(quoted);
1675     }
1676     return TRUE;
1677 }
1678
1679 gboolean
1680 check_suid(
1681     char *      filename)
1682 {
1683 #ifndef SINGLE_USERID
1684     struct stat stat_buf;
1685     char *quoted = quote_string(filename);
1686
1687     if(!stat(filename, &stat_buf)) {
1688         if(stat_buf.st_uid != 0 ) {
1689             g_printf(_("ERROR [%s is not owned by root]\n"), quoted);
1690             amfree(quoted);
1691             return FALSE;
1692         }
1693         if((stat_buf.st_mode & S_ISUID) != S_ISUID) {
1694             g_printf(_("ERROR [%s is not SUID root]\n"), quoted);
1695             amfree(quoted);
1696             return FALSE;
1697         }
1698     }
1699     else {
1700         g_printf(_("ERROR [can not stat %s: %s]\n"), quoted, strerror(errno));
1701         amfree(quoted);
1702         return FALSE;
1703     }
1704     amfree(quoted);
1705 #else
1706     (void)filename;     /* Quiet unused parameter warning */
1707 #endif
1708     return TRUE;
1709 }
1710
1711 /*
1712  * Returns the value of the first integer in a string.
1713  */
1714
1715 double
1716 the_num(
1717     char *      str,
1718     int         pos)
1719 {
1720     char *num;
1721     int ch;
1722     double d;
1723
1724     do {
1725         ch = *str++;
1726         while(ch && !isdigit(ch)) ch = *str++;
1727         if (pos == 1) break;
1728         pos--;
1729         while(ch && (isdigit(ch) || ch == '.')) ch = *str++;
1730     } while (ch);
1731     num = str - 1;
1732     while(isdigit(ch) || ch == '.') ch = *str++;
1733     str[-1] = '\0';
1734     d = atof(num);
1735     str[-1] = (char)ch;
1736     return d;
1737 }
1738
1739
1740 char *
1741 config_errors_to_error_string(
1742     GSList *errlist)
1743 {
1744     char *errmsg;
1745     gboolean multiple_errors = FALSE;
1746
1747     if (errlist) {
1748         errmsg = (char *)errlist->data;
1749         if (errlist->next)
1750             multiple_errors = TRUE;
1751     } else {
1752         errmsg = _("(no error message)");
1753     }
1754
1755     return vstrallocf("ERROR %s%s", errmsg,
1756         multiple_errors? _(" (additional errors not displayed)"):"");
1757 }
1758
1759
1760 void
1761 add_type_table(
1762     dmpline_t   typ,
1763     amregex_t **re_table,
1764     amregex_t  *orig_re_table,
1765     GSList     *normal_message,
1766     GSList     *ignore_message,
1767     GSList     *strange_message)
1768 {
1769     amregex_t *rp;
1770
1771     for(rp = orig_re_table; rp->regex != NULL; rp++) {
1772         if (rp->typ == typ) {
1773             int     found = 0;
1774             GSList *mes;
1775
1776             for (mes = normal_message; mes != NULL; mes = mes->next) {
1777                 if (strcmp(rp->regex, (char *)mes->data) == 0)
1778                     found = 1;
1779             }
1780             for (mes = ignore_message; mes != NULL; mes = mes->next) {
1781                 if (strcmp(rp->regex, (char *)mes->data) == 0)
1782                     found = 1;
1783             }
1784             for (mes = strange_message; mes != NULL; mes = mes->next) {
1785                 if (strcmp(rp->regex, (char *)mes->data) == 0)
1786                     found = 1;
1787             }
1788             if (found == 0) {
1789                 (*re_table)->regex   = rp->regex;
1790                 (*re_table)->srcline = rp->srcline;
1791                 (*re_table)->scale   = rp->scale;
1792                 (*re_table)->field   = rp->field;
1793                 (*re_table)->typ     = rp->typ;
1794                 (*re_table)++;
1795             }
1796         }
1797     }
1798 }
1799
1800 void
1801 add_list_table(
1802     dmpline_t   typ,
1803     amregex_t **re_table,
1804     GSList     *message)
1805 {
1806     GSList *mes;
1807
1808     for (mes = message; mes != NULL; mes = mes->next) {
1809         (*re_table)->regex = (char *)mes->data;
1810         (*re_table)->srcline = 0;
1811         (*re_table)->scale   = 0;
1812         (*re_table)->field   = 0;
1813         (*re_table)->typ     = typ;
1814         (*re_table)++;
1815     }
1816 }
1817
1818 amregex_t *
1819 build_re_table(
1820     amregex_t *orig_re_table,
1821     GSList    *normal_message,
1822     GSList    *ignore_message,
1823     GSList    *strange_message)
1824 {
1825     int        nb = 0;
1826     amregex_t *rp;
1827     amregex_t *re_table, *new_re_table;
1828
1829     for(rp = orig_re_table; rp->regex != NULL; rp++) {
1830         nb++;
1831     }
1832     nb += g_slist_length(normal_message);
1833     nb += g_slist_length(ignore_message);
1834     nb += g_slist_length(strange_message);
1835     nb ++;
1836
1837     re_table =  new_re_table = malloc(nb * sizeof(amregex_t));
1838     
1839     /* add SIZE from orig_re_table */
1840     add_type_table(DMP_SIZE, &re_table, orig_re_table,
1841                    normal_message, ignore_message, strange_message);
1842
1843     /* add ignore_message */
1844     add_list_table(DMP_IGNORE, &re_table, ignore_message);
1845
1846     /* add IGNORE from orig_re_table */
1847     add_type_table(DMP_IGNORE, &re_table, orig_re_table,
1848                    normal_message, ignore_message, strange_message);
1849
1850     /* add normal_message */
1851     add_list_table(DMP_NORMAL, &re_table, normal_message);
1852
1853     /* add NORMAL from orig_re_table */
1854     add_type_table(DMP_NORMAL, &re_table, orig_re_table,
1855                    normal_message, ignore_message, strange_message);
1856
1857     /* add strange_message */
1858     add_list_table(DMP_STRANGE, &re_table, strange_message);
1859
1860     /* add STRANGE from orig_re_table */
1861     add_type_table(DMP_STRANGE, &re_table, orig_re_table,
1862                    normal_message, ignore_message, strange_message);
1863
1864     /* Add DMP_STRANGE with NULL regex,       */
1865     /* it is not copied by previous statement */
1866     re_table->regex = NULL;
1867     re_table->srcline = 0;
1868     re_table->scale = 0;
1869     re_table->field = 0;
1870     re_table->typ = DMP_STRANGE;
1871
1872     return new_re_table;
1873 }
1874