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