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