Imported Upstream version 3.3.3
[debian/amanda] / client-src / sendsize.c
1 /*
2  * Amanda, The Advanced Maryland Automatic Network Disk Archiver
3  * Copyright (c) 1991-1998 University of Maryland at College Park
4  * Copyright (c) 2007-2012 Zmanda, Inc.  All Rights Reserved.
5  * All Rights Reserved.
6  *
7  * Permission to use, copy, modify, distribute, and sell this software and its
8  * documentation for any purpose is hereby granted without fee, provided that
9  * the above copyright notice appear in all copies and that both that
10  * copyright notice and this permission notice appear in supporting
11  * documentation, and that the name of U.M. not be used in advertising or
12  * publicity pertaining to distribution of the software without specific,
13  * written prior permission.  U.M. makes no representations about the
14  * suitability of this software for any purpose.  It is provided "as is"
15  * without express or implied warranty.
16  *
17  * U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
18  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
19  * BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
20  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
21  * OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
22  * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
23  *
24  * Authors: the Amanda Development Team.  Its members are listed in a
25  * file named AUTHORS, in the root directory of this distribution.
26  */
27 /* 
28  * $Id: sendsize.c 10421 2008-03-06 18:48:30Z martineau $
29  *
30  * send estimated backup sizes using dump
31  */
32
33 #include "amanda.h"
34 #include "match.h"
35 #include "pipespawn.h"
36 #include "amfeatures.h"
37 #include "amandates.h"
38 #include "clock.h"
39 #include "util.h"
40 #include "getfsent.h"
41 #include "client_util.h"
42 #include "conffile.h"
43
44 #ifdef SAMBA_CLIENT
45 #include "findpass.h"
46 #endif
47
48 #define sendsize_debug(i, ...) do {     \
49         if ((i) <= debug_sebdsize) {    \
50             dbprintf(__VA_ARGS__);      \
51         }                               \
52 } while (0)
53
54 #ifdef HAVE_SETPGID
55 #  define SETPGRP       setpgid(getpid(), getpid())
56 #  define SETPGRP_FAILED() do {                                         \
57     dbprintf(_("setpgid(%ld,%ld) failed: %s\n"),                                \
58               (long)getpid(), (long)getpid(), strerror(errno)); \
59 } while(0)
60
61 #else /* () line 0 */
62 #if defined(SETPGRP_VOID)
63 #  define SETPGRP       setpgrp()
64 #  define SETPGRP_FAILED() do {                                         \
65     dbprintf(_("setpgrp() failed: %s\n"), strerror(errno));             \
66 } while(0)
67
68 #else
69 #  define SETPGRP       setpgrp(0, getpid())
70 #  define SETPGRP_FAILED() do {                                         \
71     dbprintf(_("setpgrp(0,%ld) failed: %s\n"),                          \
72               (long)getpid(), strerror(errno));                 \
73 } while(0)
74
75 #endif
76 #endif
77
78 typedef struct level_estimates_s {
79     time_t dumpsince;
80     int estsize;
81     int needestimate;
82     int server;         /* server can do estimate */
83 } level_estimate_t;
84
85 typedef struct disk_estimates_s {
86     struct disk_estimates_s *next;
87     char *qamname;
88     char *qamdevice;
89     char *dirname;
90     char *qdirname;
91     pid_t child;
92     int done;
93     dle_t *dle;
94     level_estimate_t est[DUMP_LEVELS];
95 } disk_estimates_t;
96
97 disk_estimates_t *est_list;
98
99 static am_feature_t *our_features = NULL;
100 static char *our_feature_string = NULL;
101 static g_option_t *g_options = NULL;
102 static gboolean amandates_started = FALSE;
103
104 /* local functions */
105 int main(int argc, char **argv);
106 void dle_add_diskest(dle_t *dle);
107 void calc_estimates(disk_estimates_t *est);
108 void free_estimates(disk_estimates_t *est);
109 void dump_calc_estimates(disk_estimates_t *);
110 void star_calc_estimates(disk_estimates_t *);
111 void smbtar_calc_estimates(disk_estimates_t *);
112 void gnutar_calc_estimates(disk_estimates_t *);
113 void application_api_calc_estimate(disk_estimates_t *);
114 void generic_calc_estimates(disk_estimates_t *);
115
116 int
117 main(
118     int         argc,
119     char **     argv)
120 {
121     int level;
122     char *dumpdate;
123     disk_estimates_t *est;
124     disk_estimates_t *est1;
125     disk_estimates_t *est_prev;
126     disk_estimates_t *est_next;
127     char *line = NULL;
128     char *s, *fp;
129     int ch;
130     char *err_extra = NULL;
131     int done;
132     int need_wait;
133     int dumpsrunning;
134     char *qdisk = NULL;
135     char *qlist = NULL;
136     char *qamdevice = NULL;
137     dle_t *dle;
138     GSList *errlist;
139     am_level_t *alevel;
140
141     (void)argc; /* Quiet unused parameter warning */
142     (void)argv; /* Quiet unused parameter warning */
143
144     if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
145         printf("sendsize-%s\n", VERSION);
146         return (0);
147     }
148
149     /* initialize */
150
151     /*
152      * Configure program for internationalization:
153      *   1) Only set the message locale for now.
154      *   2) Set textdomain for all amanda related programs to "amanda"
155      *      We don't want to be forced to support dozens of message catalogs.
156      */  
157     setlocale(LC_MESSAGES, "C");
158     textdomain("amanda"); 
159
160     safe_fd(-1, 0);
161     openbsd_fd_inform();
162     safe_cd();
163
164     set_pname("sendsize");
165
166     /* Don't die when child closes pipe */
167     signal(SIGPIPE, SIG_IGN);
168
169     add_amanda_log_handler(amanda_log_stderr);
170     add_amanda_log_handler(amanda_log_syslog);
171     dbopen(DBG_SUBDIR_CLIENT);
172     startclock();
173     dbprintf(_("version %s\n"), VERSION);
174
175     our_features = am_init_feature_set();
176     our_feature_string = am_feature_to_string(our_features);
177
178     config_init(CONFIG_INIT_CLIENT, NULL);
179     /* (check for config errors comes later) */
180
181     check_running_as(RUNNING_AS_CLIENT_LOGIN);
182
183     /* handle all service requests */
184
185     for(; (line = agets(stdin)) != NULL; free(line)) {
186         if (line[0] == '\0')
187             continue;
188         if(strncmp_const(line, "OPTIONS ") == 0) {
189             g_options = parse_g_options(line+8, 1);
190             if(!g_options->hostname) {
191                 g_options->hostname = alloc(MAX_HOSTNAME_LENGTH+1);
192                 gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
193                 g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
194             }
195
196             g_printf("OPTIONS ");
197             if(am_has_feature(g_options->features, fe_rep_options_features)) {
198                 g_printf("features=%s;", our_feature_string);
199             }
200             if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
201                 g_printf("maxdumps=%d;", g_options->maxdumps);
202             }
203             if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
204                 g_printf("hostname=%s;", g_options->hostname);
205             }
206             g_printf("\n");
207             fflush(stdout);
208
209             if (g_options->config) {
210                 /* overlay this configuration on the existing (nameless) configuration */
211                 config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
212                             g_options->config);
213
214                 dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
215             }
216
217             /* check for any config errors now */
218             if (config_errors(&errlist) >= CFGERR_ERRORS) {
219                 char *errstr = config_errors_to_error_string(errlist);
220                 g_printf("%s\n", errstr);
221                 dbclose();
222                 return 1;
223             }
224
225             if (am_has_feature(g_options->features, fe_req_xml)) {
226                 break;
227             }
228             continue;
229         }
230
231         dle = alloc_dle();
232         s = line;
233         ch = *s++;
234
235         skip_whitespace(s, ch);                 /* find the program name */
236         if(ch == '\0') {
237             err_extra = stralloc(_("no program name"));
238             goto err;                           /* no program name */
239         }
240         dle->program = s - 1;
241         skip_non_whitespace(s, ch);
242         s[-1] = '\0';
243
244         dle->program_is_application_api=0;
245         if(strncmp_const(dle->program, "CALCSIZE") == 0) {
246             skip_whitespace(s, ch);             /* find the program name */
247             if(ch == '\0') {
248                 err_extra = stralloc(_("no program name"));
249                 goto err;
250             }
251             dle->estimatelist = g_slist_append(dle->estimatelist,
252                                                GINT_TO_POINTER(ES_CALCSIZE));
253             dle->program = s - 1;
254             skip_non_whitespace(s, ch);
255             s[-1] = '\0';
256             if (strcmp(dle->program,"APPLICATION") == 0) {
257                 dle->program_is_application_api=1;
258                 skip_whitespace(s, ch);         /* find dumper name */
259                 if (ch == '\0') {
260                     goto err;                   /* no program */
261                 }
262                 dle->program = s - 1;
263                 skip_non_whitespace(s, ch);
264                 s[-1] = '\0';
265             }
266         }
267         else {
268             dle->estimatelist = g_slist_append(dle->estimatelist,
269                                                GINT_TO_POINTER(ES_CLIENT));
270             if (strcmp(dle->program,"APPLICATION") == 0) {
271                 dle->program_is_application_api=1;
272                 skip_whitespace(s, ch);         /* find dumper name */
273                 if (ch == '\0') {
274                     goto err;                   /* no program */
275                 }
276                 dle->program = s - 1;
277                 skip_non_whitespace(s, ch);
278                 s[-1] = '\0';
279             }
280         }
281         dle->program = stralloc(dle->program);
282
283         skip_whitespace(s, ch);                 /* find the disk name */
284         if(ch == '\0') {
285             err_extra = stralloc(_("no disk name"));
286             goto err;                           /* no disk name */
287         }
288
289         if (qdisk != NULL)
290             amfree(qdisk);
291
292         fp = s - 1;
293         skip_quoted_string(s, ch);
294         s[-1] = '\0';                           /* terminate the disk name */
295         qdisk = stralloc(fp);
296         dle->disk = unquote_string(qdisk);
297
298         skip_whitespace(s, ch);                 /* find the device or level */
299         if (ch == '\0') {
300             err_extra = stralloc(_("bad level"));
301             goto err;
302         }
303         if(!isdigit((int)s[-1])) {
304             fp = s - 1;
305             skip_quoted_string(s, ch);
306             s[-1] = '\0';
307             qamdevice = stralloc(fp);
308             dle->device = unquote_string(qamdevice);
309             skip_whitespace(s, ch);             /* find level number */
310         }
311         else {
312             dle->device = stralloc(dle->disk);
313             qamdevice = stralloc(qdisk);
314         }
315
316                                                 /* find the level number */
317         if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
318             err_extra = stralloc(_("bad level"));
319             goto err;                           /* bad level */
320         }
321         if (level < 0 || level >= DUMP_LEVELS) {
322             err_extra = stralloc(_("bad level"));
323             goto err;
324         }
325         skip_integer(s, ch);
326         alevel = g_new0(am_level_t, 1);
327         alevel->level = level;
328         dle->levellist = g_slist_append(dle->levellist, alevel);
329
330         skip_whitespace(s, ch);                 /* find the dump date */
331         if(ch == '\0') {
332             err_extra = stralloc(_("no dumpdate"));
333             goto err;                           /* no dumpdate */
334         }
335         dumpdate = s - 1;
336         skip_non_whitespace(s, ch);
337         s[-1] = '\0';
338         (void)dumpdate;                         /* XXX: Set but not used */
339
340         dle->spindle = 0;                       /* default spindle */
341
342         skip_whitespace(s, ch);                 /* find the spindle */
343         if(ch != '\0') {
344             if(sscanf(s - 1, "%d", &dle->spindle) != 1) {
345                 err_extra = stralloc(_("bad spindle"));
346                 goto err;                       /* bad spindle */
347             }
348             skip_integer(s, ch);
349
350             skip_whitespace(s, ch);             /* find the parameters */
351             if(ch != '\0') {
352                 if(strncmp_const(s-1, "OPTIONS |;") == 0) {
353                     parse_options(s + 8,
354                                   dle,
355                                   g_options->features,
356                                   0);
357                 }
358                 else {
359                     while (ch != '\0') {
360                         if(strncmp_const(s-1, "exclude-file=") == 0) {
361                             qlist = unquote_string(s+12);
362                             dle->exclude_file =
363                                 append_sl(dle->exclude_file, qlist);
364                             amfree(qlist);
365                         } else if(strncmp_const(s-1, "exclude-list=") == 0) {
366                             qlist = unquote_string(s+12);
367                             dle->exclude_list =
368                                 append_sl(dle->exclude_list, qlist);
369                             amfree(qlist);
370                         } else if(strncmp_const(s-1, "include-file=") == 0) {
371                             qlist = unquote_string(s+12);
372                             dle->include_file =
373                                 append_sl(dle->include_file, qlist);
374                             amfree(qlist);
375                         } else if(strncmp_const(s-1, "include-list=") == 0) {
376                             qlist = unquote_string(s+12);
377                             dle->include_list =
378                                 append_sl(dle->include_list, qlist);
379                             amfree(qlist);
380                         } else {
381                             err_extra = vstrallocf(_("Invalid parameter (%s)"), s-1);
382                             goto err;           /* should have gotten to end */
383                         }
384                         skip_quoted_string(s, ch);
385                         skip_whitespace(s, ch); /* find the inclusion list */
386                         amfree(qlist);
387                     }
388                 }
389             }
390         }
391
392         /*@ignore@*/
393         dle_add_diskest(dle);
394         /*@end@*/
395     }
396     if (g_options == NULL) {
397         g_printf(_("ERROR [Missing OPTIONS line in sendsize input]\n"));
398         error(_("Missing OPTIONS line in sendsize input\n"));
399         /*NOTREACHED*/
400     }
401     amfree(line);
402
403     if (am_has_feature(g_options->features, fe_req_xml)) {
404         char    *errmsg = NULL;
405         dle_t   *dles, *dle;
406
407         dles = amxml_parse_node_FILE(stdin, &errmsg);
408         if (errmsg) {
409             err_extra = errmsg;
410             goto err;
411         }
412         for (dle = dles; dle != NULL; dle = dle->next) {
413             dle_add_diskest(dle);
414         }
415     }
416
417     if (amandates_started) {
418         finish_amandates();
419         free_amandates();
420         amandates_started = FALSE;
421     }
422
423     est_prev = NULL;
424     for(est = est_list; est != NULL; est = est_next) {
425         int good = merge_dles_properties(est->dle, 0);
426         est_next = est->next;
427         if (!good) {
428             if (est == est_list) {
429                 est_list = est_next;
430             } else {
431                 est_prev->next = est_next;
432             }
433         } else {
434             est_prev = est;
435         }
436     }
437     for(est = est_list; est != NULL; est = est->next) {
438         run_client_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE, g_options, est->dle,
439                            stdout);
440     }
441
442     dumpsrunning = 0;
443     need_wait = 0;
444     done = 0;
445     while(! done) {
446         done = 1;
447         /*
448          * See if we need to wait for a child before we can do anything
449          * else in this pass.
450          */
451         if(need_wait) {
452             pid_t child_pid;
453             amwait_t child_status;
454
455             need_wait = 0;
456             dbprintf(_("waiting for any estimate child: %d running\n"),
457                       dumpsrunning);
458             child_pid = wait(&child_status);
459             if(child_pid == -1) {
460                 error(_("wait failed: %s"), strerror(errno));
461                 /*NOTREACHED*/
462             }
463
464             if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0) {
465                 char *child_name = vstrallocf(_("child %ld"), (long)child_pid);
466                 char *child_status_str = str_exit_status(child_name, child_status);
467                 dbprintf("%s\n", child_status_str);
468                 amfree(child_status_str);
469                 amfree(child_name);
470             }
471
472             /*
473              * Find the child and mark it done.
474              */
475             for(est = est_list; est != NULL; est = est->next) {
476                 if(est->child == child_pid) {
477                     break;
478                 }
479             }
480             if(est == NULL) {
481                 dbprintf(_("unexpected child %ld\n"), (long)child_pid);
482             } else {
483                 est->done = 1;
484                 est->child = 0;
485                 dumpsrunning--;
486                 run_client_scripts(EXECUTE_ON_POST_DLE_ESTIMATE, g_options,
487                                    est->dle, stdout);
488             }
489         }
490         /*
491          * If we are already running the maximum number of children
492          * go back and wait until one of them finishes.
493          */
494         if(dumpsrunning >= g_options->maxdumps) {
495             done = 0;
496             need_wait = 1;
497             continue;                           /* have to wait first */
498         }
499         /*
500          * Find a new child to start.
501          */
502         for(est = est_list; est != NULL; est = est->next) {
503             if(est->done == 0) {
504                 done = 0;                       /* more to do */
505             }
506             if(est->child != 0 || est->done) {
507                 continue;                       /* child is running or done */
508             }
509             /*
510              * Make sure there is no spindle conflict.
511              */
512             if(est->dle->spindle != -1) {
513                 for(est1 = est_list; est1 != NULL; est1 = est1->next) {
514                     if(est1->child == 0 || est == est1 || est1->done) {
515                         /*
516                          * Ignore anything not yet started, ourself,
517                          * and anything completed.
518                          */
519                         continue;
520                     }
521                     if(est1->dle->spindle == est->dle->spindle) {
522                         break;                  /* oops -- they match */
523                     }
524                 }
525                 if(est1 != NULL) {
526                     continue;                   /* spindle conflict */
527                 }
528             }
529             break;                              /* start this estimate */
530         }
531         if(est == NULL) {
532             if(dumpsrunning > 0) {
533                 need_wait = 1;                  /* nothing to do but wait */
534             }
535         } else {
536             done = 0;
537             run_client_scripts(EXECUTE_ON_PRE_DLE_ESTIMATE, g_options,
538                                est->dle, stdout);
539
540             if((est->child = fork()) == 0) {
541                 calc_estimates(est);            /* child does the estimate */
542                 exit(0);
543             } else if(est->child == -1) {
544                 error(_("calc_estimates fork failed: %s"), strerror(errno));
545                 /*NOTREACHED*/
546             }
547             dumpsrunning++;                     /* parent */
548         }
549     }
550
551     for(est = est_list; est != NULL; est = est->next) {
552         run_client_scripts(EXECUTE_ON_POST_HOST_ESTIMATE, g_options, est->dle,
553                            stdout);
554     }
555
556     est_prev = NULL;
557     for(est = est_list; est != NULL; est = est->next) {
558         free_estimates(est);
559         amfree(est_prev);
560         est_prev = est;
561     }
562     amfree(est_prev);
563     amfree(our_feature_string);
564     am_release_feature_set(our_features);
565     our_features = NULL;
566     free_g_options(g_options);
567
568     dbclose();
569     return 0;
570  err:
571     if (err_extra) {
572         g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET '%s'\n"), err_extra);
573         dbprintf(_("REQ packet is bogus: %s\n"), err_extra);
574         amfree(err_extra);
575     } else {
576         g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET\n"));
577         dbprintf(_("REQ packet is bogus\n"));
578     }
579
580     free_g_options(g_options);
581
582     dbclose();
583     return 1;
584 }
585
586
587 void
588 dle_add_diskest(
589     dle_t    *dle)
590 {
591     disk_estimates_t *newp, *curp;
592     amandates_t *amdp;
593     int dumplev, estlev;
594     time_t dumpdate;
595     levellist_t levellist;
596     char *amandates_file;
597     gboolean need_amandates = FALSE;
598     estimatelist_t el;
599
600     if (dle->levellist == NULL) {
601         g_printf(_("ERROR Missing level in request\n"));
602         return;
603     }
604
605     /* should we use amandates for this? */
606     for (el = dle->estimatelist; el != NULL; el=el->next) {
607         estimate_t estimate = (estimate_t)GPOINTER_TO_INT(el->data);
608         if (estimate == ES_CALCSIZE)
609             need_amandates = TRUE;
610     }
611
612     if (strcmp(dle->program, "GNUTAR") == 0) {
613         /* GNUTAR only needs amandates if gnutar_list_dir is NULL */
614         char *gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
615         if (!gnutar_list_dir || !*gnutar_list_dir)
616             need_amandates = TRUE;
617     }
618
619     /* start amandates here, before adding this DLE to est_list, in case
620      * we encounter an error. */
621     if (need_amandates) {
622         if (!amandates_started) {
623             amandates_file = getconf_str(CNF_AMANDATES);
624             if(!start_amandates(amandates_file, 0)) {
625                 char *errstr = strerror(errno);
626                 char *qamname = quote_string(dle->disk);
627                 char *errmsg = vstrallocf(_("could not open %s: %s"), amandates_file, errstr);
628                 char *qerrmsg = quote_string(errmsg);
629                 g_printf(_("%s %d ERROR %s\n"), qamname, 0, qerrmsg);
630                 amfree(qamname);
631                 amfree(errmsg);
632                 amfree(qerrmsg);
633                 return;
634             }
635             amandates_started = TRUE;
636         }
637     }
638
639     levellist = dle->levellist;
640     while (levellist != NULL) {
641         am_level_t *alevel = (am_level_t *)levellist->data;
642         if (alevel->level < 0)
643             alevel->level = 0;
644         if (alevel->level >= DUMP_LEVELS)
645             alevel->level = DUMP_LEVELS - 1;
646         levellist = g_slist_next(levellist);
647     }
648
649     for(curp = est_list; curp != NULL; curp = curp->next) {
650         if(strcmp(curp->dle->disk, dle->disk) == 0) {
651             /* already have disk info, just note the level request */
652             levellist = dle->levellist;
653             while (levellist != NULL) {
654                 am_level_t *alevel = (am_level_t *)levellist->data;
655                 int      level  = alevel->level;
656                 curp->est[level].needestimate = 1;
657                 curp->est[level].server = alevel->server;
658                 levellist = g_slist_next(levellist);
659             }
660
661             return;
662         }
663     }
664
665     newp = (disk_estimates_t *) alloc(SIZEOF(disk_estimates_t));
666     memset(newp, 0, SIZEOF(*newp));
667     newp->next = est_list;
668     est_list = newp;
669     newp->qamname = quote_string(dle->disk);
670     if (dle->device) {
671         newp->qamdevice = quote_string(dle->device);
672         newp->dirname = amname_to_dirname(dle->device);
673         newp->qdirname = quote_string(newp->dirname);
674     } else {
675         newp->qamdevice = stralloc("");
676         newp->dirname = stralloc("");
677         newp->qdirname = stralloc("");
678     }
679     levellist = dle->levellist;
680     while (levellist != NULL) {
681         am_level_t *alevel = (am_level_t *)levellist->data;
682         newp->est[alevel->level].needestimate = 1;
683         newp->est[alevel->level].server = alevel->server;
684         levellist = g_slist_next(levellist);
685     }
686     newp->dle = dle;
687
688     /* fill in dump-since dates */
689     if (need_amandates) {
690         amdp = amandates_lookup(newp->dle->disk);
691
692         newp->est[0].dumpsince = EPOCH;
693         for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
694             dumpdate = amdp->dates[dumplev];
695             for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
696                 if(dumpdate > newp->est[estlev].dumpsince)
697                     newp->est[estlev].dumpsince = dumpdate;
698             }
699         }
700     } else {
701         /* just zero everything out */
702         for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
703             newp->est[dumplev].dumpsince = 0;
704         }
705     }
706 }
707
708
709 void
710 free_estimates(
711     disk_estimates_t *  est)
712 {
713     amfree(est->qamname);
714     amfree(est->qamdevice);
715     amfree(est->dirname);
716     amfree(est->qdirname);
717     if (est->dle) {
718         free_dle(est->dle);
719     }
720 }
721
722 /*
723  * ------------------------------------------------------------------------
724  *
725  */
726
727 void
728 calc_estimates(
729     disk_estimates_t *  est)
730 {
731     dbprintf(_("calculating for amname %s, dirname %s, spindle %d %s\n"),
732              est->qamname, est->qdirname, est->dle->spindle, est->dle->program);
733
734     if(est->dle->program_is_application_api ==  1)
735         application_api_calc_estimate(est);
736     else {
737         estimatelist_t el;
738         estimate_t     estimate;
739         int            level;
740         estimate_t     estimate_method = ES_ES;
741         estimate_t     client_method = ES_ES;
742
743         /* find estimate method to use */
744         for (el = est->dle->estimatelist; el != NULL; el = el->next) {
745             estimate = (estimate_t)GPOINTER_TO_INT(el->data);
746             if (estimate == ES_SERVER) {
747                 if (estimate_method == ES_ES)
748                     estimate_method = ES_SERVER;
749             }
750             if (estimate == ES_CLIENT || 
751                 (estimate == ES_CALCSIZE &&
752                  (est->dle->device[0] != '/' || est->dle->device[1] != '/'))) {
753                 if (client_method == ES_ES)
754                     client_method = estimate;
755                 if (estimate_method == ES_ES)
756                     estimate_method = estimate;
757             }
758         }
759
760         /* do server estimate */
761         if (estimate_method == ES_SERVER) {
762             for (level = 0; level < DUMP_LEVELS; level++) {
763                 if (est->est[level].needestimate) {
764                     if (est->est[level].server || client_method == ES_ES) {
765                         g_printf(_("%s %d SIZE -1\n"), est->qamname, level);
766                         est->est[level].needestimate = 0;
767                     }
768                 }
769             }
770         }
771
772         if (client_method == ES_ES && estimate_method != ES_SERVER) {
773             g_printf(_("%s %d SIZE -2\n"), est->qamname, 0);
774             dbprintf(_("Can't use CALCSIZE for samba estimate: %s %s\n"),
775                      est->qamname, est->qdirname);
776         } else if (client_method == ES_CALCSIZE) {
777             generic_calc_estimates(est);
778         } else if (client_method == ES_CLIENT) {
779 #ifndef USE_GENERIC_CALCSIZE
780             if (strcmp(est->dle->program, "DUMP") == 0)
781                 dump_calc_estimates(est);
782             else
783 #endif
784 #ifdef SAMBA_CLIENT
785             if (strcmp(est->dle->program, "GNUTAR") == 0 &&
786                 est->dle->device[0] == '/' && est->dle->device[1] == '/')
787                 smbtar_calc_estimates(est);
788             else
789 #endif
790 #ifdef GNUTAR
791             if (strcmp(est->dle->program, "GNUTAR") == 0)
792                 gnutar_calc_estimates(est);
793             else
794 #endif
795                 dbprintf(_("Invalid program: %s %s %s\n"),
796                          est->qamname, est->qdirname, est->dle->program);
797         }
798     }
799
800     dbprintf(_("done with amname %s dirname %s spindle %d\n"),
801               est->qamname, est->qdirname, est->dle->spindle);
802 }
803
804 /*
805  * ------------------------------------------------------------------------
806  *
807  */
808
809 /* local functions */
810 off_t getsize_dump(dle_t *dle, int level, char **errmsg);
811 off_t getsize_smbtar(dle_t *dle, int level, char **errmsg);
812 off_t getsize_gnutar(dle_t *dle, int level, time_t dumpsince, char **errmsg);
813 off_t getsize_application_api(disk_estimates_t *est, int nb_level,
814                               int *levels, backup_support_option_t *bsu);
815 off_t handle_dumpline(char *str);
816 double first_num(char *str);
817
818 void
819 application_api_calc_estimate(
820     disk_estimates_t *  est)
821 {
822     int    level;
823     int    i;
824     int    levels[DUMP_LEVELS];
825     int    nb_level = 0;
826     backup_support_option_t *bsu;
827     GPtrArray               *errarray;
828     estimatelist_t el;
829     estimate_t     estimate;
830     estimate_t     estimate_method = ES_ES;
831     estimate_t     client_method = ES_ES;
832     int            has_calcsize = 0;
833     int            has_client = 0;
834
835     bsu = backup_support_option(est->dle->program, g_options, est->dle->disk,
836                                 est->dle->device, &errarray);
837     if (!bsu) {
838         guint  i;
839         for (i=0; i < errarray->len; i++) {
840             char  *line;
841             char  *errmsg;
842             char  *qerrmsg;
843             line = g_ptr_array_index(errarray, i);
844             if(am_has_feature(g_options->features,
845                               fe_rep_sendsize_quoted_error)) {
846                 errmsg = g_strdup_printf(_("Application '%s': %s"),
847                                      est->dle->program, line);
848                 qerrmsg = quote_string(errmsg);
849                 for (level = 0; level < DUMP_LEVELS; level++) {
850                     if (est->est[level].needestimate) {
851                         g_printf(_("%s %d ERROR %s\n"),
852                                  est->dle->disk, level, qerrmsg);
853                         dbprintf(_("%s %d ERROR %s\n"),
854                                  est->qamname, level, qerrmsg);
855                     }
856                 }
857                 amfree(errmsg);
858                 amfree(qerrmsg);
859             }
860         }
861         if (i == 0) { /* nothing in errarray */
862             char  *errmsg;
863             char  *qerrmsg;
864             errmsg = g_strdup_printf(
865                 _("Application '%s': cannon execute support command"),
866                 est->dle->program);
867             qerrmsg = quote_string(errmsg);
868             for (level = 0; level < DUMP_LEVELS; level++) {
869                 if (est->est[level].needestimate) {
870                     g_printf(_("%s %d ERROR %s\n"),
871                              est->dle->disk, level, qerrmsg);
872                     dbprintf(_("%s %d ERROR %s\n"),
873                              est->qamname, level, qerrmsg);
874                 }
875             }
876             amfree(errmsg);
877             amfree(qerrmsg);
878         }
879         for (level = 0; level < DUMP_LEVELS; level++) {
880             est->est[level].needestimate = 0;
881         }
882         g_ptr_array_free(errarray, TRUE);
883     }
884
885     if (est->dle->data_path == DATA_PATH_AMANDA &&
886         (bsu->data_path_set & DATA_PATH_AMANDA)==0) {
887         g_printf("%s %d ERROR application %s doesn't support amanda data-path\n", est->qamname, 0, est->dle->program);
888         amfree(bsu);
889         return;
890     }
891     if (est->dle->data_path == DATA_PATH_DIRECTTCP &&
892         (bsu->data_path_set & DATA_PATH_DIRECTTCP)==0) {
893         g_printf("%s %d ERROR application %s doesn't support directtcp data-path\n", est->qamname, 0, est->dle->program);
894         amfree(bsu);
895         return;
896     }
897
898     /* find estimate method to use */
899     for (el = est->dle->estimatelist; el != NULL; el = el->next) {
900         estimate = (estimate_t)GPOINTER_TO_INT(el->data);
901         if (estimate == ES_CLIENT)
902             has_client = 1;
903         if (estimate == ES_CALCSIZE)
904             has_calcsize = 1;
905         if (estimate == ES_SERVER) {
906             if (estimate_method == ES_ES)
907                 estimate_method = ES_SERVER;
908         }
909         if ((estimate == ES_CLIENT && bsu->client_estimate) || 
910             (estimate == ES_CALCSIZE && bsu->calcsize)) {
911             if (client_method == ES_ES)
912                 client_method = estimate;
913             if (estimate_method == ES_ES)
914                 estimate_method = estimate;
915         }
916     }
917
918     for(level = 0; level < DUMP_LEVELS; level++) {
919         if (est->est[level].needestimate) {
920             if (level > bsu->max_level) {
921                 /* planner will not even consider this level */
922                 g_printf("%s %d SIZE %lld\n", est->qamname, level,
923                          (long long)-2);
924                 est->est[level].needestimate = 0;
925                 dbprintf(_("Application '%s' can't estimate level %d\n"),
926                          est->dle->program, level);
927             } else if (estimate_method == ES_ES) {
928                 g_printf("%s %d SIZE %lld\n", est->qamname, level,
929                          (long long)-2);
930                 est->est[level].needestimate = 0;
931                 if (am_has_feature(g_options->features,
932                                    fe_rep_sendsize_quoted_error)) {
933                     char *errmsg, *qerrmsg;
934                     if (has_client && !bsu->client_estimate &&
935                         has_calcsize && !bsu->calcsize) {
936                         errmsg = vstrallocf(_("Application '%s' can't do CLIENT or CALCSIZE estimate"),
937                                             est->dle->program);
938                     } else if (has_client && !bsu->client_estimate) {
939                         errmsg = vstrallocf(_("Application '%s' can't do CLIENT estimate"),
940                                             est->dle->program);
941                     } else if (has_calcsize && !bsu->calcsize) {
942                         errmsg = vstrallocf(_("Application '%s' can't do CALCSIZE estimate"),
943                                             est->dle->program);
944                     } else {
945                         errmsg = vstrallocf(_("Application '%s' can't do estimate"),
946                                             est->dle->program);
947                     }
948                     qerrmsg = quote_string(errmsg);
949                     dbprintf(_("errmsg is %s\n"), errmsg);
950                     g_printf("%s %d ERROR %s\n",
951                              est->qamname, 0, qerrmsg);
952                     amfree(errmsg);
953                     amfree(qerrmsg);
954                 }
955             } else if (estimate_method == ES_SERVER &&
956                        (est->est[level].server || client_method == ES_ES)) {
957                 /* planner will consider this level, */
958                 /* but use a server-side estimate    */
959                 g_printf("%s %d SIZE -1\n", est->qamname, level);
960                 est->est[level].needestimate = 0;
961             } else if (client_method == ES_CLIENT) {
962                 levels[nb_level++] = level;
963             } else if (client_method == ES_CALCSIZE) {
964                 levels[nb_level++] = level;
965             }
966         }
967     }
968
969     if (nb_level == 0) {
970         amfree(bsu);
971         return;
972     }
973
974     if (bsu->multi_estimate) {
975         for (i=0;i<nb_level;i++) {
976             dbprintf(_("getting size via application API for %s %s level %d\n"),
977                      est->qamname, est->qamdevice, levels[i]);
978         }
979         getsize_application_api(est, nb_level, levels, bsu);
980
981     } else {
982         for(level = 0; level < DUMP_LEVELS; level++) {
983             if (est->est[level].needestimate) {
984                 dbprintf(
985                     _("getting size via application API for %s %s level %d\n"),
986                     est->qamname, est->qamdevice, level);
987                 levels[0] = level;
988                 getsize_application_api(est, 1, levels, bsu);
989             }
990         }
991     }
992
993     amfree(bsu);
994 }
995
996
997 void
998 generic_calc_estimates(
999     disk_estimates_t *  est)
1000 {
1001     int pipefd = -1, nullfd = -1;
1002     char *cmd;
1003     char *cmdline;
1004     char *command;
1005     GPtrArray *argv_ptr = g_ptr_array_new();
1006     char number[NUM_STR_SIZE];
1007     unsigned int i;
1008     int level;
1009     pid_t calcpid;
1010     int nb_exclude = 0;
1011     int nb_include = 0;
1012     char *file_exclude = NULL;
1013     char *file_include = NULL;
1014     times_t start_time;
1015     FILE *dumpout = NULL;
1016     char *line = NULL;
1017     char *match_expr;
1018     amwait_t wait_status;
1019     char *errmsg = NULL, *qerrmsg;
1020     char tmppath[PATH_MAX];
1021     int len;
1022
1023     cmd = vstralloc(amlibexecdir, "/", "calcsize", NULL);
1024
1025     g_ptr_array_add(argv_ptr, stralloc("calcsize"));
1026     if (g_options->config)
1027         g_ptr_array_add(argv_ptr, stralloc(g_options->config));
1028     else
1029         g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
1030
1031     g_ptr_array_add(argv_ptr, stralloc(est->dle->program));
1032     canonicalize_pathname(est->dle->disk, tmppath);
1033     g_ptr_array_add(argv_ptr, stralloc(tmppath));
1034     canonicalize_pathname(est->dirname, tmppath);
1035     g_ptr_array_add(argv_ptr, stralloc(tmppath));
1036
1037     if (est->dle->exclude_file)
1038         nb_exclude += est->dle->exclude_file->nb_element;
1039     if (est->dle->exclude_list)
1040         nb_exclude += est->dle->exclude_list->nb_element;
1041     if (est->dle->include_file)
1042         nb_include += est->dle->include_file->nb_element;
1043     if (est->dle->include_list)
1044         nb_include += est->dle->include_list->nb_element;
1045
1046     if (nb_exclude > 0)
1047         file_exclude = build_exclude(est->dle, 0);
1048     if (nb_include > 0)
1049         file_include = build_include(est->dle, 0);
1050
1051     if(file_exclude) {
1052         g_ptr_array_add(argv_ptr, stralloc("-X"));
1053         g_ptr_array_add(argv_ptr, stralloc(file_exclude));
1054     }
1055
1056     if(file_include) {
1057         g_ptr_array_add(argv_ptr, stralloc("-I"));
1058         g_ptr_array_add(argv_ptr, stralloc(file_include));
1059     }
1060     start_time = curclock();
1061
1062     command = (char *)g_ptr_array_index(argv_ptr, 0);
1063     cmdline = stralloc(command);
1064     for(i = 1; i < argv_ptr->len-1; i++) {
1065         cmdline = vstrextend(&cmdline, " ",
1066                              (char *)g_ptr_array_index(argv_ptr, i), NULL);
1067     }
1068     dbprintf(_("running: \"%s\"\n"), cmdline);
1069     amfree(cmdline);
1070
1071     for(level = 0; level < DUMP_LEVELS; level++) {
1072         if(est->est[level].needestimate) {
1073             g_snprintf(number, SIZEOF(number), "%d", level);
1074             g_ptr_array_add(argv_ptr, stralloc(number));
1075             dbprintf(" %s", number);
1076             g_snprintf(number, SIZEOF(number),
1077                         "%ld", (long)est->est[level].dumpsince);
1078             g_ptr_array_add(argv_ptr, stralloc(number));
1079             dbprintf(" %s", number);
1080         }
1081     }
1082     g_ptr_array_add(argv_ptr, NULL);
1083     dbprintf("\n");
1084
1085     fflush(stderr); fflush(stdout);
1086
1087     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1088         errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
1089                             strerror(errno));
1090         dbprintf("%s\n", errmsg);
1091         goto common_exit;
1092     }
1093
1094     calcpid = pipespawnv(cmd, STDERR_PIPE, 0,
1095                          &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
1096     amfree(cmd);
1097
1098     dumpout = fdopen(pipefd,"r");
1099     if (!dumpout) {
1100         error(_("Can't fdopen: %s"), strerror(errno));
1101         /*NOTREACHED*/
1102     }
1103     match_expr = vstralloc(" %d SIZE %lld", NULL);
1104     len = strlen(est->qamname);
1105     for(; (line = agets(dumpout)) != NULL; free(line)) {
1106         long long size_ = (long long)0;
1107         if (line[0] == '\0' || (int)strlen(line) <= len)
1108             continue;
1109         /* Don't use sscanf for est->qamname because it can have a '%'. */
1110         if (strncmp(line, est->qamname, len) == 0 &&
1111             sscanf(line+len, match_expr, &level, &size_) == 2) {
1112             g_printf("%s\n", line); /* write to amandad */
1113             dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1114                       est->qamname,
1115                       level,
1116                       size_);
1117         }
1118     }
1119     amfree(match_expr);
1120
1121     dbprintf(_("waiting for %s %s child (pid=%d)\n"),
1122              command, est->qamdevice, (int)calcpid);
1123     waitpid(calcpid, &wait_status, 0);
1124     if (WIFSIGNALED(wait_status)) {
1125         errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1126                             "calcsize", WTERMSIG(wait_status),
1127                             dbfn());
1128     } else if (WIFEXITED(wait_status)) {
1129         if (WEXITSTATUS(wait_status) != 0) {
1130             errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1131                                 "calcsize", WEXITSTATUS(wait_status),
1132                                 dbfn());
1133         } else {
1134             /* Normal exit */
1135         }
1136     } else {
1137         errmsg = vstrallocf(_("%s got bad exit: see %s"),
1138                              "calcsize", dbfn());
1139     }
1140     dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
1141              command, est->qamdevice,
1142               (int)calcpid, WEXITSTATUS(wait_status));
1143
1144     dbprintf(_(".....\n"));
1145     dbprintf(_("estimate time for %s: %s\n"),
1146               est->qamname,
1147               walltime_str(timessub(curclock(), start_time)));
1148
1149 common_exit:
1150     if (errmsg && errmsg[0] != '\0') {
1151         if(am_has_feature(g_options->features, fe_rep_sendsize_quoted_error)) {
1152             qerrmsg = quote_string(errmsg);
1153             dbprintf(_("errmsg is %s\n"), errmsg);
1154             g_printf("%s %d ERROR %s\n",
1155                     est->qamname, 0, qerrmsg);
1156             amfree(qerrmsg);
1157         }
1158     }
1159     amfree(errmsg);
1160     g_ptr_array_free_full(argv_ptr);
1161     amfree(cmd);
1162 }
1163
1164
1165 void
1166 dump_calc_estimates(
1167     disk_estimates_t *  est)
1168 {
1169     int level;
1170     off_t size;
1171     char *errmsg=NULL, *qerrmsg;
1172
1173     for(level = 0; level < DUMP_LEVELS; level++) {
1174         if(est->est[level].needestimate) {
1175             dbprintf(_("getting size via dump for %s level %d\n"),
1176                       est->qamname, level);
1177             size = getsize_dump(est->dle, level, &errmsg);
1178
1179             amflock(1, "size");
1180
1181             g_printf(_("%s %d SIZE %lld\n"),
1182                    est->qamname, level, (long long)size);
1183             if (errmsg && errmsg[0] != '\0') {
1184                 if(am_has_feature(g_options->features,
1185                                   fe_rep_sendsize_quoted_error)) {
1186                     qerrmsg = quote_string(errmsg);
1187                     dbprintf(_("errmsg is %s\n"), errmsg);
1188                     g_printf("%s %d ERROR %s\n",
1189                            est->qamname, level, qerrmsg);
1190                     amfree(qerrmsg);
1191                 }
1192             }
1193             amfree(errmsg);
1194             fflush(stdout);
1195
1196             amfunlock(1, "size");
1197         }
1198     }
1199 }
1200
1201 #ifdef SAMBA_CLIENT
1202 void
1203 smbtar_calc_estimates(
1204     disk_estimates_t *  est)
1205 {
1206     int level;
1207     off_t size;
1208     char  *errmsg = NULL, *qerrmsg;
1209
1210     for(level = 0; level < DUMP_LEVELS; level++) {
1211         if(est->est[level].needestimate) {
1212             dbprintf(_("getting size via smbclient for %s level %d\n"),
1213                       est->qamname, level);
1214             size = getsize_smbtar(est->dle, level, &errmsg);
1215
1216             amflock(1, "size");
1217
1218             g_printf(_("%s %d SIZE %lld\n"),
1219                    est->qamname, level, (long long)size);
1220             if (errmsg && errmsg[0] != '\0') {
1221                 if(am_has_feature(g_options->features,
1222                                   fe_rep_sendsize_quoted_error)) {
1223                     qerrmsg = quote_string(errmsg);
1224                     dbprintf(_("errmsg is %s\n"), errmsg);
1225                     g_printf("%s %d ERROR %s\n",
1226                            est->qamname, level, qerrmsg);
1227                     amfree(qerrmsg);
1228                 }
1229             }
1230             amfree(errmsg);
1231             fflush(stdout);
1232
1233             amfunlock(1, "size");
1234         }
1235     }
1236 }
1237 #endif
1238
1239 #ifdef GNUTAR
1240 void
1241 gnutar_calc_estimates(
1242     disk_estimates_t *  est)
1243 {
1244     int level;
1245     off_t size;
1246     char *errmsg = NULL, *qerrmsg;
1247
1248     for(level = 0; level < DUMP_LEVELS; level++) {
1249         if (est->est[level].needestimate) {
1250             dbprintf(_("getting size via gnutar for %s level %d\n"),
1251                       est->qamname, level);
1252             size = getsize_gnutar(est->dle, level,
1253                                   est->est[level].dumpsince,
1254                                   &errmsg);
1255
1256             amflock(1, "size");
1257
1258             g_printf(_("%s %d SIZE %lld\n"),
1259                    est->qamname, level, (long long)size);
1260             if (errmsg && errmsg[0] != '\0') {
1261                 if(am_has_feature(g_options->features,
1262                                   fe_rep_sendsize_quoted_error)) {
1263                     qerrmsg = quote_string(errmsg);
1264                     dbprintf(_("errmsg is %s\n"), errmsg);
1265                     g_printf(_("%s %d ERROR %s\n"),
1266                            est->qamname, level, qerrmsg);
1267                     amfree(qerrmsg);
1268                 }
1269             }
1270             amfree(errmsg);
1271             fflush(stdout);
1272
1273             amfunlock(1, "size");
1274         }
1275     }
1276 }
1277 #endif
1278
1279 typedef struct regex_scale_s {
1280     char *regex;
1281     int scale;
1282 } regex_scale_t;
1283
1284 /*@ignore@*/
1285 regex_scale_t re_size[] = {
1286 #ifdef DUMP
1287     {"  DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
1288     {"  DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
1289     {"  DUMP: [Ee]stimated [0-9][0-9]* bytes", 1},              /* Ultrix 4.4 */
1290     {" UFSDUMP: estimated [0-9][0-9]* blocks", 512},            /* NEC EWS-UX */
1291     {"dump: Estimate: [0-9][0-9]* tape blocks", 1024},              /* OSF/1 */
1292     {"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
1293     {"backup: estimated [0-9][0-9]* 1k blocks", 1024},                /* AIX */
1294     {"backup: estimated [0-9][0-9]* tape blocks", 1024},              /* AIX */
1295     {"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024},  /* AIX */
1296     {"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024},  /* AIX */
1297     {"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
1298                                                               /* DU 4.0 dump  */
1299     {"dump: Dumping [0-9][0-9]* bytes, ", 1},                 /* DU 4.0 vdump */
1300     {"DUMP: estimated [0-9][0-9]* KB output", 1024},                  /* HPUX */
1301     {"DUMP: estimated [0-9][0-9]* KB\\.", 1024},                    /* NetApp */
1302     {"  UFSDUMP: estimated [0-9][0-9]* blocks", 512},                /* Sinix */
1303
1304 #ifdef HAVE_DUMP_ESTIMATE
1305     {"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
1306                                                            /* DU 3.2g dump -E */
1307     {"^[0-9][0-9]* blocks$", 1024},                        /* DU 4.0 dump  -E */
1308     {"^[0-9][0-9]*$", 1},                               /* Solaris ufsdump -S */
1309 #endif
1310 #endif
1311
1312 #ifdef VDUMP
1313     {"vdump: Dumping [0-9][0-9]* bytes, ", 1},                 /* OSF/1 vdump */
1314 #endif
1315     
1316 #ifdef VXDUMP
1317     {"vxdump: estimated [0-9][0-9]* blocks", 512},           /* HPUX's vxdump */
1318     {"  VXDUMP: estimated [0-9][0-9]* blocks", 512},                 /* Sinix */
1319 #endif
1320
1321 #ifdef XFSDUMP
1322     {"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1},  /* Irix 6.2 xfs */
1323 #endif
1324
1325 #ifdef GNUTAR
1326     {"Total bytes written: [0-9][0-9]*", 1},                /* Gnutar client */
1327 #endif
1328
1329 #ifdef SAMBA_CLIENT
1330 #if SAMBA_VERSION >= 2
1331 #define SAMBA_DEBUG_LEVEL "0"
1332     {"Total number of bytes: [0-9][0-9]*", 1},                   /* Samba du */
1333 #else
1334 #define SAMBA_DEBUG_LEVEL "3"
1335     {"Total bytes listed: [0-9][0-9]*", 1},                     /* Samba dir */
1336 #endif
1337 #endif
1338
1339     { NULL, 0 }
1340 };
1341 /*@end@*/
1342
1343 off_t
1344 getsize_dump(
1345     dle_t      *dle,
1346     int         level,
1347     char      **errmsg)
1348 {
1349     int pipefd[2], nullfd, stdoutfd, killctl[2];
1350     pid_t dumppid;
1351     off_t size;
1352     FILE *dumpout;
1353     char *dumpkeys = NULL;
1354     char *device = NULL;
1355     char *fstype = NULL;
1356     char *cmd = NULL;
1357     char *name = NULL;
1358     char *line = NULL;
1359     char *rundump_cmd = NULL;
1360     char level_str[NUM_STR_SIZE];
1361     int s;
1362     times_t start_time;
1363     char *qdisk = quote_string(dle->disk);
1364     char *qdevice;
1365     char *config;
1366     amwait_t wait_status;
1367 #if defined(DUMP) || defined(VDUMP) || defined(VXDUMP) || defined(XFSDUMP)
1368     int is_rundump = 1;
1369 #endif
1370
1371     if (level > 9)
1372         return -2; /* planner will not even consider this level */
1373
1374     g_snprintf(level_str, SIZEOF(level_str), "%d", level);
1375
1376     device = amname_to_devname(dle->device);
1377     qdevice = quote_string(device);
1378     fstype = amname_to_fstype(dle->device);
1379
1380     dbprintf(_("calculating for device %s with %s\n"),
1381               qdevice, fstype);
1382
1383     cmd = vstralloc(amlibexecdir, "/rundump", NULL);
1384     rundump_cmd = stralloc(cmd);
1385     if (g_options->config)
1386         config = g_options->config;
1387     else
1388         config = "NOCONFIG";
1389     if ((stdoutfd = nullfd = open("/dev/null", O_RDWR)) == -1) {
1390         *errmsg = vstrallocf(_("getsize_dump could not open /dev/null: %s"),
1391                              strerror(errno));
1392         dbprintf("%s\n", *errmsg);
1393         amfree(cmd);
1394         amfree(rundump_cmd);
1395         amfree(fstype);
1396         amfree(device);
1397         amfree(qdevice);
1398         amfree(qdisk);
1399         return(-1);
1400     }
1401     pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
1402     if (pipe(pipefd) < 0) {
1403         *errmsg = vstrallocf(_("getsize_dump could create data pipes: %s"),
1404                              strerror(errno));
1405         dbprintf("%s\n", *errmsg);
1406         amfree(cmd);
1407         amfree(rundump_cmd);
1408         amfree(fstype);
1409         amfree(device);
1410         amfree(qdevice);
1411         amfree(qdisk);
1412         return(-1);
1413     }
1414
1415 #ifdef XFSDUMP                                          /* { */
1416 #ifdef DUMP                                             /* { */
1417     if (strcmp(fstype, "xfs") == 0)
1418 #else                                                   /* } { */
1419     if (1)
1420 #endif                                                  /* } */
1421     {
1422         name = stralloc(" (xfsdump)");
1423         dbprintf(_("running \"%s%s -F -J -l %s - %s\"\n"),
1424                   cmd, name, level_str, qdevice);
1425     }
1426     else
1427 #endif                                                  /* } */
1428 #ifdef VXDUMP                                           /* { */
1429 #ifdef DUMP                                             /* { */
1430     if (strcmp(fstype, "vxfs") == 0)
1431 #else                                                   /* } { */
1432     if (1)
1433 #endif                                                  /* } */
1434     {
1435 #ifdef USE_RUNDUMP
1436         name = stralloc(" (vxdump)");
1437 #else
1438         name = stralloc("");
1439         cmd = newstralloc(cmd, VXDUMP);
1440         config = skip_argument;
1441         is_rundump = 0;
1442 #endif
1443         dumpkeys = vstralloc(level_str, "s", "f", NULL);
1444         dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1445                   cmd, name, dumpkeys, qdevice);
1446     }
1447     else
1448 #endif                                                  /* } */
1449 #ifdef VDUMP                                            /* { */
1450 #ifdef DUMP                                             /* { */
1451     if (strcmp(fstype, "advfs") == 0)
1452 #else                                                   /* } { */
1453     if (1)
1454 #endif                                                  /* } */
1455     {
1456         name = stralloc(" (vdump)");
1457         dumpkeys = vstralloc(level_str, "b", "f", NULL);
1458         dbprintf(_("running \"%s%s %s 60 - %s\"\n"),
1459                   cmd, name, dumpkeys, qdevice);
1460     }
1461     else
1462 #endif                                                  /* } */
1463 #ifdef DUMP                                             /* { */
1464     if (1) {
1465 # ifdef USE_RUNDUMP                                     /* { */
1466 #  ifdef AIX_BACKUP                                     /* { */
1467         name = stralloc(" (backup)");
1468 #  else                                                 /* } { */
1469         name = vstralloc(" (", DUMP, ")", NULL);
1470 #  endif                                                /* } */
1471 # else                                                  /* } { */
1472         name = stralloc("");
1473         cmd = newstralloc(cmd, DUMP);
1474         config = skip_argument;
1475         is_rundump = 0;
1476 # endif                                                 /* } */
1477
1478 # ifdef AIX_BACKUP                                      /* { */
1479         dumpkeys = vstralloc("-", level_str, "f", NULL);
1480         dbprintf(_("running \"%s%s %s - %s\"\n"),
1481                   cmd, name, dumpkeys, qdevice);
1482 # else                                                  /* } { */
1483 #  ifdef HAVE_DUMP_ESTIMATE
1484 #    define PARAM_DUMP_ESTIMATE HAVE_DUMP_ESTIMATE
1485 #  else
1486 #    define PARAM_DUMP_ESTIMATE ""
1487 #  endif
1488 #  ifdef HAVE_HONOR_NODUMP
1489 #    define PARAM_HONOR_NODUMP "h"
1490 #  else
1491 #    define PARAM_HONOR_NODUMP ""
1492 #  endif
1493         dumpkeys = vstralloc(level_str,
1494                              PARAM_DUMP_ESTIMATE,
1495                              PARAM_HONOR_NODUMP,
1496                              "s", "f", NULL);
1497
1498 #  ifdef HAVE_DUMP_ESTIMATE
1499         stdoutfd = pipefd[1];
1500 #  endif
1501
1502 #  ifdef HAVE_HONOR_NODUMP                              /* { */
1503         dbprintf(_("running \"%s%s %s 0 1048576 - %s\"\n"),
1504                   cmd, name, dumpkeys, qdevice);
1505 #  else                                                 /* } { */
1506         dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
1507                   cmd, name, dumpkeys, qdevice);
1508 #  endif                                                /* } */
1509 # endif                                                 /* } */
1510     }
1511     else
1512 #endif                                                  /* } */
1513     {
1514         error(_("no dump program available"));
1515         /*NOTREACHED*/
1516     }
1517
1518     if (pipe(killctl) < 0) {
1519         dbprintf(_("Could not create pipe: %s\n"), strerror(errno));
1520         /* Message will be printed later... */
1521         killctl[0] = killctl[1] = -1;
1522     }
1523
1524     start_time = curclock();
1525     switch(dumppid = fork()) {
1526     case -1:
1527         *errmsg = vstrallocf(_("cannot fork for killpgrp: %s"),
1528                              strerror(errno));
1529         dbprintf("%s\n", *errmsg);
1530         amfree(dumpkeys);
1531         amfree(cmd);
1532         amfree(rundump_cmd);
1533         amfree(device);
1534         amfree(qdevice);
1535         amfree(qdisk);
1536         amfree(name);
1537         amfree(fstype);
1538         return -1;
1539     default:
1540         break; 
1541     case 0:     /* child process */
1542         if(SETPGRP == -1)
1543             SETPGRP_FAILED();
1544         else if (killctl[0] == -1 || killctl[1] == -1)
1545             dbprintf(_("Trying without killpgrp\n"));
1546         else {
1547             switch(fork()) {
1548             case -1:
1549                 dbprintf(_("fork failed, trying without killpgrp\n"));
1550                 break;
1551
1552             default:
1553             {
1554                 char *config;
1555                 char *killpgrp_cmd = vstralloc(amlibexecdir, "/killpgrp", NULL);
1556                 dbprintf(_("running %s\n"), killpgrp_cmd);
1557                 dup2(killctl[0], 0);
1558                 dup2(nullfd, 1);
1559                 dup2(nullfd, 2);
1560                 close(pipefd[0]);
1561                 close(pipefd[1]);
1562                 close(killctl[1]);
1563                 close(nullfd);
1564                 if (g_options->config)
1565                     config = g_options->config;
1566                 else
1567                     config = "NOCONFIG";
1568                 safe_fd(-1, 0);
1569                 execle(killpgrp_cmd, killpgrp_cmd, config, (char *)0,
1570                        safe_env());
1571                 dbprintf(_("cannot execute %s: %s\n"),
1572                           killpgrp_cmd, strerror(errno));
1573                 exit(-1);
1574             }
1575
1576             case 0:  /* child process */
1577                 break;
1578             }
1579         }
1580
1581         dup2(nullfd, 0);
1582         dup2(stdoutfd, 1);
1583         dup2(pipefd[1], 2);
1584         aclose(pipefd[0]);
1585         if (killctl[0] != -1)
1586             aclose(killctl[0]);
1587         if (killctl[1] != -1)
1588             aclose(killctl[1]);
1589         safe_fd(-1, 0);
1590
1591 #ifdef XFSDUMP
1592 #ifdef DUMP
1593         if (strcmp(fstype, "xfs") == 0)
1594 #else
1595         if (1)
1596 #endif
1597             if (is_rundump)
1598                 execle(cmd, "rundump", config, "xfsdump", "-F", "-J", "-l",
1599                        level_str, "-", device, (char *)0, safe_env());
1600             else
1601                 execle(cmd, "xfsdump", "-F", "-J", "-l",
1602                        level_str, "-", device, (char *)0, safe_env());
1603         else
1604 #endif
1605 #ifdef VXDUMP
1606 #ifdef DUMP
1607         if (strcmp(fstype, "vxfs") == 0)
1608 #else
1609         if (1)
1610 #endif
1611             if (is_rundump)
1612                 execle(cmd, "rundump", config, "vxdump", dumpkeys, "1048576",
1613                        "-", device, (char *)0, safe_env());
1614             else
1615                 execle(cmd, "vxdump", dumpkeys, "1048576", "-",
1616                        device, (char *)0, safe_env());
1617         else
1618 #endif
1619 #ifdef VDUMP
1620 #ifdef DUMP
1621         if (strcmp(fstype, "advfs") == 0)
1622 #else
1623         if (1)
1624 #endif
1625             if (is_rundump)
1626                 execle(cmd, "rundump", config, "vdump", dumpkeys, "60", "-",
1627                        device, (char *)0, safe_env());
1628             else
1629                 execle(cmd, "vdump", dumpkeys, "60", "-",
1630                        device, (char *)0, safe_env());
1631         else
1632 #endif
1633 #ifdef DUMP
1634 # ifdef AIX_BACKUP
1635             if (is_rundump)
1636                 execle(cmd, "rundump", config, "backup", dumpkeys, "-",
1637                        device, (char *)0, safe_env());
1638             else
1639                 execle(cmd, "backup", dumpkeys, "-",
1640                        device, (char *)0, safe_env());
1641 # else
1642             if (is_rundump) {
1643                 execle(cmd, "rundump", config, "dump", dumpkeys, 
1644 #ifdef HAVE_HONOR_NODUMP
1645                        "0",
1646 #endif
1647                        "1048576", "-", device, (char *)0, safe_env());
1648             } else {
1649                 execle(cmd, "dump", dumpkeys, 
1650 #ifdef HAVE_HONOR_NODUMP
1651                        "0",
1652 #endif
1653                        "1048576", "-", device, (char *)0, safe_env());
1654             }
1655 # endif
1656 #endif
1657         {
1658             error(_("exec %s failed or no dump program available: %s"),
1659                   cmd, strerror(errno));
1660             /*NOTREACHED*/
1661         }
1662     }
1663
1664     amfree(dumpkeys);
1665     amfree(rundump_cmd);
1666
1667     aclose(pipefd[1]);
1668     if (killctl[0] != -1)
1669         aclose(killctl[0]);
1670     dumpout = fdopen(pipefd[0],"r");
1671     if (!dumpout) {
1672         error(_("Can't fdopen: %s"), strerror(errno));
1673         /*NOTREACHED*/
1674     }
1675
1676     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1677         if (line[0] == '\0')
1678             continue;
1679         dbprintf("%s\n", line);
1680         size = handle_dumpline(line);
1681         if(size > (off_t)-1) {
1682             amfree(line);
1683             while ((line = agets(dumpout)) != NULL) {
1684                 if (line[0] != '\0')
1685                     break;
1686                 amfree(line);
1687             }
1688             if (line != NULL) {
1689                 dbprintf("%s\n", line);
1690             }
1691             break;
1692         }
1693     }
1694     amfree(line);
1695
1696     dbprintf(".....\n");
1697     dbprintf(_("estimate time for %s level %d: %s\n"),
1698               qdisk,
1699               level,
1700               walltime_str(timessub(curclock(), start_time)));
1701     if(size == (off_t)-1) {
1702         *errmsg = vstrallocf(_("no size line match in %s%s output"),
1703                              cmd, name);
1704         dbprintf(_("%s for %s\n"),
1705                   *errmsg, qdisk);
1706
1707         dbprintf(".....\n");
1708         dbprintf(_("Run %s%s manually to check for errors\n"),
1709                     cmd, name);
1710     } else if(size == (off_t)0 && level == 0) {
1711         dbprintf(_("possible %s%s problem -- is \"%s\" really empty?\n"),
1712                   cmd, name, dle->disk);
1713         dbprintf(".....\n");
1714     } else {
1715             dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1716               qdisk,
1717               level,
1718               (long long)size);
1719     }
1720
1721     if (killctl[1] != -1) {
1722         dbprintf(_("asking killpgrp to terminate\n"));
1723         aclose(killctl[1]);
1724         for(s = 5; s > 0; --s) {
1725             sleep(1);
1726             if (waitpid(dumppid, NULL, WNOHANG) != -1)
1727                 goto terminated;
1728         }
1729     }
1730     
1731     /*
1732      * First, try to kill the dump process nicely.  If it ignores us
1733      * for several seconds, hit it harder.
1734      */
1735     dbprintf(_("sending SIGTERM to process group %ld\n"), (long)dumppid);
1736     if (kill(-dumppid, SIGTERM) == -1) {
1737         dbprintf(_("kill failed: %s\n"), strerror(errno));
1738     }
1739     /* Now check whether it dies */
1740     for(s = 5; s > 0; --s) {
1741         sleep(1);
1742         if (waitpid(dumppid, NULL, WNOHANG) != -1)
1743             goto terminated;
1744     }
1745
1746     dbprintf(_("sending SIGKILL to process group %ld\n"), (long)dumppid);
1747     if (kill(-dumppid, SIGKILL) == -1) {
1748         dbprintf(_("kill failed: %s\n"), strerror(errno));
1749     }
1750     for(s = 5; s > 0; --s) {
1751         sleep(1);
1752         if (waitpid(dumppid, NULL, WNOHANG) != -1)
1753             goto terminated;
1754     }
1755
1756     dbprintf(_("waiting for %s%s \"%s\" child\n"), cmd, name, qdisk);
1757     waitpid(dumppid, &wait_status, 0);
1758     if (WIFSIGNALED(wait_status)) {
1759         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
1760                              cmd, WTERMSIG(wait_status), dbfn());
1761     } else if (WIFEXITED(wait_status)) {
1762         if (WEXITSTATUS(wait_status) != 0) {
1763             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
1764                                  cmd, WEXITSTATUS(wait_status), dbfn());
1765         } else {
1766             /* Normal exit */
1767         }
1768     } else {
1769         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
1770                              cmd, dbfn());
1771     }
1772     dbprintf(_("after %s%s %s wait\n"), cmd, name, qdisk);
1773
1774  terminated:
1775
1776     aclose(nullfd);
1777     afclose(dumpout);
1778
1779     amfree(device);
1780     amfree(qdevice);
1781     amfree(qdisk);
1782     amfree(fstype);
1783
1784     amfree(cmd);
1785     amfree(name);
1786
1787     return size;
1788 }
1789
1790 #ifdef SAMBA_CLIENT
1791 off_t
1792 getsize_smbtar(
1793     dle_t      *dle,
1794     int         level,
1795     char      **errmsg)
1796 {
1797     int pipefd = -1, nullfd = -1, passwdfd = -1;
1798     pid_t dumppid;
1799     off_t size;
1800     FILE *dumpout;
1801     char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
1802     char *share = NULL, *subdir = NULL;
1803     size_t lpass;
1804     char *pwtext;
1805     size_t pwtext_len;
1806     char *line;
1807     char *pw_fd_env;
1808     times_t start_time;
1809     char *error_pn = NULL;
1810     char *qdisk = quote_string(dle->disk);
1811     amwait_t wait_status;
1812
1813     error_pn = stralloc2(get_pname(), "-smbclient");
1814
1815     if (level > 1)
1816         return -2; /* planner will not even consider this level */
1817
1818     parsesharename(dle->device, &share, &subdir);
1819     if (!share) {
1820         amfree(share);
1821         amfree(subdir);
1822         set_pname(error_pn);
1823         amfree(error_pn);
1824         error(_("cannot parse disk entry %s for share/subdir"), qdisk);
1825         /*NOTREACHED*/
1826     }
1827     if ((subdir) && (SAMBA_VERSION < 2)) {
1828         amfree(share);
1829         amfree(subdir);
1830         set_pname(error_pn);
1831         amfree(error_pn);
1832         error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
1833         /*NOTREACHED*/
1834     }
1835     if ((user_and_password = findpass(share, &domain)) == NULL) {
1836
1837         if(domain) {
1838             memset(domain, '\0', strlen(domain));
1839             amfree(domain);
1840         }
1841         set_pname(error_pn);
1842         amfree(error_pn);
1843         error(_("cannot find password for %s"), dle->disk);
1844         /*NOTREACHED*/
1845     }
1846     lpass = strlen(user_and_password);
1847     if ((pwtext = strchr(user_and_password, '%')) == NULL) {
1848         memset(user_and_password, '\0', (size_t)lpass);
1849         amfree(user_and_password);
1850         if(domain) {
1851             memset(domain, '\0', strlen(domain));
1852             amfree(domain);
1853         }
1854         set_pname(error_pn);
1855         amfree(error_pn);
1856         error(_("password field not \'user%%pass\' for %s"), dle->disk);
1857         /*NOTREACHED*/
1858     }
1859     *pwtext++ = '\0';
1860     pwtext_len = strlen(pwtext);
1861     if ((sharename = makesharename(share, 0)) == NULL) {
1862         memset(user_and_password, '\0', (size_t)lpass);
1863         amfree(user_and_password);
1864         if(domain) {
1865             memset(domain, '\0', strlen(domain));
1866             amfree(domain);
1867         }
1868         set_pname(error_pn);
1869         amfree(error_pn);
1870         error(_("cannot make share name of %s"), share);
1871         /*NOTREACHED*/
1872     }
1873     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
1874         memset(user_and_password, '\0', (size_t)lpass);
1875         amfree(user_and_password);
1876         if(domain) {
1877             memset(domain, '\0', strlen(domain));
1878             amfree(domain);
1879         }
1880         set_pname(error_pn);
1881         amfree(error_pn);
1882         amfree(sharename);
1883         error(_("could not open /dev/null: %s\n"),
1884               strerror(errno));
1885         /*NOTREACHED*/
1886     }
1887
1888 #if SAMBA_VERSION >= 2
1889     if (level == 0)
1890         tarkeys = "archive 0;recurse;du";
1891     else
1892         tarkeys = "archive 1;recurse;du";
1893 #else
1894     if (level == 0)
1895         tarkeys = "archive 0;recurse;dir";
1896     else
1897         tarkeys = "archive 1;recurse;dir";
1898 #endif
1899
1900     start_time = curclock();
1901
1902     if (pwtext_len > 0) {
1903         pw_fd_env = "PASSWD_FD";
1904     } else {
1905         pw_fd_env = "dummy_PASSWD_FD";
1906     }
1907     dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE, 0,
1908               &nullfd, &nullfd, &pipefd, 
1909               pw_fd_env, &passwdfd,
1910               "smbclient",
1911               sharename,
1912               "-d", SAMBA_DEBUG_LEVEL,
1913               *user_and_password ? "-U" : skip_argument,
1914               *user_and_password ? user_and_password : skip_argument,
1915               "-E",
1916               domain ? "-W" : skip_argument,
1917               domain ? domain : skip_argument,
1918 #if SAMBA_VERSION >= 2
1919               subdir ? "-D" : skip_argument,
1920               subdir ? subdir : skip_argument,
1921 #endif
1922               "-c", tarkeys,
1923               NULL);
1924     if(domain) {
1925         memset(domain, '\0', strlen(domain));
1926         amfree(domain);
1927     }
1928     aclose(nullfd);
1929     if(pwtext_len > 0 && full_write(passwdfd, pwtext, pwtext_len) < pwtext_len) {
1930         int save_errno = errno;
1931
1932         memset(user_and_password, '\0', (size_t)lpass);
1933         amfree(user_and_password);
1934         aclose(passwdfd);
1935         set_pname(error_pn);
1936         amfree(error_pn);
1937         error(_("password write failed: %s"), strerror(save_errno));
1938         /*NOTREACHED*/
1939     }
1940     memset(user_and_password, '\0', (size_t)lpass);
1941     amfree(user_and_password);
1942     aclose(passwdfd);
1943     amfree(sharename);
1944     amfree(share);
1945     amfree(subdir);
1946     amfree(error_pn);
1947     dumpout = fdopen(pipefd,"r");
1948     if (!dumpout) {
1949         error(_("Can't fdopen: %s"), strerror(errno));
1950         /*NOTREACHED*/
1951     }
1952
1953     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
1954         if (line[0] == '\0')
1955             continue;
1956         dbprintf("%s\n", line);
1957         size = handle_dumpline(line);
1958         if(size > -1) {
1959             amfree(line);
1960             while ((line = agets(dumpout)) != NULL) {
1961                 if (line[0] != '\0')
1962                     break;
1963                 amfree(line);
1964             }
1965             if(line != NULL) {
1966                 dbprintf("%s\n", line);
1967             }
1968             break;
1969         }
1970     }
1971     amfree(line);
1972
1973     dbprintf(".....\n");
1974     dbprintf(_("estimate time for %s level %d: %s\n"),
1975               qdisk,
1976               level,
1977               walltime_str(timessub(curclock(), start_time)));
1978     if(size == (off_t)-1) {
1979         *errmsg = vstrallocf(_("no size line match in %s output"),
1980                              SAMBA_CLIENT);
1981         dbprintf(_("%s for %s\n"),
1982                   *errmsg, qdisk);
1983         dbprintf(".....\n");
1984     } else if(size == (off_t)0 && level == 0) {
1985         dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
1986                   SAMBA_CLIENT, dle->disk);
1987         dbprintf(".....\n");
1988     }
1989     dbprintf(_("estimate size for %s level %d: %lld KB\n"),
1990               qdisk,
1991               level,
1992               (long long)size);
1993
1994     kill(-dumppid, SIGTERM);
1995
1996     dbprintf(_("waiting for %s \"%s\" child\n"), SAMBA_CLIENT, qdisk);
1997     waitpid(dumppid, &wait_status, 0);
1998     if (WIFSIGNALED(wait_status)) {
1999         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2000                              SAMBA_CLIENT, WTERMSIG(wait_status), dbfn());
2001     } else if (WIFEXITED(wait_status)) {
2002         if (WEXITSTATUS(wait_status) != 0) {
2003             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
2004                                  SAMBA_CLIENT, WEXITSTATUS(wait_status),
2005                                  dbfn());
2006         } else {
2007             /* Normal exit */
2008         }
2009     } else {
2010         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2011                              SAMBA_CLIENT, dbfn());
2012     }
2013     dbprintf(_("after %s %s wait\n"), SAMBA_CLIENT, qdisk);
2014
2015     afclose(dumpout);
2016     pipefd = -1;
2017
2018     amfree(error_pn);
2019     amfree(qdisk);
2020
2021     return size;
2022 }
2023 #endif
2024
2025 #ifdef GNUTAR
2026 off_t
2027 getsize_gnutar(
2028     dle_t      *dle,
2029     int         level,
2030     time_t      dumpsince,
2031     char      **errmsg)
2032 {
2033     int pipefd = -1, nullfd = -1;
2034     pid_t dumppid;
2035     off_t size = (off_t)-1;
2036     FILE *dumpout = NULL;
2037     char *incrname = NULL;
2038     char *basename = NULL;
2039     char *dirname = NULL;
2040     char *inputname = NULL;
2041     FILE *in = NULL;
2042     FILE *out = NULL;
2043     char *line = NULL;
2044     char *cmd = NULL;
2045     char *command = NULL;
2046     char dumptimestr[80];
2047     struct tm *gmtm;
2048     int nb_exclude = 0;
2049     int nb_include = 0;
2050     GPtrArray *argv_ptr = g_ptr_array_new();
2051     char *file_exclude = NULL;
2052     char *file_include = NULL;
2053     times_t start_time;
2054     int infd, outfd;
2055     ssize_t nb;
2056     char buf[32768];
2057     char *qdisk = quote_string(dle->disk);
2058     char *gnutar_list_dir;
2059     amwait_t wait_status;
2060     char tmppath[PATH_MAX];
2061
2062     if (level > 9)
2063         return -2; /* planner will not even consider this level */
2064
2065     if(dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
2066     if(dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
2067     if(dle->include_file) nb_include += dle->include_file->nb_element;
2068     if(dle->include_list) nb_include += dle->include_list->nb_element;
2069
2070     if(nb_exclude > 0) file_exclude = build_exclude(dle, 0);
2071     if(nb_include > 0) file_include = build_include(dle, 0);
2072
2073     gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
2074     if (strlen(gnutar_list_dir) == 0)
2075         gnutar_list_dir = NULL;
2076     if (gnutar_list_dir) {
2077         char number[NUM_STR_SIZE];
2078         int baselevel;
2079         char *sdisk = sanitise_filename(dle->disk);
2080
2081         basename = vstralloc(gnutar_list_dir,
2082                              "/",
2083                              g_options->hostname,
2084                              sdisk,
2085                              NULL);
2086         amfree(sdisk);
2087
2088         g_snprintf(number, SIZEOF(number), "%d", level);
2089         incrname = vstralloc(basename, "_", number, ".new", NULL);
2090         unlink(incrname);
2091
2092         /*
2093          * Open the listed incremental file from the previous level.  Search
2094          * backward until one is found.  If none are found (which will also
2095          * be true for a level 0), arrange to read from /dev/null.
2096          */
2097         baselevel = level;
2098         infd = -1;
2099         while (infd == -1) {
2100             if (--baselevel >= 0) {
2101                 g_snprintf(number, SIZEOF(number), "%d", baselevel);
2102                 inputname = newvstralloc(inputname,
2103                                          basename, "_", number, NULL);
2104             } else {
2105                 inputname = newstralloc(inputname, "/dev/null");
2106             }
2107             if ((infd = open(inputname, O_RDONLY)) == -1) {
2108
2109                 *errmsg = vstrallocf(_("gnutar: error opening %s: %s"),
2110                                      inputname, strerror(errno));
2111                 dbprintf("%s\n", *errmsg);
2112                 if (baselevel < 0) {
2113                     goto common_exit;
2114                 }
2115                 amfree(*errmsg);
2116             }
2117         }
2118
2119         /*
2120          * Copy the previous listed incremental file to the new one.
2121          */
2122         if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
2123             *errmsg = vstrallocf(_("opening %s: %s"),
2124                                  incrname, strerror(errno));
2125             dbprintf("%s\n", *errmsg);
2126             goto common_exit;
2127         }
2128
2129         while ((nb = read(infd, &buf, SIZEOF(buf))) > 0) {
2130             if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
2131                 *errmsg = vstrallocf(_("writing to %s: %s"),
2132                                      incrname, strerror(errno));
2133                 dbprintf("%s\n", *errmsg);
2134                 goto common_exit;
2135             }
2136         }
2137
2138         if (nb < 0) {
2139             *errmsg = vstrallocf(_("reading from %s: %s"),
2140                                  inputname, strerror(errno));
2141             dbprintf("%s\n", *errmsg);
2142             goto common_exit;
2143         }
2144
2145         if (close(infd) != 0) {
2146             *errmsg = vstrallocf(_("closing %s: %s"),
2147                                  inputname, strerror(errno));
2148             dbprintf("%s\n", *errmsg);
2149             goto common_exit;
2150         }
2151         if (close(outfd) != 0) {
2152             *errmsg = vstrallocf(_("closing %s: %s"),
2153                                  incrname, strerror(errno));
2154             dbprintf("%s\n", *errmsg);
2155             goto common_exit;
2156         }
2157
2158         amfree(inputname);
2159         amfree(basename);
2160     }
2161
2162     gmtm = gmtime(&dumpsince);
2163     g_snprintf(dumptimestr, SIZEOF(dumptimestr),
2164                 "%04d-%02d-%02d %2d:%02d:%02d GMT",
2165                 gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
2166                 gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
2167
2168     dirname = amname_to_dirname(dle->device);
2169
2170     cmd = vstralloc(amlibexecdir, "/", "runtar", NULL);
2171     g_ptr_array_add(argv_ptr, stralloc("runtar"));
2172     if (g_options->config)
2173         g_ptr_array_add(argv_ptr, stralloc(g_options->config));
2174     else
2175         g_ptr_array_add(argv_ptr, stralloc("NOCONFIG"));
2176
2177 #ifdef GNUTAR
2178     g_ptr_array_add(argv_ptr, stralloc(GNUTAR));
2179 #else
2180     g_ptr_array_add(argv_ptr, stralloc("tar"));
2181 #endif
2182     g_ptr_array_add(argv_ptr, stralloc("--create"));
2183     g_ptr_array_add(argv_ptr, stralloc("--file"));
2184     g_ptr_array_add(argv_ptr, stralloc("/dev/null"));
2185     /* use --numeric-owner for estimates, to reduce the number of user/group
2186      * lookups required */
2187     g_ptr_array_add(argv_ptr, stralloc("--numeric-owner"));
2188     g_ptr_array_add(argv_ptr, stralloc("--directory"));
2189     canonicalize_pathname(dirname, tmppath);
2190     g_ptr_array_add(argv_ptr, stralloc(tmppath));
2191     g_ptr_array_add(argv_ptr, stralloc("--one-file-system"));
2192     if (gnutar_list_dir) {
2193         g_ptr_array_add(argv_ptr, stralloc("--listed-incremental"));
2194         g_ptr_array_add(argv_ptr, stralloc(incrname));
2195     } else {
2196         g_ptr_array_add(argv_ptr, stralloc("--incremental"));
2197         g_ptr_array_add(argv_ptr, stralloc("--newer"));
2198         g_ptr_array_add(argv_ptr, stralloc(dumptimestr));
2199     }
2200 #ifdef ENABLE_GNUTAR_ATIME_PRESERVE
2201     /* --atime-preserve causes gnutar to call
2202      * utime() after reading files in order to
2203      * adjust their atime.  However, utime()
2204      * updates the file's ctime, so incremental
2205      * dumps will think the file has changed. */
2206     g_ptr_array_add(argv_ptr, stralloc("--atime-preserve"));
2207 #endif
2208     g_ptr_array_add(argv_ptr, stralloc("--sparse"));
2209     g_ptr_array_add(argv_ptr, stralloc("--ignore-failed-read"));
2210     g_ptr_array_add(argv_ptr, stralloc("--totals"));
2211
2212     if(file_exclude) {
2213         g_ptr_array_add(argv_ptr, stralloc("--exclude-from"));
2214         g_ptr_array_add(argv_ptr, stralloc(file_exclude));
2215     }
2216
2217     if(file_include) {
2218         g_ptr_array_add(argv_ptr, stralloc("--files-from"));
2219         g_ptr_array_add(argv_ptr, stralloc(file_include));
2220     }
2221     else {
2222         g_ptr_array_add(argv_ptr, stralloc("."));
2223     }
2224     g_ptr_array_add(argv_ptr, NULL);
2225
2226     start_time = curclock();
2227
2228     if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
2229         *errmsg = vstrallocf(_("Cannot access /dev/null : %s"),
2230                              strerror(errno));
2231         dbprintf("%s\n", *errmsg);
2232         goto common_exit;
2233     }
2234
2235     command = (char *)g_ptr_array_index(argv_ptr, 0);
2236     dumppid = pipespawnv(cmd, STDERR_PIPE, 0,
2237                          &nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
2238
2239     dumpout = fdopen(pipefd,"r");
2240     if (!dumpout) {
2241         error(_("Can't fdopen: %s"), strerror(errno));
2242         /*NOTREACHED*/
2243     }
2244
2245     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2246         if (line[0] == '\0')
2247             continue;
2248         dbprintf("%s\n", line);
2249         size = handle_dumpline(line);
2250         if(size > (off_t)-1) {
2251             amfree(line);
2252             while ((line = agets(dumpout)) != NULL) {
2253                 if (line[0] != '\0') {
2254                     break;
2255                 }
2256                 amfree(line);
2257             }
2258             if (line != NULL) {
2259                 dbprintf("%s\n", line);
2260                 break;
2261             }
2262             break;
2263         }
2264     }
2265     amfree(line);
2266
2267     dbprintf(".....\n");
2268     dbprintf(_("estimate time for %s level %d: %s\n"),
2269               qdisk,
2270               level,
2271               walltime_str(timessub(curclock(), start_time)));
2272     if(size == (off_t)-1) {
2273         *errmsg = vstrallocf(_("no size line match in %s output"),
2274                              command);
2275         dbprintf(_("%s for %s\n"), *errmsg, qdisk);
2276         dbprintf(".....\n");
2277     } else if(size == (off_t)0 && level == 0) {
2278         dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
2279                   command, dle->disk);
2280         dbprintf(".....\n");
2281     }
2282     dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2283               qdisk,
2284               level,
2285               (long long)size);
2286
2287     kill(-dumppid, SIGTERM);
2288
2289     dbprintf(_("waiting for %s \"%s\" child\n"),
2290              command, qdisk);
2291     waitpid(dumppid, &wait_status, 0);
2292     if (WIFSIGNALED(wait_status)) {
2293         *errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2294                              cmd, WTERMSIG(wait_status), dbfn());
2295     } else if (WIFEXITED(wait_status)) {
2296         if (WEXITSTATUS(wait_status) != 0) {
2297             *errmsg = vstrallocf(_("%s exited with status %d: see %s"),
2298                                  cmd, WEXITSTATUS(wait_status), dbfn());
2299         } else {
2300             /* Normal exit */
2301         }
2302     } else {
2303         *errmsg = vstrallocf(_("%s got bad exit: see %s"),
2304                              cmd, dbfn());
2305     }
2306     dbprintf(_("after %s %s wait\n"), command, qdisk);
2307
2308 common_exit:
2309
2310     if (incrname) {
2311         unlink(incrname);
2312     }
2313     amfree(incrname);
2314     amfree(basename);
2315     amfree(dirname);
2316     amfree(inputname);
2317     g_ptr_array_free_full(argv_ptr);
2318     amfree(qdisk);
2319     amfree(cmd);
2320     amfree(file_exclude);
2321     amfree(file_include);
2322
2323     aclose(nullfd);
2324     afclose(dumpout);
2325     afclose(in);
2326     afclose(out);
2327
2328     return size;
2329 }
2330 #endif
2331
2332 off_t
2333 getsize_application_api(
2334     disk_estimates_t         *est,
2335     int                       nb_level,
2336     int                      *levels,
2337     backup_support_option_t  *bsu)
2338 {
2339     dle_t *dle = est->dle;
2340     int pipeinfd[2], pipeoutfd[2], pipeerrfd[2];
2341     pid_t dumppid;
2342     off_t size = (off_t)-1;
2343     FILE *dumpout;
2344     FILE *dumperr;
2345     char *line = NULL;
2346     char *cmd = NULL;
2347     char *cmdline;
2348     guint i;
2349     int   j;
2350     GPtrArray *argv_ptr = g_ptr_array_new();
2351     char *newoptstr = NULL;
2352     off_t size1, size2;
2353     times_t start_time;
2354     char *qdisk = quote_string(dle->disk);
2355     char *qamdevice = quote_string(dle->device);
2356     amwait_t wait_status;
2357     char levelstr[NUM_STR_SIZE];
2358     GSList   *scriptlist;
2359     script_t *script;
2360     char     *errmsg = NULL;
2361     estimate_t     estimate;
2362     estimatelist_t el;
2363
2364     cmd = vstralloc(APPLICATION_DIR, "/", dle->program, NULL);
2365
2366     g_ptr_array_add(argv_ptr, stralloc(dle->program));
2367     g_ptr_array_add(argv_ptr, stralloc("estimate"));
2368     if (bsu->message_line == 1) {
2369         g_ptr_array_add(argv_ptr, stralloc("--message"));
2370         g_ptr_array_add(argv_ptr, stralloc("line"));
2371     }
2372     if (g_options->config && bsu->config == 1) {
2373         g_ptr_array_add(argv_ptr, stralloc("--config"));
2374         g_ptr_array_add(argv_ptr, stralloc(g_options->config));
2375     }
2376     if (g_options->hostname && bsu->host == 1) {
2377         g_ptr_array_add(argv_ptr, stralloc("--host"));
2378         g_ptr_array_add(argv_ptr, stralloc(g_options->hostname));
2379     }
2380     g_ptr_array_add(argv_ptr, stralloc("--device"));
2381     g_ptr_array_add(argv_ptr, stralloc(dle->device));
2382     if (dle->disk && bsu->disk == 1) {
2383         g_ptr_array_add(argv_ptr, stralloc("--disk"));
2384         g_ptr_array_add(argv_ptr, stralloc(dle->disk));
2385     }
2386     for (j=0; j < nb_level; j++) {
2387         g_ptr_array_add(argv_ptr, stralloc("--level"));
2388         g_snprintf(levelstr,SIZEOF(levelstr),"%d", levels[j]);
2389         g_ptr_array_add(argv_ptr, stralloc(levelstr));
2390     }
2391     /* find the first in ES_CLIENT and ES_CALCSIZE */
2392     estimate = ES_CLIENT;
2393     for (el = dle->estimatelist; el != NULL; el = el->next) {
2394         estimate = (estimate_t)GPOINTER_TO_INT(el->data);
2395         if ((estimate == ES_CLIENT && bsu->client_estimate) ||
2396             (estimate == ES_CALCSIZE && bsu->calcsize))
2397             break;
2398         estimate = ES_CLIENT;
2399     }
2400     if (estimate == ES_CALCSIZE && bsu->calcsize) {
2401         g_ptr_array_add(argv_ptr, stralloc("--calcsize"));
2402     }
2403
2404     application_property_add_to_argv(argv_ptr, dle, bsu, g_options->features);
2405
2406     for (scriptlist = dle->scriptlist; scriptlist != NULL;
2407          scriptlist = scriptlist->next) {
2408         script = (script_t *)scriptlist->data;
2409         if (script->result && script->result->proplist) {
2410             property_add_to_argv(argv_ptr, script->result->proplist);
2411         }
2412     }
2413
2414     g_ptr_array_add(argv_ptr, NULL);
2415
2416     cmdline = stralloc(cmd);
2417     for(i = 1; i < argv_ptr->len-1; i++)
2418         cmdline = vstrextend(&cmdline, " ",
2419                              (char *)g_ptr_array_index(argv_ptr, i), NULL);
2420     dbprintf("running: \"%s\"\n", cmdline);
2421     amfree(cmdline);
2422
2423     if (pipe(pipeerrfd) < 0) {
2424         errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2425                             strerror(errno));
2426         goto common_exit;
2427     }
2428
2429     if (pipe(pipeinfd) < 0) {
2430         errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2431                             strerror(errno));
2432         goto common_exit;
2433     }
2434
2435     if (pipe(pipeoutfd) < 0) {
2436         errmsg = vstrallocf(_("getsize_application_api could not create data pipes: %s"),
2437                             strerror(errno));
2438         goto common_exit;
2439     }
2440
2441     start_time = curclock();
2442
2443     switch(dumppid = fork()) {
2444     case -1:
2445       size = (off_t)-1;
2446       goto common_exit;
2447     default:
2448       break; /* parent */
2449     case 0:
2450       dup2(pipeinfd[0], 0);
2451       dup2(pipeoutfd[1], 1);
2452       dup2(pipeerrfd[1], 2);
2453       aclose(pipeinfd[1]);
2454       aclose(pipeoutfd[0]);
2455       aclose(pipeerrfd[0]);
2456       safe_fd(-1, 0);
2457
2458       execve(cmd, (char **)argv_ptr->pdata, safe_env());
2459       error(_("exec %s failed: %s"), cmd, strerror(errno));
2460       /*NOTREACHED*/
2461     }
2462     amfree(newoptstr);
2463
2464     aclose(pipeinfd[0]);
2465     aclose(pipeoutfd[1]);
2466     aclose(pipeerrfd[1]);
2467     aclose(pipeinfd[1]);
2468
2469     dumpout = fdopen(pipeoutfd[0],"r");
2470     if (!dumpout) {
2471         error(_("Can't fdopen: %s"), strerror(errno));
2472         /*NOTREACHED*/
2473     }
2474
2475     for(size = (off_t)-1; (line = agets(dumpout)) != NULL; free(line)) {
2476         long long size1_ = (long long)0;
2477         long long size2_ = (long long)0;
2478         int  level = 0;
2479         if (line[0] == '\0')
2480             continue;
2481         dbprintf("%s\n", line);
2482         if (strncmp(line,"ERROR ", 6) == 0) {
2483             char *errmsg, *qerrmsg;
2484
2485             errmsg = stralloc(line+6);
2486             qerrmsg = quote_string(errmsg);
2487             dbprintf(_("errmsg is %s\n"), errmsg);
2488             g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2489             amfree(qerrmsg);
2490             continue;
2491         }
2492         i = sscanf(line, "%d %lld %lld", &level, &size1_, &size2_);
2493         if (i != 3) {
2494             i = sscanf(line, "%lld %lld", &size1_, &size2_);
2495             level = levels[0];
2496             if (i != 2) {
2497                 char *errmsg, *qerrmsg;
2498
2499                 errmsg = vstrallocf(_("bad line %s"), line);
2500                 qerrmsg = quote_string(errmsg);
2501                 dbprintf(_("errmsg is %s\n"), errmsg);
2502                 g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
2503                 amfree(qerrmsg);
2504                 continue;
2505             }
2506         }
2507         size1 = (off_t)size1_;
2508         size2 = (off_t)size2_;
2509         if (size1 <= 0 || size2 <=0)
2510             size = -1;
2511         else if (size1 * size2 > 0)
2512             size = size1 * size2;
2513         dbprintf(_("estimate size for %s level %d: %lld KB\n"),
2514                  qamdevice,
2515                  level,
2516                  (long long)size);
2517         g_printf("%s %d SIZE %lld\n", est->qamname, level, (long long)size);
2518     }
2519     amfree(line);
2520
2521     dumperr = fdopen(pipeerrfd[0],"r");
2522     if (!dumperr) {
2523         error(_("Can't fdopen: %s"), strerror(errno));
2524         /*NOTREACHED*/
2525     }
2526
2527     while ((line = agets(dumperr)) != NULL) {
2528             if (strlen(line) > 0) {
2529             char *err =  g_strdup_printf(_("Application '%s': %s"),
2530                                          dle->program, line);
2531             char *qerr = quote_string(err);
2532             for (j=0; j < nb_level; j++) {
2533                 fprintf(stdout, "%s %d ERROR %s\n",
2534                         est->qamname, levels[j], qerr);
2535             }
2536             dbprintf("ERROR %s", qerr);
2537             amfree(err);
2538             amfree(qerr);
2539         }
2540         amfree(line);
2541     }
2542
2543     dbprintf(".....\n");
2544     if (nb_level == 1) {
2545         dbprintf(_("estimate time for %s level %d: %s\n"), qamdevice,
2546                  levels[0], walltime_str(timessub(curclock(), start_time)));
2547     } else {
2548         dbprintf(_("estimate time for %s all level: %s\n"), qamdevice,
2549                  walltime_str(timessub(curclock(), start_time)));
2550     }
2551
2552     kill(-dumppid, SIGTERM);
2553
2554     dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
2555     waitpid(dumppid, &wait_status, 0);
2556     if (WIFSIGNALED(wait_status)) {
2557         errmsg = vstrallocf(_("%s terminated with signal %d: see %s"),
2558                             cmd, WTERMSIG(wait_status), dbfn());
2559     } else if (WIFEXITED(wait_status)) {
2560         if (WEXITSTATUS(wait_status) != 0) {
2561             errmsg = vstrallocf(_("%s exited with status %d: see %s"), cmd,
2562                                 WEXITSTATUS(wait_status), dbfn());
2563         } else {
2564             /* Normal exit */
2565         }
2566     } else {
2567         errmsg = vstrallocf(_("%s got bad exit: see %s"),
2568                             cmd, dbfn());
2569     }
2570     dbprintf(_("after %s %s wait\n"), cmd, qdisk);
2571
2572     afclose(dumpout);
2573     afclose(dumperr);
2574
2575 common_exit:
2576
2577     amfree(cmd);
2578     g_ptr_array_free_full(argv_ptr);
2579     amfree(newoptstr);
2580     amfree(qdisk);
2581     amfree(qamdevice);
2582     if (errmsg) {
2583         char *qerrmsg = quote_string(errmsg);
2584         dbprintf(_("errmsg is %s\n"), errmsg);
2585         for (j=0; j < nb_level; j++) {
2586             g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[j], qerrmsg);
2587         }
2588         amfree(errmsg);
2589         amfree(qerrmsg);
2590     }
2591     return size;
2592 }
2593
2594
2595 /*
2596  * Returns the value of the first integer in a string.
2597  */
2598
2599 double
2600 first_num(
2601     char *      str)
2602 {
2603     char *start;
2604     int ch;
2605     double d;
2606
2607     ch = *str++;
2608     while(ch && !isdigit(ch)) ch = *str++;
2609     start = str-1;
2610     while(isdigit(ch) || (ch == '.')) ch = *str++;
2611     str[-1] = '\0';
2612     d = atof(start);
2613     str[-1] = (char)ch;
2614     return d;
2615 }
2616
2617
2618 /*
2619  * Checks the dump output line against the error and size regex tables.
2620  */
2621
2622 off_t
2623 handle_dumpline(
2624     char *      str)
2625 {
2626     regex_scale_t *rp;
2627     double size;
2628
2629     /* check for size match */
2630     /*@ignore@*/
2631     for(rp = re_size; rp->regex != NULL; rp++) {
2632         if(match(rp->regex, str)) {
2633             size = ((first_num(str)*rp->scale+1023.0)/1024.0);
2634             if(size < 0.0)
2635                 size = 1.0;             /* found on NeXT -- sigh */
2636             return (off_t)size;
2637         }
2638     }
2639     /*@end@*/
2640     return (off_t)-1;
2641 }