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