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