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