2f8b17c26f9f80c23c25fbdeb9398dbd610f4bb1
[debian/amanda] / application-src / amstar.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: amstar.c 8888 2007-10-02 13:40:42Z martineau $
28  *
29  * send estimated backup sizes using dump
30  */
31
32 /* PROPERTY:
33  *
34  * STAR-PATH (default STAR)
35  * STAR-TARDUMP
36  * STAR-DLE-TARDUMP
37  * ONE-FILE-SYSTEM
38  * SPARSE
39  * NORMAL
40  * IGNORE
41  * STRANGE
42  * INCLUDE-LIST         (for restore only)
43  * EXCLUDE-LIST         (for restore only)
44  * DIRECTORY
45  */
46
47 #include "amanda.h"
48 #include "match.h"
49 #include "pipespawn.h"
50 #include "amfeatures.h"
51 #include "amandates.h"
52 #include "clock.h"
53 #include "util.h"
54 #include "getfsent.h"
55 #include "client_util.h"
56 #include "conffile.h"
57 #include "getopt.h"
58 #include "sendbackup.h"
59
60 int debug_application = 1;
61 #define application_debug(i, ...) do {  \
62         if ((i) <= debug_application) { \
63             dbprintf(__VA_ARGS__);      \
64         }                               \
65 } while (0)
66
67 static amregex_t init_re_table[] = {
68   /* tar prints the size in bytes */
69   AM_SIZE_RE("star: [0-9][0-9]* blocks", 10240, 1),
70   AM_NORMAL_RE("^could not open conf file"),
71   AM_NORMAL_RE("^Type of this level "),
72   AM_NORMAL_RE("^Date of this level "),
73   AM_NORMAL_RE("^Date of last level "),
74   AM_NORMAL_RE("^Dump record  level "),
75   AM_NORMAL_RE("^Throughput"),
76   AM_NORMAL_RE("^.*is sparse$"),
77
78 #ifdef IGNORE_TAR_ERRORS
79   AM_NORMAL_RE("^.*shrunk*$"),
80   AM_NORMAL_RE("^.*changed size.*$"),
81   AM_NORMAL_RE("^.*Cannot listxattr for.*$"),
82   AM_NORMAL_RE("^.Cannot: stat .*$"),
83   AM_NORMAL_RE("^.Missing links .*$"),
84   AM_NORMAL_RE("^.Cannot get xattr.*$"),
85   AM_NORMAL_RE("^.Cannot.*acl.*$"),
86 #endif
87
88   AM_NORMAL_RE("^star: dumped [0-9][0-9]* (tar )?files"),
89   AM_NORMAL_RE("^.*The following problems occurred during .* processing.*$"),
90   AM_NORMAL_RE("^.*Processed all possible files, despite earlier errors.*$"),
91   AM_NORMAL_RE("^.*not written due to problems during backup.*$"),
92
93   AM_STRANGE_RE("^Perform a level 0 dump first.*$"),
94
95   /* catch-all: DMP_STRANGE is returned for all other lines */
96   AM_STRANGE_RE(NULL)
97 };
98 static amregex_t *re_table;
99
100 /* local functions */
101 int main(int argc, char **argv);
102
103 typedef struct application_argument_s {
104     char      *config;
105     char      *host;
106     int        message;
107     int        collection;
108     int        calcsize;
109     GSList    *level;
110     GSList    *command_options;
111     dle_t      dle;
112     int        argc;
113     char     **argv;
114 } application_argument_t;
115
116 enum { CMD_ESTIMATE, CMD_BACKUP };
117
118 static void amstar_support(application_argument_t *argument);
119 static void amstar_selfcheck(application_argument_t *argument);
120 static void amstar_estimate(application_argument_t *argument);
121 static void amstar_backup(application_argument_t *argument);
122 static void amstar_restore(application_argument_t *argument);
123 static void amstar_validate(application_argument_t *argument);
124 static GPtrArray *amstar_build_argv(application_argument_t *argument,
125                                 int level,
126                                 int command);
127
128 static char *star_path;
129 static char *star_tardumps;
130 static int   star_dle_tardumps;
131 static int   star_onefilesystem;
132 static int   star_sparse;
133 static char *star_directory;
134 static GSList *normal_message = NULL;
135 static GSList *ignore_message = NULL;
136 static GSList *strange_message = NULL;
137
138 static struct option long_options[] = {
139     {"config"          , 1, NULL,  1},
140     {"host"            , 1, NULL,  2},
141     {"disk"            , 1, NULL,  3},
142     {"device"          , 1, NULL,  4},
143     {"level"           , 1, NULL,  5},
144     {"index"           , 1, NULL,  6},
145     {"message"         , 1, NULL,  7},
146     {"collection"      , 0, NULL,  8},
147     {"record"          , 0, NULL,  9},
148     {"star-path"       , 1, NULL, 10},
149     {"star-tardump"    , 1, NULL, 11},
150     {"star-dle-tardump", 1, NULL, 12},
151     {"one-file-system" , 1, NULL, 13},
152     {"sparse"          , 1, NULL, 14},
153     {"calcsize"        , 0, NULL, 15},
154     {"normal"          , 1, NULL, 16},
155     {"ignore"          , 1, NULL, 17},
156     {"strange"         , 1, NULL, 18},
157     {"include-list"    , 1, NULL, 19},
158     {"exclude-list"    , 1, NULL, 20},
159     {"directory"       , 1, NULL, 21},
160     {"command-options" , 1, NULL, 22},
161     { NULL, 0, NULL, 0}
162 };
163
164
165 int
166 main(
167     int         argc,
168     char **     argv)
169 {
170     int c;
171     char *command;
172     application_argument_t argument;
173
174 #ifdef STAR
175     star_path = STAR;
176 #else
177     star_path = NULL;
178 #endif
179     star_tardumps = "/etc/tardumps";
180     star_dle_tardumps = 0;
181     star_onefilesystem = 1;
182     star_sparse = 1;
183     star_directory = NULL;
184
185     /* initialize */
186
187     /*
188      * Configure program for internationalization:
189      *   1) Only set the message locale for now.
190      *   2) Set textdomain for all amanda related programs to "amanda"
191      *      We don't want to be forced to support dozens of message catalogs.
192      */
193     setlocale(LC_MESSAGES, "C");
194     textdomain("amanda");
195
196     if (argc < 2) {
197         printf("ERROR no command given to amstar\n");
198         error(_("No command given to amstar"));
199     }
200
201     /* drop root privileges */
202     if (!set_root_privs(0)) {
203         if (strcmp(argv[1], "selfcheck") == 0) {
204             printf("ERROR amstar must be run setuid root\n");
205         }
206         error(_("amstar must be run setuid root"));
207     }
208
209     safe_fd(3, 2);
210
211     set_pname("amstar");
212
213     /* Don't die when child closes pipe */
214     signal(SIGPIPE, SIG_IGN);
215
216 #if defined(USE_DBMALLOC)
217     malloc_size_1 = malloc_inuse(&malloc_hist_1);
218 #endif
219
220     add_amanda_log_handler(amanda_log_stderr);
221     add_amanda_log_handler(amanda_log_syslog);
222     dbopen(DBG_SUBDIR_CLIENT);
223     startclock();
224     dbprintf(_("version %s\n"), VERSION);
225
226     config_init(CONFIG_INIT_CLIENT, NULL);
227
228     //check_running_as(RUNNING_AS_DUMPUSER_PREFERRED);
229     //root for amrecover
230     //RUNNING_AS_CLIENT_LOGIN from selfcheck, sendsize, sendbackup
231
232     /* parse argument */
233     command = argv[1];
234
235     argument.config     = NULL;
236     argument.host       = NULL;
237     argument.message    = 0;
238     argument.collection = 0;
239     argument.calcsize   = 0;
240     argument.level      = NULL;
241     argument.command_options = NULL;
242     init_dle(&argument.dle);
243
244     opterr = 0;
245     while (1) {
246         int option_index = 0;
247         c = getopt_long (argc, argv, "", long_options, &option_index);
248         if (c == -1)
249             break;
250
251         switch (c) {
252         case 1: argument.config = stralloc(optarg);
253                 break;
254         case 2: argument.host = stralloc(optarg);
255                 break;
256         case 3: argument.dle.disk = stralloc(optarg);
257                 break;
258         case 4: argument.dle.device = stralloc(optarg);
259                 break;
260         case 5: argument.level = g_slist_append(argument.level,
261                                                 GINT_TO_POINTER(atoi(optarg)));
262                 break;
263         case 6: argument.dle.create_index = 1;
264                 break;
265         case 7: argument.message = 1;
266                 break;
267         case 8: argument.collection = 1;
268                 break;
269         case 9: argument.dle.record = 1;
270                 break;
271         case 10: star_path = stralloc(optarg);
272                  break;
273         case 11: star_tardumps = stralloc(optarg);
274                  break;
275         case 12: if (optarg && strcasecmp(optarg, "NO") == 0)
276                      star_dle_tardumps = 0;
277                  else if (optarg && strcasecmp(optarg, "YES") == 0)
278                      star_dle_tardumps = 1;
279                  else if (strcasecmp(command, "selfcheck") == 0)
280                      printf(_("ERROR [%s: bad STAR-DLE-TARDUMP property value (%s)]\n"), get_pname(), optarg);
281                  break;
282         case 13: if (optarg && strcasecmp(optarg, "YES") != 0) {
283                      /* This option is required to be YES */
284                      /* star_onefilesystem = 0; */
285                  }
286                  break;
287         case 14: if (optarg && strcasecmp(optarg, "NO") == 0)
288                      star_sparse = 0;
289                  else if (optarg && strcasecmp(optarg, "YES") == 0)
290                      star_sparse = 1;
291                  else if (strcasecmp(command, "selfcheck") == 0)
292                      printf(_("ERROR [%s: bad SPARSE property value (%s)]\n"), get_pname(), optarg);
293                  break;
294         case 15: argument.calcsize = 1;
295                  break;
296         case 16: if (optarg)
297                      normal_message =
298                          g_slist_append(normal_message, optarg);
299                  break;
300         case 17: if (optarg)
301                      ignore_message =
302                          g_slist_append(ignore_message, optarg);
303                  break;
304         case 18: if (optarg)
305                      strange_message =
306                          g_slist_append(strange_message, optarg);
307                  break;
308         case 19: if (optarg)
309                      argument.dle.include_list =
310                          append_sl(argument.dle.include_list, optarg);
311                  break;
312         case 20: if (optarg)
313                      argument.dle.exclude_list =
314                          append_sl(argument.dle.exclude_list, optarg);
315                  break;
316         case 21: if (optarg)
317                      star_directory = stralloc(optarg);
318                  break;
319         case 22: argument.command_options =
320                         g_slist_append(argument.command_options,
321                                        stralloc(optarg));
322         case ':':
323         case '?':
324                 break;
325         }
326     }
327
328     if (!argument.dle.disk && argument.dle.device)
329         argument.dle.disk = stralloc(argument.dle.device);
330     if (!argument.dle.device && argument.dle.disk)
331         argument.dle.device = stralloc(argument.dle.disk);
332
333     argument.argc = argc - optind;
334     argument.argv = argv + optind;
335
336     if (argument.config) {
337         /* overlay this configuration on the existing (nameless) configuration */
338         config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
339                     argument.config);
340         dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
341
342     }
343
344     if (config_errors(NULL) >= CFGERR_ERRORS) {
345         g_critical(_("errors processing config file"));
346     }
347
348     re_table = build_re_table(init_re_table, normal_message, ignore_message,
349                               strange_message);
350
351     if (strcmp(command, "support") == 0) {
352         amstar_support(&argument);
353     } else if (strcmp(command, "selfcheck") == 0) {
354         amstar_selfcheck(&argument);
355     } else if (strcmp(command, "estimate") == 0) {
356         amstar_estimate(&argument);
357     } else if (strcmp(command, "backup") == 0) {
358         amstar_backup(&argument);
359     } else if (strcmp(command, "restore") == 0) {
360         amstar_restore(&argument);
361     } else if (strcmp(command, "validate") == 0) {
362         amstar_validate(&argument);
363     } else {
364         fprintf(stderr, "Unknown command `%s'.\n", command);
365         exit (1);
366     }
367     return 0;
368 }
369
370 static void
371 amstar_support(
372     application_argument_t *argument)
373 {
374     (void)argument;
375     fprintf(stdout, "CONFIG YES\n");
376     fprintf(stdout, "HOST YES\n");
377     fprintf(stdout, "DISK YES\n");
378     fprintf(stdout, "MAX-LEVEL 9\n");
379     fprintf(stdout, "INDEX-LINE YES\n");
380     fprintf(stdout, "INDEX-XML NO\n");
381     fprintf(stdout, "MESSAGE-LINE YES\n");
382     fprintf(stdout, "MESSAGE-XML NO\n");
383     fprintf(stdout, "RECORD YES\n");
384     fprintf(stdout, "INCLUDE-FILE NO\n");
385     fprintf(stdout, "INCLUDE-LIST YES\n");
386     fprintf(stdout, "EXCLUDE-FILE YES\n");
387     fprintf(stdout, "EXCLUDE-LIST YES\n");
388     fprintf(stdout, "COLLECTION NO\n");
389     fprintf(stdout, "MULTI-ESTIMATE YES\n");
390     fprintf(stdout, "CALCSIZE YES\n");
391     fprintf(stdout, "CLIENT-ESTIMATE YES\n");
392 }
393
394 static void
395 amstar_selfcheck(
396     application_argument_t *argument)
397 {
398     fprintf(stdout, "OK amstar\n");
399     if (argument->dle.disk) {
400         char *qdisk = quote_string(argument->dle.disk);
401         fprintf(stdout, "OK %s\n", qdisk);
402         amfree(qdisk);
403     }
404     if (argument->dle.device) {
405         char *qdevice = quote_string(argument->dle.device);
406         fprintf(stdout, "OK %s\n", qdevice);
407         amfree(qdevice);
408     }
409     if (star_directory) {
410         char *qdirectory = quote_string(star_directory);
411         fprintf(stdout, "OK %s\n", qdirectory);
412         amfree(qdirectory);
413     }
414
415     if (argument->dle.include_list &&
416         argument->dle.include_list->nb_element >= 0) {
417         fprintf(stdout, "ERROR include-list not supported for backup\n");
418     }
419     if (argument->dle.exclude_list &&
420         argument->dle.exclude_list->nb_element >= 0) {
421         fprintf(stdout, "ERROR exclude-list not supported for backup\n");
422     }
423
424     if (!star_path) {
425         fprintf(stdout, "ERROR STAR-PATH not defined\n");
426     } else {
427         check_file(star_path, X_OK);
428     }
429
430     if (argument->calcsize) {
431         char *calcsize = vstralloc(amlibexecdir, "/", "calcsize", NULL);
432         check_file(calcsize, X_OK);
433         check_suid(calcsize);
434         amfree(calcsize);
435     }
436
437     {
438         char *amandates_file;
439         amandates_file = getconf_str(CNF_AMANDATES);
440         check_file(amandates_file, R_OK|W_OK);
441     }
442
443 }
444
445 static void
446 amstar_estimate(
447     application_argument_t *argument)
448 {
449     GPtrArray  *argv_ptr;
450     char       *cmd = NULL;
451     int         nullfd;
452     int         pipefd;
453     FILE       *dumpout = NULL;
454     off_t       size = -1;
455     char        line[32768];
456     char       *errmsg = NULL;
457     char       *qerrmsg;
458     char       *qdisk;
459     amwait_t    wait_status;
460     int         starpid;
461     amregex_t  *rp;
462     times_t     start_time;
463     int         level = 0;
464     GSList     *levels = NULL;
465
466     if (!argument->level) {
467         fprintf(stderr, "ERROR No level argument\n");
468         error(_("No level argument"));
469     }
470     if (!argument->dle.disk) {
471         fprintf(stderr, "ERROR No disk argument\n");
472         error(_("No disk argument"));
473     }
474     if (!argument->dle.device) {
475         fprintf(stderr, "ERROR No device argument\n");
476         error(_("No device argument"));
477     }
478
479     if (argument->dle.include_list &&
480         argument->dle.include_list->nb_element >= 0) {
481         fprintf(stderr, "ERROR include-list not supported for backup\n");
482     }
483     if (argument->dle.exclude_list &&
484         argument->dle.exclude_list->nb_element >= 0) {
485         fprintf(stderr, "ERROR exclude-list not supported for backup\n");
486     }
487
488     qdisk = quote_string(argument->dle.disk);
489     if (argument->calcsize) {
490         char *dirname;
491
492         if (star_directory) {
493             dirname = amname_to_dirname(star_directory);
494         } else {
495             dirname = amname_to_dirname(argument->dle.device);
496         }
497         run_calcsize(argument->config, "STAR", argument->dle.disk, dirname,
498                      argument->level, NULL, NULL);
499         return;
500     }
501
502     if (!star_path) {
503         errmsg = vstrallocf(_("STAR-PATH not defined"));
504         goto common_error;
505     }
506     cmd = stralloc(star_path);
507
508     start_time = curclock();
509
510     for (levels = argument->level; levels != NULL; levels = levels->next) {
511         level = GPOINTER_TO_INT(levels->data);
512         argv_ptr = amstar_build_argv(argument, level, CMD_ESTIMATE);
513
514         if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
515             errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
516                                 strerror(errno));
517             goto common_error;
518         }
519
520         starpid = pipespawnv(cmd, STDERR_PIPE, 1,
521                              &nullfd, &nullfd, &pipefd,
522                              (char **)argv_ptr->pdata);
523
524         dumpout = fdopen(pipefd,"r");
525         if (!dumpout) {
526             errmsg = vstrallocf(_("Can't fdopen: %s"), strerror(errno));
527             goto common_error;
528         }
529
530         size = (off_t)-1;
531         while (size < 0 && (fgets(line, sizeof(line), dumpout)) != NULL) {
532             if (line[strlen(line)-1] == '\n') /* remove trailling \n */
533                 line[strlen(line)-1] = '\0';
534             if (line[0] == '\0')
535                 continue;
536             dbprintf("%s\n", line);
537             /* check for size match */
538             /*@ignore@*/
539             for(rp = re_table; rp->regex != NULL; rp++) {
540                 if(match(rp->regex, line)) {
541                     if (rp->typ == DMP_SIZE) {
542                         size = ((the_num(line, rp->field)*rp->scale+1023.0)/1024.0);
543                         if(size < 0.0)
544                             size = 1.0;             /* found on NeXT -- sigh */
545                     }
546                     break;
547                 }
548             }
549             /*@end@*/
550         }
551
552         while ((fgets(line, sizeof(line), dumpout)) != NULL) {
553             dbprintf("%s", line);
554         }
555
556         dbprintf(".....\n");
557         dbprintf(_("estimate time for %s level %d: %s\n"),
558                  qdisk,
559                  level,
560                  walltime_str(timessub(curclock(), start_time)));
561         if(size == (off_t)-1) {
562             errmsg = vstrallocf(_("no size line match in %s output"),
563                                 cmd);
564             dbprintf(_("%s for %s\n"), errmsg, qdisk);
565             dbprintf(".....\n");
566         } else if(size == (off_t)0 && argument->level == 0) {
567             dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
568                      cmd, argument->dle.disk);
569             dbprintf(".....\n");
570         }
571         dbprintf(_("estimate size for %s level %d: %lld KB\n"),
572                  qdisk,
573                  level,
574                  (long long)size);
575
576         kill(-starpid, SIGTERM);
577
578         dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
579         waitpid(starpid, &wait_status, 0);
580         if (WIFSIGNALED(wait_status)) {
581             errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
582                                 cmd, WTERMSIG(wait_status), dbfn());
583         } else if (WIFEXITED(wait_status)) {
584             if (WEXITSTATUS(wait_status) != 0) {
585                 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
586                                     cmd, WEXITSTATUS(wait_status), dbfn());
587             } else {
588                 /* Normal exit */
589             }
590         } else {
591             errmsg = vstrallocf(_("%s got bad exit: see %s"), cmd, dbfn());
592         }
593         dbprintf(_("after %s %s wait\n"), cmd, qdisk);
594
595         g_ptr_array_free_full(argv_ptr);
596
597         aclose(nullfd);
598         afclose(dumpout);
599
600         fprintf(stdout, "%d %lld 1\n", level, (long long)size);
601     }
602     amfree(qdisk);
603     amfree(cmd);
604     return;
605
606 common_error:
607     dbprintf("%s\n", errmsg);
608     qerrmsg = quote_string(errmsg);
609     amfree(qdisk);
610     dbprintf("%s", errmsg);
611     fprintf(stdout, "ERROR %s\n", qerrmsg);
612     amfree(errmsg);
613     amfree(qerrmsg);
614     amfree(cmd);
615 }
616
617 static void
618 amstar_backup(
619     application_argument_t *argument)
620 {
621     int        dumpin;
622     char      *cmd = NULL;
623     char      *qdisk;
624     char       line[32768];
625     amregex_t *rp;
626     off_t      dump_size = -1;
627     char      *type;
628     char       startchr;
629     GPtrArray *argv_ptr;
630     int        starpid;
631     int        dataf = 1;
632     int        mesgf = 3;
633     int        indexf = 4;
634     int        outf;
635     FILE      *mesgstream;
636     FILE      *indexstream = NULL;
637     FILE      *outstream;
638     int        level;
639     regex_t    regex_root;
640     regex_t    regex_dir;
641     regex_t    regex_file;
642     regex_t    regex_special;
643     regex_t    regex_symbolic;
644     regex_t    regex_hard;
645
646     mesgstream = fdopen(mesgf, "w");
647     if (!mesgstream) {
648         error(_("error mesgstream(%d): %s\n"), mesgf, strerror(errno));
649     }
650
651     if (!argument->level) {
652         fprintf(mesgstream, "? No level argument\n");
653         error(_("No level argument"));
654     }
655     if (!argument->dle.disk) {
656         fprintf(mesgstream, "? No disk argument\n");
657         error(_("No disk argument"));
658     }
659     if (!argument->dle.device) {
660         fprintf(mesgstream, "? No device argument\n");
661         error(_("No device argument"));
662     }
663
664     if (argument->dle.include_list &&
665         argument->dle.include_list->nb_element >= 0) {
666         fprintf(mesgstream, "? include-list not supported for backup\n");
667     }
668     if (argument->dle.exclude_list &&
669         argument->dle.exclude_list->nb_element >= 0) {
670         fprintf(mesgstream, "? exclude-list not supported for backup\n");
671     }
672
673     level = GPOINTER_TO_INT(argument->level->data);
674
675     qdisk = quote_string(argument->dle.disk);
676
677     argv_ptr = amstar_build_argv(argument, level, CMD_BACKUP);
678
679     cmd = stralloc(star_path);
680
681     starpid = pipespawnv(cmd, STDIN_PIPE|STDERR_PIPE, 1,
682                          &dumpin, &dataf, &outf, (char **)argv_ptr->pdata);
683
684     g_ptr_array_free_full(argv_ptr);
685     /* close the write ends of the pipes */
686     aclose(dumpin);
687     aclose(dataf);
688     if (argument->dle.create_index) {
689         indexstream = fdopen(indexf, "w");
690         if (!indexstream) {
691             error(_("error indexstream(%d): %s\n"), indexf, strerror(errno));
692         }
693     }
694     outstream = fdopen(outf, "r");
695     if (!outstream) {
696         error(_("error outstream(%d): %s\n"), outf, strerror(errno));
697     }
698
699     regcomp(&regex_root, "^a \\.\\/ directory$", REG_EXTENDED|REG_NEWLINE);
700     regcomp(&regex_dir, "^a (.*) directory$", REG_EXTENDED|REG_NEWLINE);
701     regcomp(&regex_file, "^a (.*) (.*) bytes", REG_EXTENDED|REG_NEWLINE);
702     regcomp(&regex_special, "^a (.*) special", REG_EXTENDED|REG_NEWLINE);
703     regcomp(&regex_symbolic, "^a (.*) symbolic", REG_EXTENDED|REG_NEWLINE);
704     regcomp(&regex_hard, "^a (.*) link to", REG_EXTENDED|REG_NEWLINE);
705
706     while ((fgets(line, sizeof(line), outstream)) != NULL) {
707         regmatch_t regmatch[3];
708
709         if (line[strlen(line)-1] == '\n') /* remove trailling \n */
710             line[strlen(line)-1] = '\0';
711
712         if (regexec(&regex_root, line, 1, regmatch, 0) == 0) {
713             if (argument->dle.create_index)
714                 fprintf(indexstream, "%s\n", "/");
715             continue;
716         }
717
718         if (regexec(&regex_dir, line, 3, regmatch, 0) == 0) {
719             if (argument->dle.create_index && regmatch[1].rm_so == 2) {
720                 line[regmatch[1].rm_eo+1]='\0';
721                 fprintf(indexstream, "/%s\n", &line[regmatch[1].rm_so]);
722             }
723             continue;
724         }
725
726         if (regexec(&regex_file, line, 3, regmatch, 0) == 0 ||
727             regexec(&regex_special, line, 3, regmatch, 0) == 0 ||
728             regexec(&regex_symbolic, line, 3, regmatch, 0) == 0 ||
729             regexec(&regex_hard, line, 3, regmatch, 0) == 0) {
730             if (argument->dle.create_index && regmatch[1].rm_so == 2) {
731                 line[regmatch[1].rm_eo]='\0';
732                 fprintf(indexstream, "/%s\n", &line[regmatch[1].rm_so]);
733             }
734             continue;
735         }
736
737         for (rp = re_table; rp->regex != NULL; rp++) {
738             if (match(rp->regex, line)) {
739                 break;
740             }
741         }
742         if (rp->typ == DMP_SIZE) {
743             dump_size = (long)((the_num(line, rp->field)* rp->scale+1023.0)/1024.0);
744         }
745         switch (rp->typ) {
746             case DMP_IGNORE:
747                 continue;
748             case DMP_NORMAL:
749                 type = "normal";
750                 startchr = '|';
751                 break;
752             case DMP_STRANGE:
753                 type = "strange";
754                 startchr = '?';
755                 break;
756             case DMP_SIZE:
757                 type = "size";
758                 startchr = '|';
759                 break;
760             case DMP_ERROR:
761                 type = "error";
762                 startchr = '?';
763                 break;
764             default:
765                 type = "unknown";
766                 startchr = '!';
767                 break;
768         }
769         dbprintf("%3d: %7s(%c): %s\n", rp->srcline, type, startchr, line);
770         fprintf(mesgstream,"%c %s\n", startchr, line);
771     }
772
773     regfree(&regex_root);
774     regfree(&regex_dir);
775     regfree(&regex_file);
776     regfree(&regex_special);
777     regfree(&regex_symbolic);
778     regfree(&regex_hard);
779
780     dbprintf(_("gnutar: %s: pid %ld\n"), cmd, (long)starpid);
781
782     dbprintf("sendbackup: size %lld\n", (long long)dump_size);
783     fprintf(mesgstream, "sendbackup: size %lld\n", (long long)dump_size);
784     dbprintf("sendbackup: end\n");
785     fprintf(mesgstream, "sendbackup: end\n");
786
787     fclose(mesgstream);
788     if (argument->dle.create_index)
789         fclose(indexstream);
790
791     amfree(qdisk);
792     amfree(cmd);
793 }
794
795 static void
796 amstar_restore(
797     application_argument_t *argument)
798 {
799     char       *cmd;
800     GPtrArray  *argv_ptr = g_ptr_array_new();
801     char      **env;
802     int         j;
803     char       *e;
804
805     if (!star_path) {
806         error(_("STAR-PATH not defined"));
807     }
808
809     cmd = stralloc(star_path);
810
811     g_ptr_array_add(argv_ptr, stralloc(star_path));
812     if (star_directory) {
813         struct stat stat_buf;
814         if(stat(star_directory, &stat_buf) != 0) {
815             fprintf(stderr,"can not stat directory %s: %s\n", star_directory, strerror(errno));
816             exit(1);
817         }
818         if (!S_ISDIR(stat_buf.st_mode)) {
819             fprintf(stderr,"%s is not a directory\n", star_directory);
820             exit(1);
821         }
822         if (access(star_directory, W_OK) != 0 ) {
823             fprintf(stderr, "Can't write to %s: %s\n", star_directory, strerror(errno));
824             exit(1);
825         }
826
827         g_ptr_array_add(argv_ptr, stralloc("-C"));
828         g_ptr_array_add(argv_ptr, stralloc(star_directory));
829     }
830     g_ptr_array_add(argv_ptr, stralloc("-x"));
831     g_ptr_array_add(argv_ptr, stralloc("-v"));
832     g_ptr_array_add(argv_ptr, stralloc("-xattr"));
833     g_ptr_array_add(argv_ptr, stralloc("-acl"));
834     g_ptr_array_add(argv_ptr, stralloc("errctl=WARN|SAMEFILE|SETTIME|DIFF|SETACL|SETXATTR|SETMODE|BADACL *"));
835     g_ptr_array_add(argv_ptr, stralloc("-no-fifo"));
836     g_ptr_array_add(argv_ptr, stralloc("-f"));
837     g_ptr_array_add(argv_ptr, stralloc("-"));
838
839     if (argument->dle.exclude_list &&
840         argument->dle.exclude_list->nb_element == 1) {
841         g_ptr_array_add(argv_ptr, stralloc("-exclude-from"));
842         g_ptr_array_add(argv_ptr,
843                         stralloc(argument->dle.exclude_list->first->name));
844     }
845
846     if (argument->dle.include_list &&
847         argument->dle.include_list->nb_element == 1) {
848         g_ptr_array_add(argv_ptr,
849                         stralloc2("list=",
850                                   argument->dle.include_list->first->name));
851     }
852     for (j=1; j< argument->argc; j++)
853         g_ptr_array_add(argv_ptr, stralloc(argument->argv[j]+2));/*remove ./ */
854     g_ptr_array_add(argv_ptr, NULL);
855
856     debug_executing(argv_ptr);
857     env = safe_env();
858     become_root();
859     execve(cmd, (char **)argv_ptr->pdata, env);
860     e = strerror(errno);
861     error(_("error [exec %s: %s]"), cmd, e);
862
863 }
864
865 static void
866 amstar_validate(
867     application_argument_t *argument G_GNUC_UNUSED)
868 {
869     char       *cmd;
870     GPtrArray  *argv_ptr = g_ptr_array_new();
871     char      **env;
872     char       *e;
873     char        buf[32768];
874
875     if (!star_path) {
876         dbprintf("STAR-PATH not set; Piping to /dev/null\n");
877         fprintf(stderr,"STAR-PATH not set; Piping to /dev/null\n");
878         goto pipe_to_null;
879     }
880
881     cmd = stralloc(star_path);
882
883     g_ptr_array_add(argv_ptr, stralloc(star_path));
884     g_ptr_array_add(argv_ptr, stralloc("-t"));
885     g_ptr_array_add(argv_ptr, stralloc("-f"));
886     g_ptr_array_add(argv_ptr, stralloc("-"));
887     g_ptr_array_add(argv_ptr, NULL);
888
889     debug_executing(argv_ptr);
890     env = safe_env();
891     execve(cmd, (char **)argv_ptr->pdata, env);
892     e = strerror(errno);
893     dbprintf("failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
894     fprintf(stderr,"failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
895 pipe_to_null:
896     while (read(0, buf, 32768) > 0) {
897     }
898
899 }
900
901 static GPtrArray *amstar_build_argv(
902     application_argument_t *argument,
903     int   level,
904     int   command)
905 {
906     char      *dirname;
907     char      *fsname;
908     char       levelstr[NUM_STR_SIZE+7];
909     GPtrArray *argv_ptr = g_ptr_array_new();
910     char      *s;
911     char      *tardumpfile;
912     GSList    *copt;
913
914     if (star_directory) {
915         dirname = amname_to_dirname(star_directory);
916     } else {
917         dirname = amname_to_dirname(argument->dle.device);
918     }
919     fsname = vstralloc("fs-name=", dirname, NULL);
920     for (s = fsname; *s != '\0'; s++) {
921         if (iscntrl((int)*s))
922             *s = '-';
923     }
924     snprintf(levelstr, SIZEOF(levelstr), "-level=%d", level);
925
926     if (star_dle_tardumps) {
927         char *sdisk = sanitise_filename(argument->dle.disk);
928         tardumpfile = vstralloc(star_tardumps, sdisk, NULL);
929         amfree(sdisk);
930     } else {
931         tardumpfile = stralloc(star_tardumps);
932     }
933
934     g_ptr_array_add(argv_ptr, stralloc(star_path));
935
936     g_ptr_array_add(argv_ptr, stralloc("-c"));
937     g_ptr_array_add(argv_ptr, stralloc("-f"));
938     if (command == CMD_ESTIMATE) {
939         g_ptr_array_add(argv_ptr, stralloc("/dev/null"));
940     } else {
941         g_ptr_array_add(argv_ptr, stralloc("-"));
942     }
943     g_ptr_array_add(argv_ptr, stralloc("-C"));
944 #if defined(__CYGWIN__)
945     {
946         char tmppath[PATH_MAX];
947
948         cygwin_conv_to_full_posix_path(dirname, tmppath);
949         g_ptr_array_add(argv_ptr, stralloc(tmppath));
950     }
951 #else
952     g_ptr_array_add(argv_ptr, stralloc(dirname));
953 #endif
954     g_ptr_array_add(argv_ptr, stralloc(fsname));
955     if (star_onefilesystem)
956         g_ptr_array_add(argv_ptr, stralloc("-xdev"));
957     g_ptr_array_add(argv_ptr, stralloc("-link-dirs"));
958     g_ptr_array_add(argv_ptr, stralloc(levelstr));
959     g_ptr_array_add(argv_ptr, stralloc2("tardumps=", tardumpfile));
960     if (command == CMD_BACKUP)
961         g_ptr_array_add(argv_ptr, stralloc("-wtardumps"));
962     g_ptr_array_add(argv_ptr, stralloc("-xattr"));
963     g_ptr_array_add(argv_ptr, stralloc("-acl"));
964     g_ptr_array_add(argv_ptr, stralloc("H=exustar"));
965     g_ptr_array_add(argv_ptr, stralloc("errctl=WARN|SAMEFILE|DIFF|GROW|SHRINK|SPECIALFILE|GETXATTR|BADACL *"));
966     if (star_sparse)
967         g_ptr_array_add(argv_ptr, stralloc("-sparse"));
968     g_ptr_array_add(argv_ptr, stralloc("-dodesc"));
969
970     for (copt = argument->command_options; copt != NULL; copt = copt->next) {
971         g_ptr_array_add(argv_ptr, stralloc((char *)copt->data));
972     }
973
974     if (command == CMD_BACKUP && argument->dle.create_index)
975         g_ptr_array_add(argv_ptr, stralloc("-v"));
976
977     g_ptr_array_add(argv_ptr, stralloc("."));
978
979     g_ptr_array_add(argv_ptr, NULL);
980
981     amfree(tardumpfile);
982     amfree(fsname);
983     amfree(dirname);
984
985     return(argv_ptr);
986 }