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