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