0959a1619b0ffeca8867ce88b21eee0954416a1f
[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     argument.dle.record = 0;
250
251     opterr = 0;
252     while (1) {
253         int option_index = 0;
254         c = getopt_long (argc, argv, "", long_options, &option_index);
255         if (c == -1)
256             break;
257
258         switch (c) {
259         case 1: argument.config = stralloc(optarg);
260                 break;
261         case 2: argument.host = stralloc(optarg);
262                 break;
263         case 3: argument.dle.disk = stralloc(optarg);
264                 break;
265         case 4: argument.dle.device = stralloc(optarg);
266                 break;
267         case 5: argument.level = g_slist_append(argument.level,
268                                                 GINT_TO_POINTER(atoi(optarg)));
269                 break;
270         case 6: argument.dle.create_index = 1;
271                 break;
272         case 7: argument.message = 1;
273                 break;
274         case 8: argument.collection = 1;
275                 break;
276         case 9: argument.dle.record = 1;
277                 break;
278         case 10: star_path = stralloc(optarg);
279                  break;
280         case 11: star_tardumps = stralloc(optarg);
281                  break;
282         case 12: if (optarg && strcasecmp(optarg, "NO") == 0)
283                      star_dle_tardumps = 0;
284                  else if (optarg && strcasecmp(optarg, "YES") == 0)
285                      star_dle_tardumps = 1;
286                  else if (strcasecmp(command, "selfcheck") == 0)
287                      printf(_("ERROR [%s: bad STAR-DLE-TARDUMP property value (%s)]\n"), get_pname(), optarg);
288                  break;
289         case 13: if (optarg && strcasecmp(optarg, "YES") != 0) {
290                      /* This option is required to be YES */
291                      /* star_onefilesystem = 0; */
292                  }
293                  break;
294         case 14: if (optarg && strcasecmp(optarg, "NO") == 0)
295                      star_sparse = 0;
296                  else if (optarg && strcasecmp(optarg, "YES") == 0)
297                      star_sparse = 1;
298                  else if (strcasecmp(command, "selfcheck") == 0)
299                      printf(_("ERROR [%s: bad SPARSE property value (%s)]\n"), get_pname(), optarg);
300                  break;
301         case 15: argument.calcsize = 1;
302                  break;
303         case 16: if (optarg)
304                      normal_message =
305                          g_slist_append(normal_message, optarg);
306                  break;
307         case 17: if (optarg)
308                      ignore_message =
309                          g_slist_append(ignore_message, optarg);
310                  break;
311         case 18: if (optarg)
312                      strange_message =
313                          g_slist_append(strange_message, optarg);
314                  break;
315         case 19: if (optarg)
316                      argument.dle.include_list =
317                          append_sl(argument.dle.include_list, optarg);
318                  break;
319         case 20: if (optarg)
320                      argument.dle.exclude_list =
321                          append_sl(argument.dle.exclude_list, optarg);
322                  break;
323         case 21: if (optarg)
324                      star_directory = stralloc(optarg);
325                  break;
326         case 22: argument.command_options =
327                         g_slist_append(argument.command_options,
328                                        stralloc(optarg));
329                  break;
330         case 23: if (optarg)
331                      argument.dle.exclude_file =
332                          append_sl(argument.dle.exclude_file, optarg);
333                  break;
334         case 24: if (optarg && strcasecmp(optarg, "NO") == 0)
335                      star_acl = 0;
336                  else if (optarg && strcasecmp(optarg, "YES") == 0)
337                      star_acl = 1;
338                  else if (strcasecmp(command, "selfcheck") == 0)
339                      printf(_("ERROR [%s: bad ACL property value (%s)]\n"), get_pname(), optarg);
340                  break;
341         case ':':
342         case '?':
343                 break;
344         }
345     }
346
347     if (!argument.dle.disk && argument.dle.device)
348         argument.dle.disk = stralloc(argument.dle.device);
349     if (!argument.dle.device && argument.dle.disk)
350         argument.dle.device = stralloc(argument.dle.disk);
351
352     argument.argc = argc - optind;
353     argument.argv = argv + optind;
354
355     if (argument.config) {
356         /* overlay this configuration on the existing (nameless) configuration */
357         config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
358                     argument.config);
359         dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
360
361     }
362
363     if (config_errors(NULL) >= CFGERR_ERRORS) {
364         g_critical(_("errors processing config file"));
365     }
366
367     re_table = build_re_table(init_re_table, normal_message, ignore_message,
368                               strange_message);
369
370     if (strcmp(command, "support") == 0) {
371         amstar_support(&argument);
372     } else if (strcmp(command, "selfcheck") == 0) {
373         amstar_selfcheck(&argument);
374     } else if (strcmp(command, "estimate") == 0) {
375         amstar_estimate(&argument);
376     } else if (strcmp(command, "backup") == 0) {
377         amstar_backup(&argument);
378     } else if (strcmp(command, "restore") == 0) {
379         amstar_restore(&argument);
380     } else if (strcmp(command, "validate") == 0) {
381         amstar_validate(&argument);
382     } else {
383         fprintf(stderr, "Unknown command `%s'.\n", command);
384         exit (1);
385     }
386     return 0;
387 }
388
389 static void
390 amstar_support(
391     application_argument_t *argument)
392 {
393     (void)argument;
394     fprintf(stdout, "CONFIG YES\n");
395     fprintf(stdout, "HOST YES\n");
396     fprintf(stdout, "DISK YES\n");
397     fprintf(stdout, "MAX-LEVEL 9\n");
398     fprintf(stdout, "INDEX-LINE YES\n");
399     fprintf(stdout, "INDEX-XML NO\n");
400     fprintf(stdout, "MESSAGE-LINE YES\n");
401     fprintf(stdout, "MESSAGE-XML NO\n");
402     fprintf(stdout, "RECORD YES\n");
403     fprintf(stdout, "INCLUDE-FILE NO\n");
404     fprintf(stdout, "INCLUDE-LIST YES\n");
405     fprintf(stdout, "EXCLUDE-FILE YES\n");
406     fprintf(stdout, "EXCLUDE-LIST YES\n");
407     fprintf(stdout, "COLLECTION NO\n");
408     fprintf(stdout, "MULTI-ESTIMATE YES\n");
409     fprintf(stdout, "CALCSIZE YES\n");
410     fprintf(stdout, "CLIENT-ESTIMATE YES\n");
411 }
412
413 static void
414 amstar_selfcheck(
415     application_argument_t *argument)
416 {
417     if (argument->dle.disk) {
418         char *qdisk = quote_string(argument->dle.disk);
419         fprintf(stdout, "OK disk %s\n", qdisk);
420         amfree(qdisk);
421     }
422
423     fprintf(stdout, "OK amstar version %s\n", VERSION);
424     fprintf(stdout, "OK amstar\n");
425
426     if (argument->dle.device) {
427         char *qdevice = quote_string(argument->dle.device);
428         fprintf(stdout, "OK %s\n", qdevice);
429         amfree(qdevice);
430     }
431     if (star_directory) {
432         char *qdirectory = quote_string(star_directory);
433         fprintf(stdout, "OK %s\n", qdirectory);
434         amfree(qdirectory);
435     }
436
437     if (argument->dle.include_list &&
438         argument->dle.include_list->nb_element >= 0) {
439         fprintf(stdout, "ERROR include-list not supported for backup\n");
440     }
441
442     if (!star_path) {
443         fprintf(stdout, "ERROR STAR-PATH not defined\n");
444     } else {
445         if (check_file(star_path, X_OK)) {
446             char *star_version;
447             GPtrArray *argv_ptr = g_ptr_array_new();
448
449             g_ptr_array_add(argv_ptr, star_path);
450             g_ptr_array_add(argv_ptr, "--version");
451             g_ptr_array_add(argv_ptr, NULL);
452
453             star_version = get_first_line(argv_ptr);
454
455             if (star_version) {
456                 char *sv, *sv1;
457                 for (sv = star_version; *sv && !g_ascii_isdigit(*sv); sv++);
458                 for (sv1 = sv; *sv1 && *sv1 != ' '; sv1++);
459                 *sv1 = '\0';
460                 printf("OK amstar star-version %s\n", sv);
461             } else {
462                 printf(_("ERROR [Can't get %s version]\n"), star_path);
463             }
464             g_ptr_array_free(argv_ptr, TRUE);
465             amfree(star_version);
466
467         }
468     }
469
470     if (argument->calcsize) {
471         char *calcsize = vstralloc(amlibexecdir, "/", "calcsize", NULL);
472         check_file(calcsize, X_OK);
473         check_suid(calcsize);
474         amfree(calcsize);
475     }
476
477     {
478         char *amandates_file;
479         amandates_file = getconf_str(CNF_AMANDATES);
480         check_file(amandates_file, R_OK|W_OK);
481     }
482
483     set_root_privs(1);
484     if (argument->dle.device) {
485         check_dir(argument->dle.device, R_OK);
486     }
487     set_root_privs(0);
488 }
489
490 static void
491 amstar_estimate(
492     application_argument_t *argument)
493 {
494     GPtrArray  *argv_ptr;
495     char       *cmd = NULL;
496     int         nullfd;
497     int         pipefd;
498     FILE       *dumpout = NULL;
499     off_t       size = -1;
500     char        line[32768];
501     char       *errmsg = NULL;
502     char       *qerrmsg;
503     char       *qdisk;
504     amwait_t    wait_status;
505     int         starpid;
506     amregex_t  *rp;
507     times_t     start_time;
508     int         level = 0;
509     GSList     *levels = NULL;
510
511     if (!argument->level) {
512         fprintf(stderr, "ERROR No level argument\n");
513         error(_("No level argument"));
514     }
515     if (!argument->dle.disk) {
516         fprintf(stderr, "ERROR No disk argument\n");
517         error(_("No disk argument"));
518     }
519     if (!argument->dle.device) {
520         fprintf(stderr, "ERROR No device argument\n");
521         error(_("No device argument"));
522     }
523
524     if (argument->dle.include_list &&
525         argument->dle.include_list->nb_element >= 0) {
526         fprintf(stderr, "ERROR include-list not supported for backup\n");
527     }
528
529     if (check_device(argument) == 0) {
530         return;
531     }
532
533     qdisk = quote_string(argument->dle.disk);
534     if (argument->calcsize) {
535         char *dirname;
536
537         if (star_directory) {
538             dirname = star_directory;
539         } else {
540             dirname = argument->dle.device;
541         }
542         run_calcsize(argument->config, "STAR", argument->dle.disk, dirname,
543                      argument->level, NULL, NULL);
544         return;
545     }
546
547     if (!star_path) {
548         errmsg = vstrallocf(_("STAR-PATH not defined"));
549         goto common_error;
550     }
551     cmd = stralloc(star_path);
552
553     start_time = curclock();
554
555     for (levels = argument->level; levels != NULL; levels = levels->next) {
556         level = GPOINTER_TO_INT(levels->data);
557         argv_ptr = amstar_build_argv(argument, level, CMD_ESTIMATE);
558
559         if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
560             errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
561                                 strerror(errno));
562             goto common_error;
563         }
564
565         starpid = pipespawnv(cmd, STDERR_PIPE, 1,
566                              &nullfd, &nullfd, &pipefd,
567                              (char **)argv_ptr->pdata);
568
569         dumpout = fdopen(pipefd,"r");
570         if (!dumpout) {
571             errmsg = vstrallocf(_("Can't fdopen: %s"), strerror(errno));
572             goto common_error;
573         }
574
575         size = (off_t)-1;
576         while (size < 0 && (fgets(line, sizeof(line), dumpout)) != NULL) {
577             if (line[strlen(line)-1] == '\n') /* remove trailling \n */
578                 line[strlen(line)-1] = '\0';
579             if (line[0] == '\0')
580                 continue;
581             dbprintf("%s\n", line);
582             /* check for size match */
583             /*@ignore@*/
584             for(rp = re_table; rp->regex != NULL; rp++) {
585                 if(match(rp->regex, line)) {
586                     if (rp->typ == DMP_SIZE) {
587                         size = ((the_num(line, rp->field)*rp->scale+1023.0)/1024.0);
588                         if(size < 0.0)
589                             size = 1.0;             /* found on NeXT -- sigh */
590                     }
591                     break;
592                 }
593             }
594             /*@end@*/
595         }
596
597         while ((fgets(line, sizeof(line), dumpout)) != NULL) {
598             dbprintf("%s", line);
599         }
600
601         dbprintf(".....\n");
602         dbprintf(_("estimate time for %s level %d: %s\n"),
603                  qdisk,
604                  level,
605                  walltime_str(timessub(curclock(), start_time)));
606         if(size == (off_t)-1) {
607             errmsg = vstrallocf(_("no size line match in %s output"),
608                                 cmd);
609             dbprintf(_("%s for %s\n"), errmsg, qdisk);
610             dbprintf(".....\n");
611         } else if(size == (off_t)0 && argument->level == 0) {
612             dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
613                      cmd, argument->dle.disk);
614             dbprintf(".....\n");
615         }
616         dbprintf(_("estimate size for %s level %d: %lld KB\n"),
617                  qdisk,
618                  level,
619                  (long long)size);
620
621         kill(-starpid, SIGTERM);
622
623         dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
624         waitpid(starpid, &wait_status, 0);
625         if (WIFSIGNALED(wait_status)) {
626             errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
627                                 cmd, WTERMSIG(wait_status), dbfn());
628         } else if (WIFEXITED(wait_status)) {
629             if (WEXITSTATUS(wait_status) != 0) {
630                 errmsg = vstrallocf(_("%s exited with status %d: see %s"),
631                                     cmd, WEXITSTATUS(wait_status), dbfn());
632             } else {
633                 /* Normal exit */
634             }
635         } else {
636             errmsg = vstrallocf(_("%s got bad exit: see %s"), cmd, dbfn());
637         }
638         dbprintf(_("after %s %s wait\n"), cmd, qdisk);
639
640         g_ptr_array_free_full(argv_ptr);
641
642         aclose(nullfd);
643         afclose(dumpout);
644
645         fprintf(stdout, "%d %lld 1\n", level, (long long)size);
646     }
647     amfree(qdisk);
648     amfree(cmd);
649     return;
650
651 common_error:
652     dbprintf("%s\n", errmsg);
653     qerrmsg = quote_string(errmsg);
654     amfree(qdisk);
655     dbprintf("%s", errmsg);
656     fprintf(stdout, "ERROR %s\n", qerrmsg);
657     amfree(errmsg);
658     amfree(qerrmsg);
659     amfree(cmd);
660 }
661
662 static void
663 amstar_backup(
664     application_argument_t *argument)
665 {
666     int        dumpin;
667     char      *cmd = NULL;
668     char      *qdisk;
669     char       line[32768];
670     amregex_t *rp;
671     off_t      dump_size = -1;
672     char      *type;
673     char       startchr;
674     GPtrArray *argv_ptr;
675     int        starpid;
676     int        dataf = 1;
677     int        mesgf = 3;
678     int        indexf = 4;
679     int        outf;
680     FILE      *mesgstream;
681     FILE      *indexstream = NULL;
682     FILE      *outstream;
683     int        level;
684     regex_t    regex_root;
685     regex_t    regex_dir;
686     regex_t    regex_file;
687     regex_t    regex_special;
688     regex_t    regex_symbolic;
689     regex_t    regex_hard;
690
691     mesgstream = fdopen(mesgf, "w");
692     if (!mesgstream) {
693         error(_("error mesgstream(%d): %s\n"), mesgf, strerror(errno));
694     }
695
696     if (!argument->level) {
697         fprintf(mesgstream, "? No level argument\n");
698         error(_("No level argument"));
699     }
700     if (!argument->dle.disk) {
701         fprintf(mesgstream, "? No disk argument\n");
702         error(_("No disk argument"));
703     }
704     if (!argument->dle.device) {
705         fprintf(mesgstream, "? No device argument\n");
706         error(_("No device argument"));
707     }
708
709     if (argument->dle.include_list &&
710         argument->dle.include_list->nb_element >= 0) {
711         fprintf(mesgstream, "? include-list not supported for backup\n");
712     }
713
714     level = GPOINTER_TO_INT(argument->level->data);
715
716     qdisk = quote_string(argument->dle.disk);
717
718     argv_ptr = amstar_build_argv(argument, level, CMD_BACKUP);
719
720     cmd = stralloc(star_path);
721
722     starpid = pipespawnv(cmd, STDIN_PIPE|STDERR_PIPE, 1,
723                          &dumpin, &dataf, &outf, (char **)argv_ptr->pdata);
724
725     g_ptr_array_free_full(argv_ptr);
726     /* close the write ends of the pipes */
727     aclose(dumpin);
728     aclose(dataf);
729     if (argument->dle.create_index) {
730         indexstream = fdopen(indexf, "w");
731         if (!indexstream) {
732             error(_("error indexstream(%d): %s\n"), indexf, strerror(errno));
733         }
734     }
735     outstream = fdopen(outf, "r");
736     if (!outstream) {
737         error(_("error outstream(%d): %s\n"), outf, strerror(errno));
738     }
739
740     regcomp(&regex_root, "^a \\.\\/ directory$", REG_EXTENDED|REG_NEWLINE);
741     regcomp(&regex_dir, "^a (.*) directory$", REG_EXTENDED|REG_NEWLINE);
742     regcomp(&regex_file, "^a (.*) (.*) bytes", REG_EXTENDED|REG_NEWLINE);
743     regcomp(&regex_special, "^a (.*) special", REG_EXTENDED|REG_NEWLINE);
744     regcomp(&regex_symbolic, "^a (.*) symbolic", REG_EXTENDED|REG_NEWLINE);
745     regcomp(&regex_hard, "^a (.*) link to", REG_EXTENDED|REG_NEWLINE);
746
747     while ((fgets(line, sizeof(line), outstream)) != NULL) {
748         regmatch_t regmatch[3];
749
750         if (line[strlen(line)-1] == '\n') /* remove trailling \n */
751             line[strlen(line)-1] = '\0';
752
753         if (regexec(&regex_root, line, 1, regmatch, 0) == 0) {
754             if (argument->dle.create_index)
755                 fprintf(indexstream, "%s\n", "/");
756             continue;
757         }
758
759         if (regexec(&regex_dir, line, 3, regmatch, 0) == 0) {
760             if (argument->dle.create_index && regmatch[1].rm_so == 2) {
761                 line[regmatch[1].rm_eo+1]='\0';
762                 fprintf(indexstream, "/%s\n", &line[regmatch[1].rm_so]);
763             }
764             continue;
765         }
766
767         if (regexec(&regex_file, line, 3, regmatch, 0) == 0 ||
768             regexec(&regex_special, line, 3, regmatch, 0) == 0 ||
769             regexec(&regex_symbolic, line, 3, regmatch, 0) == 0 ||
770             regexec(&regex_hard, line, 3, regmatch, 0) == 0) {
771             if (argument->dle.create_index && regmatch[1].rm_so == 2) {
772                 line[regmatch[1].rm_eo]='\0';
773                 fprintf(indexstream, "/%s\n", &line[regmatch[1].rm_so]);
774             }
775             continue;
776         }
777
778         for (rp = re_table; rp->regex != NULL; rp++) {
779             if (match(rp->regex, line)) {
780                 break;
781             }
782         }
783         if (rp->typ == DMP_SIZE) {
784             dump_size = (off_t)((the_num(line, rp->field)* rp->scale+1023.0)/1024.0);
785         }
786         switch (rp->typ) {
787             case DMP_IGNORE:
788                 continue;
789             case DMP_NORMAL:
790                 type = "normal";
791                 startchr = '|';
792                 break;
793             case DMP_STRANGE:
794                 type = "strange";
795                 startchr = '?';
796                 break;
797             case DMP_SIZE:
798                 type = "size";
799                 startchr = '|';
800                 break;
801             case DMP_ERROR:
802                 type = "error";
803                 startchr = '?';
804                 break;
805             default:
806                 type = "unknown";
807                 startchr = '!';
808                 break;
809         }
810         dbprintf("%3d: %7s(%c): %s\n", rp->srcline, type, startchr, line);
811         fprintf(mesgstream,"%c %s\n", startchr, line);
812     }
813
814     regfree(&regex_root);
815     regfree(&regex_dir);
816     regfree(&regex_file);
817     regfree(&regex_special);
818     regfree(&regex_symbolic);
819     regfree(&regex_hard);
820
821     dbprintf(_("gnutar: %s: pid %ld\n"), cmd, (long)starpid);
822
823     dbprintf("sendbackup: size %lld\n", (long long)dump_size);
824     fprintf(mesgstream, "sendbackup: size %lld\n", (long long)dump_size);
825     dbprintf("sendbackup: end\n");
826     fprintf(mesgstream, "sendbackup: end\n");
827
828     fclose(mesgstream);
829     if (argument->dle.create_index)
830         fclose(indexstream);
831
832     amfree(qdisk);
833     amfree(cmd);
834 }
835
836 static void
837 amstar_restore(
838     application_argument_t *argument)
839 {
840     char       *cmd;
841     GPtrArray  *argv_ptr = g_ptr_array_new();
842     char      **env;
843     int         j;
844     char       *e;
845
846     if (!star_path) {
847         error(_("STAR-PATH not defined"));
848     }
849
850     cmd = stralloc(star_path);
851
852     g_ptr_array_add(argv_ptr, stralloc(star_path));
853     if (star_directory) {
854         struct stat stat_buf;
855         if(stat(star_directory, &stat_buf) != 0) {
856             fprintf(stderr,"can not stat directory %s: %s\n", star_directory, strerror(errno));
857             exit(1);
858         }
859         if (!S_ISDIR(stat_buf.st_mode)) {
860             fprintf(stderr,"%s is not a directory\n", star_directory);
861             exit(1);
862         }
863         if (access(star_directory, W_OK) != 0 ) {
864             fprintf(stderr, "Can't write to %s: %s\n", star_directory, strerror(errno));
865             exit(1);
866         }
867
868         g_ptr_array_add(argv_ptr, stralloc("-C"));
869         g_ptr_array_add(argv_ptr, stralloc(star_directory));
870     }
871     g_ptr_array_add(argv_ptr, stralloc("-x"));
872     g_ptr_array_add(argv_ptr, stralloc("-v"));
873     g_ptr_array_add(argv_ptr, stralloc("-xattr"));
874     g_ptr_array_add(argv_ptr, stralloc("-acl"));
875     g_ptr_array_add(argv_ptr, stralloc("errctl=WARN|SAMEFILE|SETTIME|DIFF|SETACL|SETXATTR|SETMODE|BADACL *"));
876     g_ptr_array_add(argv_ptr, stralloc("-no-fifo"));
877     g_ptr_array_add(argv_ptr, stralloc("-f"));
878     g_ptr_array_add(argv_ptr, stralloc("-"));
879
880     if (argument->dle.exclude_list &&
881         argument->dle.exclude_list->nb_element == 1) {
882         g_ptr_array_add(argv_ptr, stralloc("-exclude-from"));
883         g_ptr_array_add(argv_ptr,
884                         stralloc(argument->dle.exclude_list->first->name));
885     }
886
887     if (argument->dle.include_list &&
888         argument->dle.include_list->nb_element == 1) {
889         FILE *include_list = fopen(argument->dle.include_list->first->name, "r");
890         char  line[2*PATH_MAX+2];
891         while (fgets(line, 2*PATH_MAX, include_list)) {
892             line[strlen(line)-1] = '\0'; /* remove '\n' */
893             if (strncmp(line, "./", 2) == 0)
894                 g_ptr_array_add(argv_ptr, stralloc(line+2)); /* remove ./ */
895             else if (strcmp(line, ".") != 0)
896                 g_ptr_array_add(argv_ptr, stralloc(line));
897         }
898         fclose(include_list);
899     }
900     for (j=1; j< argument->argc; j++) {
901         if (strncmp(argument->argv[j], "./", 2) == 0)
902             g_ptr_array_add(argv_ptr, stralloc(argument->argv[j]+2));/*remove ./ */
903         else if (strcmp(argument->argv[j], ".") != 0)
904             g_ptr_array_add(argv_ptr, stralloc(argument->argv[j]));
905     }
906     g_ptr_array_add(argv_ptr, NULL);
907
908     debug_executing(argv_ptr);
909     env = safe_env();
910     become_root();
911     execve(cmd, (char **)argv_ptr->pdata, env);
912     e = strerror(errno);
913     error(_("error [exec %s: %s]"), cmd, e);
914
915 }
916
917 static void
918 amstar_validate(
919     application_argument_t *argument G_GNUC_UNUSED)
920 {
921     char       *cmd;
922     GPtrArray  *argv_ptr = g_ptr_array_new();
923     char      **env;
924     char       *e;
925     char        buf[32768];
926
927     if (!star_path) {
928         dbprintf("STAR-PATH not set; Piping to /dev/null\n");
929         fprintf(stderr,"STAR-PATH not set; Piping to /dev/null\n");
930         goto pipe_to_null;
931     }
932
933     cmd = stralloc(star_path);
934
935     g_ptr_array_add(argv_ptr, stralloc(star_path));
936     g_ptr_array_add(argv_ptr, stralloc("-t"));
937     g_ptr_array_add(argv_ptr, stralloc("-f"));
938     g_ptr_array_add(argv_ptr, stralloc("-"));
939     g_ptr_array_add(argv_ptr, NULL);
940
941     debug_executing(argv_ptr);
942     env = safe_env();
943     execve(cmd, (char **)argv_ptr->pdata, env);
944     e = strerror(errno);
945     dbprintf("failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
946     fprintf(stderr,"failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
947 pipe_to_null:
948     while (read(0, buf, 32768) > 0) {
949     }
950
951 }
952
953 static GPtrArray *amstar_build_argv(
954     application_argument_t *argument,
955     int   level,
956     int   command)
957 {
958     char      *dirname;
959     char      *fsname;
960     char       levelstr[NUM_STR_SIZE+7];
961     GPtrArray *argv_ptr = g_ptr_array_new();
962     char      *s;
963     char      *tardumpfile;
964     GSList    *copt;
965
966     if (star_directory) {
967         dirname = star_directory;
968     } else {
969         dirname = argument->dle.device;
970     }
971     fsname = vstralloc("fs-name=", dirname, NULL);
972     for (s = fsname; *s != '\0'; s++) {
973         if (iscntrl((int)*s))
974             *s = '-';
975     }
976     snprintf(levelstr, SIZEOF(levelstr), "-level=%d", level);
977
978     if (star_dle_tardumps) {
979         char *sdisk = sanitise_filename(argument->dle.disk);
980         tardumpfile = vstralloc(star_tardumps, sdisk, NULL);
981         amfree(sdisk);
982     } else {
983         tardumpfile = stralloc(star_tardumps);
984     }
985
986     g_ptr_array_add(argv_ptr, stralloc(star_path));
987
988     g_ptr_array_add(argv_ptr, stralloc("-c"));
989     g_ptr_array_add(argv_ptr, stralloc("-f"));
990     if (command == CMD_ESTIMATE) {
991         g_ptr_array_add(argv_ptr, stralloc("/dev/null"));
992     } else {
993         g_ptr_array_add(argv_ptr, stralloc("-"));
994     }
995     g_ptr_array_add(argv_ptr, stralloc("-C"));
996
997 #if defined(__CYGWIN__)
998     {
999         char tmppath[PATH_MAX];
1000
1001         cygwin_conv_to_full_posix_path(dirname, tmppath);
1002         g_ptr_array_add(argv_ptr, stralloc(tmppath));
1003     }
1004 #else
1005     g_ptr_array_add(argv_ptr, stralloc(dirname));
1006 #endif
1007     g_ptr_array_add(argv_ptr, stralloc(fsname));
1008     if (star_onefilesystem)
1009         g_ptr_array_add(argv_ptr, stralloc("-xdev"));
1010     g_ptr_array_add(argv_ptr, stralloc("-link-dirs"));
1011     g_ptr_array_add(argv_ptr, stralloc(levelstr));
1012     g_ptr_array_add(argv_ptr, stralloc2("tardumps=", tardumpfile));
1013     if (command == CMD_BACKUP)
1014         g_ptr_array_add(argv_ptr, stralloc("-wtardumps"));
1015
1016     g_ptr_array_add(argv_ptr, stralloc("-xattr"));
1017     if (star_acl)
1018         g_ptr_array_add(argv_ptr, stralloc("-acl"));
1019     g_ptr_array_add(argv_ptr, stralloc("H=exustar"));
1020     g_ptr_array_add(argv_ptr, stralloc("errctl=WARN|SAMEFILE|DIFF|GROW|SHRINK|SPECIALFILE|GETXATTR|BADACL *"));
1021     if (star_sparse)
1022         g_ptr_array_add(argv_ptr, stralloc("-sparse"));
1023     g_ptr_array_add(argv_ptr, stralloc("-dodesc"));
1024
1025     if (command == CMD_BACKUP && argument->dle.create_index)
1026         g_ptr_array_add(argv_ptr, stralloc("-v"));
1027
1028     if ((argument->dle.exclude_file &&
1029          argument->dle.exclude_file->nb_element >= 1) ||
1030         (argument->dle.exclude_list &&
1031          argument->dle.exclude_list->nb_element >= 1)) {
1032         g_ptr_array_add(argv_ptr, stralloc("-match-tree"));
1033         g_ptr_array_add(argv_ptr, stralloc("-not"));
1034     }
1035     if (argument->dle.exclude_file &&
1036         argument->dle.exclude_file->nb_element >= 1) {
1037         sle_t *excl;
1038         for (excl = argument->dle.exclude_file->first; excl != NULL;
1039              excl = excl->next) {
1040             char *ex;
1041             if (strcmp(excl->name, "./") == 0) {
1042                 ex = g_strdup_printf("pat=%s", excl->name+2);
1043             } else {
1044                 ex = g_strdup_printf("pat=%s", excl->name);
1045             }
1046             g_ptr_array_add(argv_ptr, ex);
1047         }
1048     }
1049     if (argument->dle.exclude_list &&
1050         argument->dle.exclude_list->nb_element >= 1) {
1051         sle_t *excl;
1052         for (excl = argument->dle.exclude_list->first; excl != NULL;
1053              excl = excl->next) {
1054             char *exclname = fixup_relative(excl->name, argument->dle.device);
1055             FILE *exclude;
1056             char *aexc;
1057             if ((exclude = fopen(exclname, "r")) != NULL) {
1058                 while ((aexc = agets(exclude)) != NULL) {
1059                     if (aexc[0] != '\0') {
1060                         char *ex;
1061                         if (strcmp(aexc, "./") == 0) {
1062                             ex = g_strdup_printf("pat=%s", aexc+2);
1063                         } else {
1064                             ex = g_strdup_printf("pat=%s", aexc);
1065                         }
1066                         g_ptr_array_add(argv_ptr, ex);
1067                     }
1068                     amfree(aexc);
1069                 }
1070                 fclose(exclude);
1071             }
1072             amfree(exclname);
1073         }
1074     }
1075
1076     /* It is best to place command_options at the and of command line.
1077      * For example '-find' option requires that it is the last option used.
1078      * See: http://cdrecord.berlios.de/private/man/star/star.1.html
1079      */
1080     for (copt = argument->command_options; copt != NULL; copt = copt->next) {
1081         g_ptr_array_add(argv_ptr, stralloc((char *)copt->data));
1082     }
1083
1084     g_ptr_array_add(argv_ptr, stralloc("."));
1085
1086     g_ptr_array_add(argv_ptr, NULL);
1087
1088     amfree(tardumpfile);
1089     amfree(fsname);
1090
1091     return(argv_ptr);
1092 }
1093
1094 static int
1095 check_device(
1096     application_argument_t *argument)
1097 {
1098     char *qdevice;
1099     struct stat stat_buf;
1100
1101     qdevice = quote_string(argument->dle.device);
1102     set_root_privs(1);
1103     if(!stat(argument->dle.device, &stat_buf)) { 
1104         if (!S_ISDIR(stat_buf.st_mode)) {
1105             set_root_privs(0);
1106             g_fprintf(stderr, _("ERROR %s is not a directory\n"), qdevice);
1107             amfree(qdevice);
1108             return 0;
1109         }
1110     } else {
1111         set_root_privs(0);
1112         g_fprintf(stderr, _("ERROR can not stat %s: %s\n"), qdevice,
1113                   strerror(errno));
1114         amfree(qdevice);
1115         return 0;
1116     }
1117     if (access(argument->dle.device, R_OK|X_OK) == -1) {
1118         set_root_privs(0);
1119         g_fprintf(stderr, _("ERROR can not access %s: %s\n"),
1120                   argument->dle.device, strerror(errno));
1121         amfree(qdevice);
1122         return 0;
1123     }
1124     set_root_privs(0);
1125     amfree(qdevice);
1126     return 1;
1127 }
1128