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