Imported Upstream version 3.3.2
[debian/amanda] / application-src / amgtar.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: amgtar.c 8888 2007-10-02 13:40:42Z martineau $
28  *
29  * send estimated backup sizes using dump
30  */
31
32 /* PROPERTY:
33  *
34  * GNUTAR-PATH     (default GNUTAR)
35  * GNUTAR-LISTDIR  (default CNF_GNUTAR_LIST_DIR)
36  * DIRECTORY       (no default, if set, the backup will be from that directory
37  *                  instead of from the --device)
38  * ONE-FILE-SYSTEM (default YES)
39  * SPARSE          (default YES)
40  * ATIME-PRESERVE  (default YES)
41  * CHECK-DEVICE    (default YES)
42  * NO-UNQUOTE      (default NO)
43  * ACLS            (default NO)
44  * SELINUX         (default NO)
45  * XATTRS          (default NO)
46  * INCLUDE-FILE
47  * INCLUDE-LIST
48  * INCLUDE-LIST-GLOB
49  * INCLUDE-OPTIONAL
50  * EXCLUDE-FILE
51  * EXCLUDE-LIST
52  * EXCLUDE-LIST-GLOB
53  * EXCLUDE-OPTIONAL
54  * NORMAL
55  * IGNORE
56  * STRANGE
57  * EXIT-HANDLING   (1=GOOD 2=BAD)
58  * TAR-BLOCKSIZE   (default does not add --blocking-factor option,
59  *                  using tar's default)
60  * VERBOSE
61  */
62
63 #include "amanda.h"
64 #include "match.h"
65 #include "pipespawn.h"
66 #include "amfeatures.h"
67 #include "clock.h"
68 #include "util.h"
69 #include "getfsent.h"
70 #include "client_util.h"
71 #include "conffile.h"
72 #include "getopt.h"
73
74 int debug_application = 1;
75 #define application_debug(i, ...) do {  \
76         if ((i) <= debug_application) { \
77             dbprintf(__VA_ARGS__);      \
78         }                               \
79 } while (0)
80
81 static amregex_t init_re_table[] = {
82   /* tar prints the size in bytes */
83   AM_SIZE_RE("^ *Total bytes written: [0-9][0-9]*", 1, 1),
84   AM_NORMAL_RE("^could not open conf file"),
85   AM_NORMAL_RE("^Elapsed time:"),
86   AM_NORMAL_RE("^Throughput"),
87   AM_IGNORE_RE(": Directory is new$"),
88   AM_IGNORE_RE(": Directory has been renamed"),
89
90   /* GNU tar 1.13.17 will print this warning when (not) backing up a
91      Unix named socket.  */
92   AM_NORMAL_RE(": socket ignored$"),
93
94   /* GNUTAR produces a few error messages when files are modified or
95      removed while it is running.  They may cause data to be lost, but
96      then they may not.  We shouldn't consider them NORMAL until
97      further investigation.  */
98   AM_NORMAL_RE(": File .* shrunk by [0-9][0-9]* bytes, padding with zeros"),
99   AM_NORMAL_RE(": Cannot add file .*: No such file or directory$"),
100   AM_NORMAL_RE(": Error exit delayed from previous errors"),
101   
102   /* catch-all: DMP_STRANGE is returned for all other lines */
103   AM_STRANGE_RE(NULL)
104 };
105 static amregex_t *re_table;
106
107 /* local functions */
108 int main(int argc, char **argv);
109
110 typedef struct application_argument_s {
111     char      *config;
112     char      *host;
113     int        message;
114     int        collection;
115     int        calcsize;
116     char      *tar_blocksize;
117     GSList    *level;
118     GSList    *command_options;
119     char      *include_list_glob;
120     char      *exclude_list_glob;
121     dle_t      dle;
122     int        argc;
123     char     **argv;
124     int        verbose;
125     int        ignore_zeros;
126 } application_argument_t;
127
128 enum { CMD_ESTIMATE, CMD_BACKUP };
129
130 static void amgtar_support(application_argument_t *argument);
131 static void amgtar_selfcheck(application_argument_t *argument);
132 static void amgtar_estimate(application_argument_t *argument);
133 static void amgtar_backup(application_argument_t *argument);
134 static void amgtar_restore(application_argument_t *argument);
135 static void amgtar_validate(application_argument_t *argument);
136 static void amgtar_build_exinclude(dle_t *dle, int verbose,
137                                    int *nb_exclude, char **file_exclude,
138                                    int *nb_include, char **file_include);
139 static char *amgtar_get_incrname(application_argument_t *argument, int level,
140                                  FILE *mesgstream, int command);
141 static void check_no_check_device(void);
142 static GPtrArray *amgtar_build_argv(application_argument_t *argument,
143                                 char *incrname, char **file_exclude,
144                                 char **file_include, int command);
145 static char *gnutar_path;
146 static char *gnutar_listdir;
147 static char *gnutar_directory;
148 static int gnutar_onefilesystem;
149 static int gnutar_atimepreserve;
150 static int gnutar_acls;
151 static int gnutar_selinux;
152 static int gnutar_xattrs;
153 static int gnutar_checkdevice;
154 static int gnutar_no_unquote;
155 static int gnutar_sparse;
156 static GSList *normal_message = NULL;
157 static GSList *ignore_message = NULL;
158 static GSList *strange_message = NULL;
159 static char   *exit_handling;
160 static int    exit_value[256];
161
162 static struct option long_options[] = {
163     {"config"          , 1, NULL,  1},
164     {"host"            , 1, NULL,  2},
165     {"disk"            , 1, NULL,  3},
166     {"device"          , 1, NULL,  4},
167     {"level"           , 1, NULL,  5},
168     {"index"           , 1, NULL,  6},
169     {"message"         , 1, NULL,  7},
170     {"collection"      , 0, NULL,  8},
171     {"record"          , 0, NULL,  9},
172     {"gnutar-path"     , 1, NULL, 10},
173     {"gnutar-listdir"  , 1, NULL, 11},
174     {"one-file-system" , 1, NULL, 12},
175     {"sparse"          , 1, NULL, 13},
176     {"atime-preserve"  , 1, NULL, 14},
177     {"check-device"    , 1, NULL, 15},
178     {"include-file"    , 1, NULL, 16},
179     {"include-list"    , 1, NULL, 17},
180     {"include-optional", 1, NULL, 18},
181     {"exclude-file"    , 1, NULL, 19},
182     {"exclude-list"    , 1, NULL, 20},
183     {"exclude-optional", 1, NULL, 21},
184     {"directory"       , 1, NULL, 22},
185     {"normal"          , 1, NULL, 23},
186     {"ignore"          , 1, NULL, 24},
187     {"strange"         , 1, NULL, 25},
188     {"exit-handling"   , 1, NULL, 26},
189     {"calcsize"        , 0, NULL, 27},
190     {"tar-blocksize"   , 1, NULL, 28},
191     {"no-unquote"      , 1, NULL, 29},
192     {"acls"            , 1, NULL, 30},
193     {"selinux"         , 1, NULL, 31},
194     {"xattrs"          , 1, NULL, 32},
195     {"command-options" , 1, NULL, 33},
196     {"include-list-glob", 1, NULL, 34},
197     {"exclude-list-glob", 1, NULL, 35},
198     {"verbose"          , 1, NULL, 36},
199     {"ignore-zeros"     , 1, NULL, 37},
200     {NULL, 0, NULL, 0}
201 };
202
203 static char *
204 escape_tar_glob(
205     char *str,
206     int  *in_argv)
207 {
208     char *result = malloc(4*strlen(str)+1);
209     char *r = result;
210     char *s;
211
212     *in_argv = 0;
213     for (s = str; *s != '\0'; s++) {
214         if (*s == '\\') {
215             char c = *(s+1);
216             if (c == '\\') {
217                 *r++ = '\\';
218                 *r++ = '\\';
219                 *r++ = '\\';
220                 s++;
221             } else if (c == '?') {
222                 *r++ = 127;
223                 s++;
224                 continue;
225             } else if (c == 'a') {
226                 *r++ = 7;
227                 s++;
228                 continue;
229             } else if (c == 'b') {
230                 *r++ = 8;
231                 s++;
232                 continue;
233             } else if (c == 'f') {
234                 *r++ = 12;
235                 s++;
236                 continue;
237             } else if (c == 'n') {
238                 *r++ = 10;
239                 s++;
240                 *in_argv = 1;
241                 continue;
242             } else if (c == 'r') {
243                 *r++ = 13;
244                 s++;
245                 *in_argv = 1;
246                 continue;
247             } else if (c == 't') {
248                 *r++ = 9;
249                 s++;
250                 continue;
251             } else if (c == 'v') {
252                 *r++ = 11;
253                 s++;
254                 continue;
255             } else if (c >= '0' && c <= '9') {
256                 char d = c-'0';
257                 s++;
258                 c = *(s+1);
259                 if (c >= '0' && c <= '9') {
260                     d = (d*8)+(c-'0');
261                     s++;
262                     c = *(s+1);
263                     if (c >= '0' && c <= '9') {
264                         d = (d*8)+(c-'0');
265                         s++;
266                     }
267                 }
268                 *r++ = d;
269                 continue;
270             } else {
271                 *r++ = '\\';
272             }
273         } else if (*s == '?') {
274             *r++ = '\\';
275             *r++ = '\\';
276         } else if (*s == '*' || *s == '[') {
277             *r++ = '\\';
278         }
279         *r++ = *s;
280     }
281     *r = '\0';
282
283     return result;
284 }
285
286
287 int
288 main(
289     int         argc,
290     char **     argv)
291 {
292     int c;
293     char *command;
294     application_argument_t argument;
295     int i;
296
297 #ifdef GNUTAR
298     gnutar_path = GNUTAR;
299 #else
300     gnutar_path = NULL;
301 #endif
302     gnutar_listdir = NULL;
303     gnutar_directory = NULL;
304     gnutar_onefilesystem = 1;
305     gnutar_atimepreserve = 1;
306     gnutar_acls = 0;
307     gnutar_selinux = 0;
308     gnutar_xattrs = 0;
309     gnutar_checkdevice = 1;
310     gnutar_sparse = 1;
311     gnutar_no_unquote = 0;
312     exit_handling = NULL;
313
314     /* initialize */
315
316     /*
317      * Configure program for internationalization:
318      *   1) Only set the message locale for now.
319      *   2) Set textdomain for all amanda related programs to "amanda"
320      *      We don't want to be forced to support dozens of message catalogs.
321      */
322     setlocale(LC_MESSAGES, "C");
323     textdomain("amanda");
324
325     if (argc < 2) {
326         printf("ERROR no command given to amgtar\n");
327         error(_("No command given to amgtar"));
328     }
329
330     /* drop root privileges */
331     if (!set_root_privs(0)) {
332         if (strcmp(argv[1], "selfcheck") == 0) {
333             printf("ERROR amgtar must be run setuid root\n");
334         }
335         error(_("amgtar must be run setuid root"));
336     }
337
338     safe_fd(3, 2);
339
340     set_pname("amgtar");
341
342     /* Don't die when child closes pipe */
343     signal(SIGPIPE, SIG_IGN);
344
345 #if defined(USE_DBMALLOC)
346     malloc_size_1 = malloc_inuse(&malloc_hist_1);
347 #endif
348
349     add_amanda_log_handler(amanda_log_stderr);
350     add_amanda_log_handler(amanda_log_syslog);
351     dbopen(DBG_SUBDIR_CLIENT);
352     startclock();
353     dbprintf(_("version %s\n"), VERSION);
354
355     config_init(CONFIG_INIT_CLIENT, NULL);
356
357     //check_running_as(RUNNING_AS_DUMPUSER_PREFERRED);
358     //root for amrecover
359     //RUNNING_AS_CLIENT_LOGIN from selfcheck, sendsize, sendbackup
360
361     /* parse argument */
362     command = argv[1];
363
364     gnutar_listdir = stralloc(getconf_str(CNF_GNUTAR_LIST_DIR));
365     argument.config     = NULL;
366     argument.host       = NULL;
367     argument.message    = 0;
368     argument.collection = 0;
369     argument.calcsize   = 0;
370     argument.tar_blocksize = NULL;
371     argument.level      = NULL;
372     argument.command_options = NULL;
373     argument.include_list_glob = NULL;
374     argument.exclude_list_glob = NULL;
375     argument.verbose = 0;
376     argument.ignore_zeros = 1;
377     init_dle(&argument.dle);
378     argument.dle.record = 0;
379
380     while (1) {
381         int option_index = 0;
382         c = getopt_long (argc, argv, "", long_options, &option_index);
383         if (c == -1) {
384             break;
385         }
386         switch (c) {
387         case 1: argument.config = stralloc(optarg);
388                 break;
389         case 2: argument.host = stralloc(optarg);
390                 break;
391         case 3: argument.dle.disk = stralloc(optarg);
392                 break;
393         case 4: argument.dle.device = stralloc(optarg);
394                 break;
395         case 5: argument.level = g_slist_append(argument.level,
396                                                 GINT_TO_POINTER(atoi(optarg)));
397                 break;
398         case 6: argument.dle.create_index = 1;
399                 break;
400         case 7: argument.message = 1;
401                 break;
402         case 8: argument.collection = 1;
403                 break;
404         case 9: argument.dle.record = 1;
405                 break;
406         case 10: gnutar_path = stralloc(optarg);
407                  break;
408         case 11: gnutar_listdir = stralloc(optarg);
409                  break;
410         case 12: if (optarg && strcasecmp(optarg, "NO") == 0)
411                      gnutar_onefilesystem = 0;
412                  else if (optarg && strcasecmp(optarg, "YES") == 0)
413                      gnutar_onefilesystem = 1;
414                  else if (strcasecmp(command, "selfcheck") == 0)
415                      printf(_("ERROR [%s: bad ONE-FILE-SYSTEM property value (%s)]\n"), get_pname(), optarg);
416                  break;
417         case 13: if (optarg && strcasecmp(optarg, "NO") == 0)
418                      gnutar_sparse = 0;
419                  else if (optarg && strcasecmp(optarg, "YES") == 0)
420                      gnutar_sparse = 1;
421                  else if (strcasecmp(command, "selfcheck") == 0)
422                      printf(_("ERROR [%s: bad SPARSE property value (%s)]\n"), get_pname(), optarg);
423                  break;
424         case 14: if (optarg && strcasecmp(optarg, "NO") == 0)
425                      gnutar_atimepreserve = 0;
426                  else if (optarg && strcasecmp(optarg, "YES") == 0)
427                      gnutar_atimepreserve = 1;
428                  else if (strcasecmp(command, "selfcheck") == 0)
429                      printf(_("ERROR [%s: bad ATIME-PRESERVE property value (%s)]\n"), get_pname(), optarg);
430                  break;
431         case 15: if (optarg && strcasecmp(optarg, "NO") == 0)
432                      gnutar_checkdevice = 0;
433                  else if (optarg && strcasecmp(optarg, "YES") == 0)
434                      gnutar_checkdevice = 1;
435                  else if (strcasecmp(command, "selfcheck") == 0)
436                      printf(_("ERROR [%s: bad CHECK-DEVICE property value (%s)]\n"), get_pname(), optarg);
437                  break;
438         case 16: if (optarg)
439                      argument.dle.include_file =
440                          append_sl(argument.dle.include_file, optarg);
441                  break;
442         case 17: if (optarg)
443                      argument.dle.include_list =
444                          append_sl(argument.dle.include_list, optarg);
445                  break;
446         case 18: argument.dle.include_optional = 1;
447                  break;
448         case 19: if (optarg)
449                      argument.dle.exclude_file =
450                          append_sl(argument.dle.exclude_file, optarg);
451                  break;
452         case 20: if (optarg)
453                      argument.dle.exclude_list =
454                          append_sl(argument.dle.exclude_list, optarg);
455                  break;
456         case 21: argument.dle.exclude_optional = 1;
457                  break;
458         case 22: gnutar_directory = stralloc(optarg);
459                  break;
460         case 23: if (optarg)
461                      normal_message = 
462                          g_slist_append(normal_message, optarg);
463                  break;
464         case 24: if (optarg)
465                      ignore_message = 
466                          g_slist_append(ignore_message, optarg);
467                  break;
468         case 25: if (optarg)
469                      strange_message = 
470                          g_slist_append(strange_message, optarg);
471                  break;
472         case 26: if (optarg)
473                      exit_handling = stralloc(optarg);
474                  break;
475         case 27: argument.calcsize = 1;
476                  break;
477         case 28: argument.tar_blocksize = stralloc(optarg);
478                  break;
479         case 29: if (optarg && strcasecmp(optarg, "NO") == 0)
480                      gnutar_no_unquote = 0;
481                  else if (optarg && strcasecmp(optarg, "YES") == 0)
482                      gnutar_no_unquote = 1;
483                  else if (strcasecmp(command, "selfcheck") == 0)
484                      printf(_("ERROR [%s: bad No_UNQUOTE property value (%s)]\n"), get_pname(), optarg);
485                  break;
486         case 30: if (optarg && strcasecmp(optarg, "YES") == 0)
487                    gnutar_acls = 1;
488                  break;
489         case 31: if (optarg && strcasecmp(optarg, "YES") == 0)
490                    gnutar_selinux = 1;
491                  break;
492         case 32: if (optarg && strcasecmp(optarg, "YES") == 0)
493                    gnutar_xattrs = 1;
494                  break;
495         case 33: argument.command_options =
496                         g_slist_append(argument.command_options,
497                                        stralloc(optarg));
498                  break;
499         case 34: if (optarg)
500                      argument.include_list_glob = stralloc(optarg);
501                  break;
502         case 35: if (optarg)
503                      argument.exclude_list_glob = stralloc(optarg);
504                  break;
505         case 36: if (optarg  && strcasecmp(optarg, "YES") == 0)
506                      argument.verbose = 1;
507                  break;
508         case 37: if (strcasecmp(optarg, "YES") != 0)
509                      argument.ignore_zeros = 0;
510                  break;
511         case ':':
512         case '?':
513                 break;
514         }
515     }
516
517     if (!argument.dle.disk && argument.dle.device)
518         argument.dle.disk = stralloc(argument.dle.device);
519     if (!argument.dle.device && argument.dle.disk)
520         argument.dle.device = stralloc(argument.dle.disk);
521
522     argument.argc = argc - optind;
523     argument.argv = argv + optind;
524
525     if (argument.config) {
526         config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
527                     argument.config);
528         dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
529     }
530
531     if (config_errors(NULL) >= CFGERR_ERRORS) {
532         g_critical(_("errors processing config file"));
533     }
534
535     re_table = build_re_table(init_re_table, normal_message, ignore_message,
536                               strange_message);
537
538     for(i=0;i<256;i++)
539         exit_value[i] = 1; /* BAD  */
540     exit_value[0] = 0;     /* GOOD */
541     exit_value[1] = 0;     /* GOOD */
542     if (exit_handling) {
543         char *s = exit_handling;
544         while (s) {
545             char *r = strchr(s, '=');
546             if (r) {
547                 int j = atoi(s);
548                 if (j >= 0 && j < 256) {
549                     r++;
550                     if (strncasecmp(r, "GOOD", 4) == 0) {
551                         exit_value[j] = 0;
552                     }
553                 }
554             }
555             s = strchr(s+1, ' ');
556         }
557     }
558
559     if (strlen(gnutar_listdir) == 0)
560         gnutar_listdir = NULL;
561
562     if (gnutar_path) {
563         dbprintf("GNUTAR-PATH %s\n", gnutar_path);
564     } else {
565         dbprintf("GNUTAR-PATH is not set\n");
566     }
567     if (gnutar_listdir) {
568             dbprintf("GNUTAR-LISTDIR %s\n", gnutar_listdir);
569     } else {
570         dbprintf("GNUTAR-LISTDIR is not set\n");
571     }
572     if (gnutar_directory) {
573         dbprintf("DIRECTORY %s\n", gnutar_directory);
574     }
575     dbprintf("ONE-FILE-SYSTEM %s\n", gnutar_onefilesystem? "yes":"no");
576     dbprintf("SPARSE %s\n", gnutar_sparse? "yes":"no");
577     dbprintf("NO-UNQUOTE %s\n", gnutar_no_unquote? "yes":"no");
578     dbprintf("ATIME-PRESERVE %s\n", gnutar_atimepreserve? "yes":"no");
579     dbprintf("ACLS %s\n", gnutar_acls? "yes":"no");
580     dbprintf("SELINUX %s\n", gnutar_selinux? "yes":"no");
581     dbprintf("XATTRS %s\n", gnutar_xattrs? "yes":"no");
582     dbprintf("CHECK-DEVICE %s\n", gnutar_checkdevice? "yes":"no");
583     {
584         amregex_t *rp;
585         for (rp = re_table; rp->regex != NULL; rp++) {
586             switch (rp->typ) {
587                 case DMP_NORMAL : dbprintf("NORMAL %s\n", rp->regex); break;
588                 case DMP_IGNORE : dbprintf("IGNORE %s\n", rp->regex); break;
589                 case DMP_STRANGE: dbprintf("STRANGE %s\n", rp->regex); break;
590                 case DMP_SIZE   : dbprintf("SIZE %s\n", rp->regex); break;
591                 case DMP_ERROR  : dbprintf("ERROR %s\n", rp->regex); break;
592             }
593         }
594     }
595
596     if (strcmp(command, "support") == 0) {
597         amgtar_support(&argument);
598     } else if (strcmp(command, "selfcheck") == 0) {
599         amgtar_selfcheck(&argument);
600     } else if (strcmp(command, "estimate") == 0) {
601         amgtar_estimate(&argument);
602     } else if (strcmp(command, "backup") == 0) {
603         amgtar_backup(&argument);
604     } else if (strcmp(command, "restore") == 0) {
605         amgtar_restore(&argument);
606     } else if (strcmp(command, "validate") == 0) {
607         amgtar_validate(&argument);
608     } else {
609         dbprintf("Unknown command `%s'.\n", command);
610         fprintf(stderr, "Unknown command `%s'.\n", command);
611         exit (1);
612     }
613     return 0;
614 }
615
616 static void
617 amgtar_support(
618     application_argument_t *argument)
619 {
620     (void)argument;
621     fprintf(stdout, "CONFIG YES\n");
622     fprintf(stdout, "HOST YES\n");
623     fprintf(stdout, "DISK YES\n");
624     fprintf(stdout, "MAX-LEVEL 399\n");
625     fprintf(stdout, "INDEX-LINE YES\n");
626     fprintf(stdout, "INDEX-XML NO\n");
627     fprintf(stdout, "MESSAGE-LINE YES\n");
628     fprintf(stdout, "MESSAGE-XML NO\n");
629     fprintf(stdout, "RECORD YES\n");
630     fprintf(stdout, "INCLUDE-FILE YES\n");
631     fprintf(stdout, "INCLUDE-LIST YES\n");
632     fprintf(stdout, "INCLUDE-LIST-GLOB YES\n");
633     fprintf(stdout, "INCLUDE-OPTIONAL YES\n");
634     fprintf(stdout, "EXCLUDE-FILE YES\n");
635     fprintf(stdout, "EXCLUDE-LIST YES\n");
636     fprintf(stdout, "EXCLUDE-LIST-GLOB YES\n");
637     fprintf(stdout, "EXCLUDE-OPTIONAL YES\n");
638     fprintf(stdout, "COLLECTION NO\n");
639     fprintf(stdout, "MULTI-ESTIMATE YES\n");
640     fprintf(stdout, "CALCSIZE YES\n");
641     fprintf(stdout, "CLIENT-ESTIMATE YES\n");
642 }
643
644 static void
645 amgtar_selfcheck(
646     application_argument_t *argument)
647 {
648     if (argument->dle.disk) {
649         char *qdisk = quote_string(argument->dle.disk);
650         fprintf(stdout, "OK disk %s\n", qdisk);
651         amfree(qdisk);
652     }
653
654     printf("OK amgtar version %s\n", VERSION);
655     amgtar_build_exinclude(&argument->dle, 1, NULL, NULL, NULL, NULL);
656
657     printf("OK amgtar\n");
658     if (gnutar_path) {
659         if (check_file(gnutar_path, X_OK)) {
660             char *gtar_version;
661             GPtrArray *argv_ptr = g_ptr_array_new();
662
663             g_ptr_array_add(argv_ptr, gnutar_path);
664             g_ptr_array_add(argv_ptr, "--version");
665             g_ptr_array_add(argv_ptr, NULL);
666
667             gtar_version = get_first_line(argv_ptr);
668             if (gtar_version) {
669                 char *gv;
670                 for (gv = gtar_version; *gv && !g_ascii_isdigit(*gv); gv++);
671                 printf("OK amgtar gtar-version %s\n", gv);
672             } else {
673                 printf(_("ERROR [Can't get %s version]\n"), gnutar_path);
674             }
675
676             g_ptr_array_free(argv_ptr, TRUE);
677             amfree(gtar_version);
678         }
679     } else {
680         printf(_("ERROR [GNUTAR program not available]\n"));
681     }
682
683     set_root_privs(1);
684     if (gnutar_listdir && strlen(gnutar_listdir) == 0)
685         gnutar_listdir = NULL;
686     if (gnutar_listdir) {
687         check_dir(gnutar_listdir, R_OK|W_OK);
688     } else {
689         printf(_("ERROR [No GNUTAR-LISTDIR]\n"));
690     }
691
692     if (gnutar_directory) {
693         check_dir(gnutar_directory, R_OK);
694     } else if (argument->dle.device) {
695         check_dir(argument->dle.device, R_OK);
696     }
697     if (argument->calcsize) {
698         char *calcsize = vstralloc(amlibexecdir, "/", "calcsize", NULL);
699         check_file(calcsize, X_OK);
700         check_suid(calcsize);
701         amfree(calcsize);
702     }
703     set_root_privs(0);
704 }
705
706 static void
707 amgtar_estimate(
708     application_argument_t *argument)
709 {
710     char      *incrname = NULL;
711     GPtrArray *argv_ptr;
712     char      *cmd = NULL;
713     int        nullfd = -1;
714     int        pipefd = -1;
715     FILE      *dumpout = NULL;
716     off_t      size = -1;
717     char       line[32768];
718     char      *errmsg = NULL;
719     char      *qerrmsg = NULL;
720     char      *qdisk;
721     amwait_t   wait_status;
722     int        tarpid;
723     amregex_t *rp;
724     times_t    start_time;
725     int        level;
726     GSList    *levels;
727     char      *file_exclude;
728     char      *file_include;
729
730     if (!argument->level) {
731         fprintf(stderr, "ERROR No level argument\n");
732         error(_("No level argument"));
733     }
734     if (!argument->dle.disk) {
735         fprintf(stderr, "ERROR No disk argument\n");
736         error(_("No disk argument"));
737     }
738     if (!argument->dle.device) {
739         fprintf(stderr, "ERROR No device argument\n");
740         error(_("No device argument"));
741     }
742
743     qdisk = quote_string(argument->dle.disk);
744
745     if (argument->calcsize) {
746         char *dirname;
747         int   nb_exclude;
748         int   nb_include;
749
750         if (gnutar_directory) {
751             dirname = gnutar_directory;
752         } else {
753             dirname = argument->dle.device;
754         }
755         amgtar_build_exinclude(&argument->dle, 1,
756                                &nb_exclude, &file_exclude,
757                                &nb_include, &file_include);
758
759         run_calcsize(argument->config, "GNUTAR", argument->dle.disk, dirname,
760                      argument->level, file_exclude, file_include);
761
762         if (argument->verbose == 0) {
763             if (file_exclude)
764                 unlink(file_exclude);
765             if (file_include)
766                 unlink(file_include);
767         }
768         return;
769     }
770
771     if (!gnutar_path) {
772         errmsg = vstrallocf(_("GNUTAR-PATH not defined"));
773         goto common_error;
774     }
775
776     if (!gnutar_listdir) {
777         errmsg = vstrallocf(_("GNUTAR-LISTDIR not defined"));
778         goto common_error;
779     }
780
781     for (levels = argument->level; levels != NULL; levels = levels->next) {
782         level = GPOINTER_TO_INT(levels->data);
783         incrname = amgtar_get_incrname(argument, level, stdout, CMD_ESTIMATE);
784         cmd = stralloc(gnutar_path);
785         argv_ptr = amgtar_build_argv(argument, incrname, &file_exclude,
786                                      &file_include, CMD_ESTIMATE);
787
788         start_time = curclock();
789
790         if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
791             errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
792                                 strerror(errno));
793             goto common_exit;
794         }
795
796         tarpid = pipespawnv(cmd, STDERR_PIPE, 1,
797                             &nullfd, &nullfd, &pipefd,
798                             (char **)argv_ptr->pdata);
799
800         dumpout = fdopen(pipefd,"r");
801         if (!dumpout) {
802             error(_("Can't fdopen: %s"), strerror(errno));
803             /*NOTREACHED*/
804         }
805
806         size = (off_t)-1;
807         while (size < 0 && (fgets(line, sizeof(line), dumpout) != NULL)) {
808             if (line[strlen(line)-1] == '\n') /* remove trailling \n */
809                 line[strlen(line)-1] = '\0';
810             if (line[0] == '\0')
811                 continue;
812             dbprintf("%s\n", line);
813             /* check for size match */
814             /*@ignore@*/
815             for(rp = re_table; rp->regex != NULL; rp++) {
816                 if(match(rp->regex, line)) {
817                     if (rp->typ == DMP_SIZE) {
818                         size = ((the_num(line, rp->field)*rp->scale+1023.0)/1024.0);
819                         if(size < 0.0)
820                             size = 1.0;             /* found on NeXT -- sigh */
821                     }
822                     break;
823                 }
824             }
825             /*@end@*/
826         }
827
828         while (fgets(line, sizeof(line), dumpout) != NULL) {
829             dbprintf("%s", line);
830         }
831
832         dbprintf(".....\n");
833         dbprintf(_("estimate time for %s level %d: %s\n"),
834                  qdisk,
835                  level,
836                  walltime_str(timessub(curclock(), start_time)));
837         if(size == (off_t)-1) {
838             errmsg = vstrallocf(_("no size line match in %s output"), cmd);
839             dbprintf(_("%s for %s\n"), errmsg, qdisk);
840             dbprintf(".....\n");
841         } else if(size == (off_t)0 && argument->level == 0) {
842             dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
843                      cmd, argument->dle.disk);
844             dbprintf(".....\n");
845         }
846         dbprintf(_("estimate size for %s level %d: %lld KB\n"),
847                  qdisk,
848                  level,
849                  (long long)size);
850
851         kill(-tarpid, SIGTERM);
852
853         dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
854         waitpid(tarpid, &wait_status, 0);
855         if (WIFSIGNALED(wait_status)) {
856             errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
857                                 cmd, WTERMSIG(wait_status), dbfn());
858         } else if (WIFEXITED(wait_status)) {
859             if (exit_value[WEXITSTATUS(wait_status)] == 1) {
860                 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
861                                     cmd, WEXITSTATUS(wait_status), dbfn());
862             } else {
863                 /* Normal exit */
864             }
865         } else {
866             errmsg = vstrallocf(_("%s got bad exit: see %s"),
867                                 cmd, dbfn());
868         }
869         dbprintf(_("after %s %s wait\n"), cmd, qdisk);
870
871 common_exit:
872         if (errmsg) {
873             dbprintf("%s", errmsg);
874             fprintf(stdout, "ERROR %s\n", errmsg);
875         }
876
877         if (incrname) {
878             unlink(incrname);
879         }
880
881         if (argument->verbose == 0) {
882             if (file_exclude)
883                 unlink(file_exclude);
884             if (file_include)
885                 unlink(file_include);
886         }
887
888         g_ptr_array_free_full(argv_ptr);
889         amfree(cmd);
890
891         aclose(nullfd);
892         afclose(dumpout);
893
894         fprintf(stdout, "%d %lld 1\n", level, (long long)size);
895     }
896     amfree(qdisk);
897     return;
898
899 common_error:
900     qerrmsg = quote_string(errmsg);
901     amfree(qdisk);
902     dbprintf("%s", errmsg);
903     fprintf(stdout, "ERROR %s\n", qerrmsg);
904     amfree(errmsg);
905     amfree(qerrmsg);
906     return;
907 }
908
909 static void
910 amgtar_backup(
911     application_argument_t *argument)
912 {
913     int         dumpin;
914     char      *cmd = NULL;
915     char      *qdisk;
916     char      *incrname;
917     char       line[32768];
918     amregex_t *rp;
919     off_t      dump_size = -1;
920     char      *type;
921     char       startchr;
922     int        dataf = 1;
923     int        mesgf = 3;
924     int        indexf = 4;
925     int        outf;
926     FILE      *mesgstream;
927     FILE      *indexstream = NULL;
928     FILE      *outstream;
929     char      *errmsg = NULL;
930     amwait_t   wait_status;
931     GPtrArray *argv_ptr;
932     int        tarpid;
933     char      *file_exclude;
934     char      *file_include;
935
936     mesgstream = fdopen(mesgf, "w");
937     if (!mesgstream) {
938         error(_("error mesgstream(%d): %s\n"), mesgf, strerror(errno));
939     }
940
941     if (!gnutar_path) {
942         error(_("GNUTAR-PATH not defined"));
943     }
944     if (!gnutar_listdir) {
945         error(_("GNUTAR-LISTDIR not defined"));
946     }
947
948     if (!argument->level) {
949         fprintf(mesgstream, "? No level argument\n");
950         error(_("No level argument"));
951     }
952     if (!argument->dle.disk) {
953         fprintf(mesgstream, "? No disk argument\n");
954         error(_("No disk argument"));
955     }
956     if (!argument->dle.device) {
957         fprintf(mesgstream, "? No device argument\n");
958         error(_("No device argument"));
959     }
960
961     qdisk = quote_string(argument->dle.disk);
962
963     incrname = amgtar_get_incrname(argument,
964                                    GPOINTER_TO_INT(argument->level->data),
965                                    mesgstream, CMD_BACKUP);
966     cmd = stralloc(gnutar_path);
967     argv_ptr = amgtar_build_argv(argument, incrname, &file_exclude,
968                                  &file_include, CMD_BACKUP);
969
970     tarpid = pipespawnv(cmd, STDIN_PIPE|STDERR_PIPE, 1,
971                         &dumpin, &dataf, &outf, (char **)argv_ptr->pdata);
972     /* close the write ends of the pipes */
973
974     aclose(dumpin);
975     aclose(dataf);
976     if (argument->dle.create_index) {
977         indexstream = fdopen(indexf, "w");
978         if (!indexstream) {
979             error(_("error indexstream(%d): %s\n"), indexf, strerror(errno));
980         }
981     }
982     outstream = fdopen(outf, "r");
983     if (!outstream) {
984         error(_("error outstream(%d): %s\n"), outf, strerror(errno));
985     }
986
987     while (fgets(line, sizeof(line), outstream) != NULL) {
988         if (line[strlen(line)-1] == '\n') /* remove trailling \n */
989             line[strlen(line)-1] = '\0';
990         if (*line == '.' && *(line+1) == '/') { /* filename */
991             if (argument->dle.create_index) {
992                 fprintf(indexstream, "%s\n", &line[1]); /* remove . */
993             }
994         } else { /* message */
995             for(rp = re_table; rp->regex != NULL; rp++) {
996                 if(match(rp->regex, line)) {
997                     break;
998                 }
999             }
1000             if(rp->typ == DMP_SIZE) {
1001                 dump_size = (off_t)((the_num(line, rp->field)* rp->scale+1023.0)/1024.0);
1002             }
1003             switch(rp->typ) {
1004             case DMP_NORMAL:
1005                 type = "normal";
1006                 startchr = '|';
1007                 break;
1008             case DMP_IGNORE:
1009                 continue;
1010             case DMP_STRANGE:
1011                 type = "strange";
1012                 startchr = '?';
1013                 break;
1014             case DMP_SIZE:
1015                 type = "size";
1016                 startchr = '|';
1017                 break;
1018             case DMP_ERROR:
1019                 type = "error";
1020                 startchr = '?';
1021                 break;
1022             default:
1023                 type = "unknown";
1024                 startchr = '!';
1025                 break;
1026             }
1027             dbprintf("%3d: %7s(%c): %s\n", rp->srcline, type, startchr, line);
1028             fprintf(mesgstream,"%c %s\n", startchr, line);
1029         }
1030     }
1031
1032     waitpid(tarpid, &wait_status, 0);
1033     if (WIFSIGNALED(wait_status)) {
1034         errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1035                             cmd, WTERMSIG(wait_status), dbfn());
1036     } else if (WIFEXITED(wait_status)) {
1037         if (exit_value[WEXITSTATUS(wait_status)] == 1) {
1038             errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1039                                 cmd, WEXITSTATUS(wait_status), dbfn());
1040         } else {
1041             /* Normal exit */
1042         }
1043     } else {
1044         errmsg = vstrallocf(_("%s got bad exit: see %s"),
1045                             cmd, dbfn());
1046     }
1047     dbprintf(_("after %s %s wait\n"), cmd, qdisk);
1048     dbprintf(_("amgtar: %s: pid %ld\n"), cmd, (long)tarpid);
1049     if (errmsg) {
1050         dbprintf("%s", errmsg);
1051         g_fprintf(mesgstream, "sendbackup: error [%s]\n", errmsg);
1052     }
1053
1054     if (!errmsg && incrname && strlen(incrname) > 4) {
1055         if (argument->dle.record) {
1056             char *nodotnew;
1057             nodotnew = stralloc(incrname);
1058             nodotnew[strlen(nodotnew)-4] = '\0';
1059             if (rename(incrname, nodotnew)) {
1060                 dbprintf(_("%s: warning [renaming %s to %s: %s]\n"),
1061                          get_pname(), incrname, nodotnew, strerror(errno));
1062                 g_fprintf(mesgstream, _("? warning [renaming %s to %s: %s]\n"),
1063                           incrname, nodotnew, strerror(errno));
1064             }
1065             amfree(nodotnew);
1066         } else {
1067             if (unlink(incrname) == -1) {
1068                 dbprintf(_("%s: warning [unlink %s: %s]\n"),
1069                          get_pname(), incrname, strerror(errno));
1070                 g_fprintf(mesgstream, _("? warning [unlink %s: %s]\n"),
1071                           incrname, strerror(errno));
1072             }
1073         }
1074     }
1075
1076     dbprintf("sendbackup: size %lld\n", (long long)dump_size);
1077     fprintf(mesgstream, "sendbackup: size %lld\n", (long long)dump_size);
1078     dbprintf("sendbackup: end\n");
1079     fprintf(mesgstream, "sendbackup: end\n");
1080
1081     if (argument->dle.create_index)
1082         fclose(indexstream);
1083
1084     fclose(mesgstream);
1085
1086     if (argument->verbose == 0) {
1087         if (file_exclude)
1088             unlink(file_exclude);
1089         if (file_include)
1090             unlink(file_include);
1091     }
1092
1093     amfree(incrname);
1094     amfree(qdisk);
1095     amfree(cmd);
1096     g_ptr_array_free_full(argv_ptr);
1097 }
1098
1099 static void
1100 amgtar_restore(
1101     application_argument_t *argument)
1102 {
1103     char       *cmd;
1104     GPtrArray  *argv_ptr = g_ptr_array_new();
1105     char      **env;
1106     int         j;
1107     char       *e;
1108     char       *include_filename = NULL;
1109     char       *exclude_filename = NULL;
1110     int         tarpid;
1111
1112     if (!gnutar_path) {
1113         error(_("GNUTAR-PATH not defined"));
1114     }
1115
1116     cmd = stralloc(gnutar_path);
1117     g_ptr_array_add(argv_ptr, stralloc(gnutar_path));
1118     g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
1119     if (gnutar_no_unquote)
1120         g_ptr_array_add(argv_ptr, stralloc("--no-unquote"));
1121     if (gnutar_acls)
1122         g_ptr_array_add(argv_ptr, stralloc("--acls"));
1123     if (gnutar_selinux)
1124         g_ptr_array_add(argv_ptr, stralloc("--selinux"));
1125     if (gnutar_xattrs)
1126         g_ptr_array_add(argv_ptr, stralloc("--xattrs"));
1127     /* ignore trailing zero blocks on input (this was the default until tar-1.21) */
1128     if (argument->ignore_zeros) {
1129         g_ptr_array_add(argv_ptr, stralloc("--ignore-zeros"));
1130     }
1131     if (argument->tar_blocksize) {
1132         g_ptr_array_add(argv_ptr, stralloc("--blocking-factor"));
1133         g_ptr_array_add(argv_ptr, stralloc(argument->tar_blocksize));
1134     }
1135     g_ptr_array_add(argv_ptr, stralloc("-xpGvf"));
1136     g_ptr_array_add(argv_ptr, stralloc("-"));
1137     if (gnutar_directory) {
1138         struct stat stat_buf;
1139         if(stat(gnutar_directory, &stat_buf) != 0) {
1140             fprintf(stderr,"can not stat directory %s: %s\n", gnutar_directory, strerror(errno));
1141             exit(1);
1142         }
1143         if (!S_ISDIR(stat_buf.st_mode)) {
1144             fprintf(stderr,"%s is not a directory\n", gnutar_directory);
1145             exit(1);
1146         }
1147         if (access(gnutar_directory, W_OK) != 0) {
1148             fprintf(stderr, "Can't write to %s: %s\n", gnutar_directory, strerror(errno));
1149             exit(1);
1150         }
1151         g_ptr_array_add(argv_ptr, stralloc("--directory"));
1152         g_ptr_array_add(argv_ptr, stralloc(gnutar_directory));
1153     }
1154
1155     g_ptr_array_add(argv_ptr, stralloc("--wildcards"));
1156     if (argument->dle.exclude_list &&
1157         argument->dle.exclude_list->nb_element == 1) {
1158         FILE      *exclude;
1159         char      *sdisk;
1160         int        in_argv;
1161         int        entry_in_exclude = 0;
1162         char       line[2*PATH_MAX];
1163         FILE      *exclude_list;
1164
1165         if (argument->dle.disk) {
1166             sdisk = sanitise_filename(argument->dle.disk);
1167         } else {
1168             sdisk = g_strdup_printf("no_dle-%d", getpid());
1169         }
1170         exclude_filename= vstralloc(AMANDA_TMPDIR, "/", "exclude-", sdisk,  NULL);
1171         exclude_list = fopen(argument->dle.exclude_list->first->name, "r");
1172
1173         exclude = fopen(exclude_filename, "w");
1174         while (fgets(line, 2*PATH_MAX, exclude_list)) {
1175             char *escaped;
1176             line[strlen(line)-1] = '\0'; /* remove '\n' */
1177             escaped = escape_tar_glob(line, &in_argv);
1178             if (in_argv) {
1179                 g_ptr_array_add(argv_ptr, "--exclude");
1180                 g_ptr_array_add(argv_ptr, escaped);
1181             } else {
1182                 fprintf(exclude,"%s\n", escaped);
1183                 entry_in_exclude++;
1184                 amfree(escaped);
1185             }
1186         }
1187         fclose(exclude);
1188         g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1189         g_ptr_array_add(argv_ptr, exclude_filename);
1190     }
1191
1192     if (argument->exclude_list_glob) {
1193         g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1194         g_ptr_array_add(argv_ptr, stralloc(argument->exclude_list_glob));
1195     }
1196
1197     {
1198         GPtrArray *argv_include = g_ptr_array_new();
1199         FILE      *include;
1200         char      *sdisk;
1201         int        in_argv;
1202         guint      i;
1203         int        entry_in_include = 0;
1204
1205         if (argument->dle.disk) {
1206             sdisk = sanitise_filename(argument->dle.disk);
1207         } else {
1208             sdisk = g_strdup_printf("no_dle-%d", getpid());
1209         }
1210         include_filename = vstralloc(AMANDA_TMPDIR, "/", "include-", sdisk,  NULL);
1211         include = fopen(include_filename, "w");
1212         if (argument->dle.include_list &&
1213             argument->dle.include_list->nb_element == 1) {
1214             char line[2*PATH_MAX];
1215             FILE *include_list = fopen(argument->dle.include_list->first->name, "r");
1216             while (fgets(line, 2*PATH_MAX, include_list)) {
1217                 char *escaped;
1218                 line[strlen(line)-1] = '\0'; /* remove '\n' */
1219                 escaped = escape_tar_glob(line, &in_argv);
1220                 if (in_argv) {
1221                     g_ptr_array_add(argv_include, escaped);
1222                 } else {
1223                     fprintf(include,"%s\n", escaped);
1224                     entry_in_include++;
1225                     amfree(escaped);
1226                 }
1227             }
1228         }
1229
1230         for (j=1; j< argument->argc; j++) {
1231             char *escaped = escape_tar_glob(argument->argv[j], &in_argv);
1232             if (in_argv) {
1233                 g_ptr_array_add(argv_include, escaped);
1234             } else {
1235                 fprintf(include,"%s\n", escaped);
1236                 entry_in_include++;
1237                 amfree(escaped);
1238             }
1239         }
1240         fclose(include);
1241
1242         if (entry_in_include) {
1243             g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1244             g_ptr_array_add(argv_ptr, include_filename);
1245         }
1246
1247         if (argument->include_list_glob) {
1248             g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1249             g_ptr_array_add(argv_ptr, stralloc(argument->include_list_glob));
1250         }
1251
1252         for (i = 0; i < argv_include->len; i++) {
1253             g_ptr_array_add(argv_ptr, (char *)g_ptr_array_index(argv_include,i));
1254         }
1255     }
1256     g_ptr_array_add(argv_ptr, NULL);
1257
1258     debug_executing(argv_ptr);
1259
1260     tarpid = fork();
1261     switch (tarpid) {
1262     case -1: error(_("%s: fork returned: %s"), get_pname(), strerror(errno));
1263     case 0:
1264         env = safe_env();
1265         become_root();
1266         execve(cmd, (char **)argv_ptr->pdata, env);
1267         e = strerror(errno);
1268         error(_("error [exec %s: %s]"), cmd, e);
1269         break;
1270     default: break;
1271     }
1272
1273     waitpid(tarpid, NULL, 0);
1274     if (argument->verbose == 0) {
1275         if (exclude_filename)
1276             unlink(exclude_filename);
1277         if (include_filename)
1278             unlink(include_filename);
1279     }
1280 }
1281
1282 static void
1283 amgtar_validate(
1284     application_argument_t *argument G_GNUC_UNUSED)
1285 {
1286     char       *cmd;
1287     GPtrArray  *argv_ptr = g_ptr_array_new();
1288     char      **env;
1289     char       *e;
1290     char        buf[32768];
1291
1292     if (!gnutar_path) {
1293         dbprintf("GNUTAR-PATH not set; Piping to /dev/null\n");
1294         fprintf(stderr,"GNUTAR-PATH not set; Piping to /dev/null\n");
1295         goto pipe_to_null;
1296     }
1297
1298     cmd = stralloc(gnutar_path);
1299     g_ptr_array_add(argv_ptr, stralloc(gnutar_path));
1300     /* ignore trailing zero blocks on input (this was the default until tar-1.21) */
1301     g_ptr_array_add(argv_ptr, stralloc("--ignore-zeros"));
1302     g_ptr_array_add(argv_ptr, stralloc("-tf"));
1303     g_ptr_array_add(argv_ptr, stralloc("-"));
1304     g_ptr_array_add(argv_ptr, NULL);
1305
1306     debug_executing(argv_ptr);
1307     env = safe_env();
1308     execve(cmd, (char **)argv_ptr->pdata, env);
1309     e = strerror(errno);
1310     dbprintf("failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
1311     fprintf(stderr,"failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
1312 pipe_to_null:
1313     while (read(0, buf, 32768) > 0) {
1314     }
1315 }
1316
1317 static void
1318 amgtar_build_exinclude(
1319     dle_t  *dle,
1320     int     verbose,
1321     int    *nb_exclude,
1322     char  **file_exclude,
1323     int    *nb_include,
1324     char  **file_include)
1325 {
1326     int n_exclude = 0;
1327     int n_include = 0;
1328     char *exclude = NULL;
1329     char *include = NULL;
1330
1331     if (dle->exclude_file) n_exclude += dle->exclude_file->nb_element;
1332     if (dle->exclude_list) n_exclude += dle->exclude_list->nb_element;
1333     if (dle->include_file) n_include += dle->include_file->nb_element;
1334     if (dle->include_list) n_include += dle->include_list->nb_element;
1335
1336     if (n_exclude > 0) exclude = build_exclude(dle, verbose);
1337     if (n_include > 0) include = build_include(dle, verbose);
1338
1339     if (nb_exclude)
1340         *nb_exclude = n_exclude;
1341     if (file_exclude)
1342         *file_exclude = exclude;
1343     else
1344         amfree(exclude);
1345
1346     if (nb_include)
1347         *nb_include = n_include;
1348     if (file_include)
1349         *file_include = include;
1350     else
1351         amfree(include);
1352 }
1353
1354 static char *
1355 amgtar_get_incrname(
1356     application_argument_t *argument,
1357     int                     level,
1358     FILE                   *mesgstream,
1359     int                     command)
1360 {
1361     char *basename = NULL;
1362     char *incrname = NULL;
1363     int   infd, outfd;
1364     ssize_t   nb;
1365     char *inputname = NULL;
1366     char *errmsg = NULL;
1367     char *buf;
1368
1369     if (gnutar_listdir) {
1370         char number[NUM_STR_SIZE];
1371         int baselevel;
1372         char *sdisk = sanitise_filename(argument->dle.disk);
1373
1374         basename = vstralloc(gnutar_listdir,
1375                              "/",
1376                              argument->host,
1377                              sdisk,
1378                              NULL);
1379         amfree(sdisk);
1380
1381         snprintf(number, SIZEOF(number), "%d", level);
1382         incrname = vstralloc(basename, "_", number, ".new", NULL);
1383         unlink(incrname);
1384
1385         /*
1386          * Open the listed incremental file from the previous level.  Search
1387          * backward until one is found.  If none are found (which will also
1388          * be true for a level 0), arrange to read from /dev/null.
1389          */
1390         baselevel = level;
1391         infd = -1;
1392         while (infd == -1) {
1393             if (--baselevel >= 0) {
1394                 snprintf(number, SIZEOF(number), "%d", baselevel);
1395                 inputname = newvstralloc(inputname,
1396                                          basename, "_", number, NULL);
1397             } else {
1398                 inputname = newstralloc(inputname, "/dev/null");
1399             }
1400             if ((infd = open(inputname, O_RDONLY)) == -1) {
1401
1402                 errmsg = vstrallocf(_("amgtar: error opening %s: %s"),
1403                                      inputname, strerror(errno));
1404                 dbprintf("%s\n", errmsg);
1405                 if (baselevel < 0) {
1406                     if (command == CMD_ESTIMATE) {
1407                         fprintf(mesgstream, "ERROR %s\n", errmsg);
1408                     } else {
1409                         fprintf(mesgstream, "? %s\n", errmsg);
1410                     }
1411                     exit(1);
1412                 }
1413                 amfree(errmsg);
1414             }
1415         }
1416
1417         /*
1418          * Copy the previous listed incremental file to the new one.
1419          */
1420         if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
1421             errmsg = vstrallocf(_("error opening %s: %s"),
1422                                  incrname, strerror(errno));
1423             dbprintf("%s\n", errmsg);
1424             if (command == CMD_ESTIMATE) {
1425                 fprintf(mesgstream, "ERROR %s\n", errmsg);
1426             } else {
1427                 fprintf(mesgstream, "? %s\n", errmsg);
1428             }
1429             exit(1);
1430         }
1431
1432         while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
1433             if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
1434                 errmsg = vstrallocf(_("writing to %s: %s"),
1435                                      incrname, strerror(errno));
1436                 dbprintf("%s\n", errmsg);
1437                 return NULL;
1438             }
1439         }
1440
1441         if (nb < 0) {
1442             errmsg = vstrallocf(_("reading from %s: %s"),
1443                                  inputname, strerror(errno));
1444             dbprintf("%s\n", errmsg);
1445             return NULL;
1446         }
1447
1448         if (close(infd) != 0) {
1449             errmsg = vstrallocf(_("closing %s: %s"),
1450                                  inputname, strerror(errno));
1451             dbprintf("%s\n", errmsg);
1452             return NULL;
1453         }
1454         if (close(outfd) != 0) {
1455             errmsg = vstrallocf(_("closing %s: %s"),
1456                                  incrname, strerror(errno));
1457             dbprintf("%s\n", errmsg);
1458             return NULL;
1459         }
1460
1461         amfree(inputname);
1462         amfree(basename);
1463     }
1464     return incrname;
1465 }
1466
1467 static void
1468 check_no_check_device(void)
1469 {
1470     if (gnutar_checkdevice == 0) {
1471         GPtrArray *argv_ptr = g_ptr_array_new();
1472         int dumpin;
1473         int dataf;
1474         int outf;
1475         int size;
1476         char buf[32768];
1477
1478         g_ptr_array_add(argv_ptr, gnutar_path);
1479         g_ptr_array_add(argv_ptr, "-x");
1480         g_ptr_array_add(argv_ptr, "--no-check-device");
1481         g_ptr_array_add(argv_ptr, "-f");
1482         g_ptr_array_add(argv_ptr, "-");
1483         g_ptr_array_add(argv_ptr, NULL);
1484
1485         pipespawnv(gnutar_path, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
1486                              &dumpin, &dataf, &outf, (char **)argv_ptr->pdata);
1487         aclose(dumpin);
1488         aclose(dataf);
1489         size = read(outf, buf, 32767);
1490         if (size > 0) {
1491             buf[size] = '\0';
1492             if (strstr(buf, "--no-check-device")) {
1493                 g_debug("disabling --no-check-device since '%s' doesn't support it", gnutar_path);
1494                 gnutar_checkdevice = 1;
1495             }
1496         }
1497         aclose(outf);
1498         g_ptr_array_free(argv_ptr, TRUE);
1499     }
1500 }
1501
1502 GPtrArray *amgtar_build_argv(
1503     application_argument_t *argument,
1504     char  *incrname,
1505     char **file_exclude,
1506     char **file_include,
1507     int    command)
1508 {
1509     int    nb_exclude;
1510     int    nb_include;
1511     char  *dirname;
1512     char   tmppath[PATH_MAX];
1513     GPtrArray *argv_ptr = g_ptr_array_new();
1514     GSList    *copt;
1515
1516     check_no_check_device();
1517     amgtar_build_exinclude(&argument->dle, 1,
1518                            &nb_exclude, file_exclude,
1519                            &nb_include, file_include);
1520
1521     if (gnutar_directory) {
1522         dirname = gnutar_directory;
1523     } else {
1524         dirname = argument->dle.device;
1525     }
1526
1527     g_ptr_array_add(argv_ptr, stralloc(gnutar_path));
1528
1529     g_ptr_array_add(argv_ptr, stralloc("--create"));
1530     if (command == CMD_BACKUP && argument->dle.create_index)
1531         g_ptr_array_add(argv_ptr, stralloc("--verbose"));
1532     g_ptr_array_add(argv_ptr, stralloc("--file"));
1533     if (command == CMD_ESTIMATE) {
1534         g_ptr_array_add(argv_ptr, stralloc("/dev/null"));
1535     } else {
1536         g_ptr_array_add(argv_ptr, stralloc("-"));
1537     }
1538     if (gnutar_no_unquote)
1539         g_ptr_array_add(argv_ptr, stralloc("--no-unquote"));
1540     g_ptr_array_add(argv_ptr, stralloc("--directory"));
1541     canonicalize_pathname(dirname, tmppath);
1542     g_ptr_array_add(argv_ptr, stralloc(tmppath));
1543     if (gnutar_onefilesystem)
1544         g_ptr_array_add(argv_ptr, stralloc("--one-file-system"));
1545     if (gnutar_atimepreserve)
1546         g_ptr_array_add(argv_ptr, stralloc("--atime-preserve=system"));
1547     if (!gnutar_checkdevice)
1548         g_ptr_array_add(argv_ptr, stralloc("--no-check-device"));
1549     if (gnutar_acls)
1550         g_ptr_array_add(argv_ptr, stralloc("--acls"));
1551     if (gnutar_selinux)
1552         g_ptr_array_add(argv_ptr, stralloc("--selinux"));
1553     if (gnutar_xattrs)
1554         g_ptr_array_add(argv_ptr, stralloc("--xattrs"));
1555     g_ptr_array_add(argv_ptr, stralloc("--listed-incremental"));
1556     g_ptr_array_add(argv_ptr, stralloc(incrname));
1557     if (gnutar_sparse)
1558         g_ptr_array_add(argv_ptr, stralloc("--sparse"));
1559     if (argument->tar_blocksize) {
1560         g_ptr_array_add(argv_ptr, stralloc("--blocking-factor"));
1561         g_ptr_array_add(argv_ptr, stralloc(argument->tar_blocksize));
1562     }
1563     g_ptr_array_add(argv_ptr, stralloc("--ignore-failed-read"));
1564     g_ptr_array_add(argv_ptr, stralloc("--totals"));
1565
1566     for (copt = argument->command_options; copt != NULL; copt = copt->next) {
1567         g_ptr_array_add(argv_ptr, stralloc((char *)copt->data));
1568     }
1569
1570     if (*file_exclude) {
1571         g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
1572         g_ptr_array_add(argv_ptr, stralloc(*file_exclude));
1573     }
1574
1575     if (*file_include) {
1576         g_ptr_array_add(argv_ptr, stralloc("--files-from"));
1577         g_ptr_array_add(argv_ptr, stralloc(*file_include));
1578     }
1579     else {
1580         g_ptr_array_add(argv_ptr, stralloc("."));
1581     }
1582     g_ptr_array_add(argv_ptr, NULL);
1583
1584     return(argv_ptr);
1585 }
1586