/*
* Amanda, The Advanced Maryland Automatic Network Disk Archiver
* Copyright (c) 1991-1998 University of Maryland at College Park
* Copyright (c) 2007-2012 Zmanda, Inc. All Rights Reserved.
* Copyright (c) 2013-2016 Carbonite, Inc. All Rights Reserved.
* All Rights Reserved.
*
* Permission to use, copy, modify, distribute, and sell this software and its
* documentation for any purpose is hereby granted without fee, provided that
* the above copyright notice appear in all copies and that both that
* copyright notice and this permission notice appear in supporting
* documentation, and that the name of U.M. not be used in advertising or
* publicity pertaining to distribution of the software without specific,
* written prior permission. U.M. makes no representations about the
* suitability of this software for any purpose. It is provided "as is"
* without express or implied warranty.
*
* U.M. DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING ALL
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL U.M.
* BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION
* OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN
* CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
*
* Authors: the Amanda Development Team. Its members are listed in a
* file named AUTHORS, in the root directory of this distribution.
*/
/*
* $Id: sendsize.c 10421 2008-03-06 18:48:30Z martineau $
*
* send estimated backup sizes using dump
*/
#include "amanda.h"
#include "match.h"
#include "pipespawn.h"
#include "amfeatures.h"
#include "amandates.h"
#include "clock.h"
#include "amutil.h"
#include "getfsent.h"
#include "client_util.h"
#include "conffile.h"
#ifdef SAMBA_CLIENT
#include "findpass.h"
#endif
#define sendsize_debug(i, ...) do { \
if ((i) <= debug_sebdsize) { \
dbprintf(__VA_ARGS__); \
} \
} while (0)
#ifdef HAVE_SETPGID
# define SETPGRP setpgid(getpid(), getpid())
# define SETPGRP_FAILED() do { \
dbprintf(_("setpgid(%ld,%ld) failed: %s\n"), \
(long)getpid(), (long)getpid(), strerror(errno)); \
} while(0)
#else /* () line 0 */
#if defined(SETPGRP_VOID)
# define SETPGRP setpgrp()
# define SETPGRP_FAILED() do { \
dbprintf(_("setpgrp() failed: %s\n"), strerror(errno)); \
} while(0)
#else
# define SETPGRP setpgrp(0, getpid())
# define SETPGRP_FAILED() do { \
dbprintf(_("setpgrp(0,%ld) failed: %s\n"), \
(long)getpid(), strerror(errno)); \
} while(0)
#endif
#endif
typedef struct level_estimates_s {
time_t dumpsince;
int estsize;
int needestimate;
int server; /* server can do estimate */
} level_estimate_t;
typedef struct disk_estimates_s {
struct disk_estimates_s *next;
char *qamname;
char *qamdevice;
char *dirname;
char *qdirname;
pid_t child;
int done;
dle_t *dle;
int dle_scripts_exit_status;
level_estimate_t est[DUMP_LEVELS];
} disk_estimates_t;
disk_estimates_t *est_list;
static am_feature_t *our_features = NULL;
static char *our_feature_string = NULL;
static g_option_t *g_options = NULL;
static gboolean amandates_started = FALSE;
static int host_scripts_exit_status = 0;
/* local functions */
int main(int argc, char **argv);
gboolean dle_add_diskest(dle_t *dle);
void calc_estimates(disk_estimates_t *est);
void free_estimates(disk_estimates_t *est);
void dump_calc_estimates(disk_estimates_t *);
void star_calc_estimates(disk_estimates_t *);
void smbtar_calc_estimates(disk_estimates_t *);
void gnutar_calc_estimates(disk_estimates_t *);
void application_api_calc_estimate(disk_estimates_t *);
void generic_calc_estimates(disk_estimates_t *);
int
main(
int argc,
char ** argv)
{
int level;
char *dumpdate;
disk_estimates_t *est;
disk_estimates_t *est1;
disk_estimates_t *est_prev;
disk_estimates_t *est_next;
char *line = NULL;
char *s, *fp;
int ch;
char *err_extra = NULL;
int done;
int need_wait;
int dumpsrunning;
char *qdisk = NULL;
char *qlist = NULL;
char *qamdevice = NULL;
dle_t *dle = NULL;
GSList *errlist;
am_level_t *alevel;
(void)argc; /* Quiet unused parameter warning */
(void)argv; /* Quiet unused parameter warning */
if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
printf("sendsize-%s\n", VERSION);
return (0);
}
glib_init();
/* initialize */
/*
* Configure program for internationalization:
* 1) Only set the message locale for now.
* 2) Set textdomain for all amanda related programs to "amanda"
* We don't want to be forced to support dozens of message catalogs.
*/
setlocale(LC_MESSAGES, "C");
textdomain("amanda");
safe_fd(-1, 0);
openbsd_fd_inform();
safe_cd();
set_pname("sendsize");
/* Don't die when child closes pipe */
signal(SIGPIPE, SIG_IGN);
add_amanda_log_handler(amanda_log_stderr);
add_amanda_log_handler(amanda_log_syslog);
dbopen(DBG_SUBDIR_CLIENT);
startclock();
dbprintf(_("version %s\n"), VERSION);
our_features = am_init_feature_set();
our_feature_string = am_feature_to_string(our_features);
config_init(CONFIG_INIT_CLIENT|CONFIG_INIT_GLOBAL, NULL);
/* (check for config errors comes later) */
check_running_as(RUNNING_AS_CLIENT_LOGIN);
/* handle all service requests */
for(; (line = agets(stdin)) != NULL; free(line)) {
if (line[0] == '\0')
continue;
if(strncmp_const(line, "OPTIONS ") == 0) {
if (g_options) {
g_printf(_("ERROR [Multiple OPTIONS line in sendsize input]\n"));
error(_("Multiple OPTIONS line in sendsize input\n"));
/*NOTREACHED*/
}
g_options = parse_g_options(line+8, 1);
if(!g_options->hostname) {
g_options->hostname = g_malloc(MAX_HOSTNAME_LENGTH+1);
gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
}
g_printf("OPTIONS ");
if(am_has_feature(g_options->features, fe_rep_options_features)) {
g_printf("features=%s;", our_feature_string);
}
if(am_has_feature(g_options->features, fe_rep_options_maxdumps)) {
g_printf("maxdumps=%d;", g_options->maxdumps);
}
if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
g_printf("hostname=%s;", g_options->hostname);
}
g_printf("\n");
fflush(stdout);
if (g_options->config) {
/* overlay this configuration on the existing (nameless) configuration */
config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
g_options->config);
dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
}
/* check for any config errors now */
if (config_errors(&errlist) >= CFGERR_ERRORS) {
char *errstr = config_errors_to_error_string(errlist);
g_printf("%s\n", errstr);
dbclose();
amfree(errstr);
amfree(line);
return 1;
}
if (am_has_feature(g_options->features, fe_req_xml)) {
break;
}
continue;
}
dle = alloc_dle();
s = line;
ch = *s++;
skip_whitespace(s, ch); /* find the program name */
if(ch == '\0') {
err_extra = g_strdup(_("no program name"));
goto err; /* no program name */
}
dle->program = s - 1;
skip_non_whitespace(s, ch);
s[-1] = '\0';
dle->program_is_application_api=0;
if(strncmp_const(dle->program, "CALCSIZE") == 0) {
skip_whitespace(s, ch); /* find the program name */
if(ch == '\0') {
err_extra = g_strdup(_("no program name"));
goto err;
}
dle->estimatelist = g_slist_append(dle->estimatelist,
GINT_TO_POINTER(ES_CALCSIZE));
dle->program = s - 1;
skip_non_whitespace(s, ch);
s[-1] = '\0';
if (g_str_equal(dle->program, "APPLICATION")) {
dle->program_is_application_api=1;
skip_whitespace(s, ch); /* find dumper name */
if (ch == '\0') {
goto err; /* no program */
}
dle->program = s - 1;
skip_non_whitespace(s, ch);
s[-1] = '\0';
}
}
else {
dle->estimatelist = g_slist_append(dle->estimatelist,
GINT_TO_POINTER(ES_CLIENT));
if (g_str_equal(dle->program, "APPLICATION")) {
dle->program_is_application_api=1;
skip_whitespace(s, ch); /* find dumper name */
if (ch == '\0') {
goto err; /* no program */
}
dle->program = s - 1;
skip_non_whitespace(s, ch);
s[-1] = '\0';
}
}
dle->program = g_strdup(dle->program);
skip_whitespace(s, ch); /* find the disk name */
if(ch == '\0') {
err_extra = g_strdup(_("no disk name"));
goto err; /* no disk name */
}
amfree(qdisk);
amfree(qamdevice);
fp = s - 1;
skip_quoted_string(s, ch);
s[-1] = '\0'; /* terminate the disk name */
qdisk = g_strdup(fp);
dle->disk = unquote_string(qdisk);
skip_whitespace(s, ch); /* find the device or level */
if (ch == '\0') {
err_extra = g_strdup(_("bad level"));
goto err;
}
if(!isdigit((int)s[-1])) {
fp = s - 1;
skip_quoted_string(s, ch);
s[-1] = '\0';
qamdevice = g_strdup(fp);
dle->device = unquote_string(qamdevice);
skip_whitespace(s, ch); /* find level number */
}
else {
dle->device = g_strdup(dle->disk);
qamdevice = g_strdup(qdisk);
}
amfree(qamdevice);
amfree(qdisk);
/* find the level number */
if(ch == '\0' || sscanf(s - 1, "%d", &level) != 1) {
err_extra = g_strdup(_("bad level"));
goto err; /* bad level */
}
if (level < 0 || level >= DUMP_LEVELS) {
err_extra = g_strdup(_("bad level"));
goto err;
}
skip_integer(s, ch);
alevel = g_new0(am_level_t, 1);
alevel->level = level;
dle->levellist = g_slist_append(dle->levellist, alevel);
skip_whitespace(s, ch); /* find the dump date */
if(ch == '\0') {
err_extra = g_strdup(_("no dumpdate"));
goto err; /* no dumpdate */
}
dumpdate = s - 1;
skip_non_whitespace(s, ch);
s[-1] = '\0';
(void)dumpdate; /* XXX: Set but not used */
dle->spindle = 0; /* default spindle */
skip_whitespace(s, ch); /* find the spindle */
if(ch != '\0') {
if(sscanf(s - 1, "%d", &dle->spindle) != 1) {
err_extra = g_strdup(_("bad spindle"));
goto err; /* bad spindle */
}
skip_integer(s, ch);
skip_whitespace(s, ch); /* find the parameters */
if(ch != '\0') {
if(strncmp_const(s-1, "OPTIONS |;") == 0) {
parse_options(s + 8,
dle,
g_options->features,
0);
}
else {
while (ch != '\0') {
if(strncmp_const(s-1, "exclude-file=") == 0) {
qlist = unquote_string(s+12);
dle->exclude_file =
append_sl(dle->exclude_file, qlist);
amfree(qlist);
} else if(strncmp_const(s-1, "exclude-list=") == 0) {
qlist = unquote_string(s+12);
dle->exclude_list =
append_sl(dle->exclude_list, qlist);
amfree(qlist);
} else if(strncmp_const(s-1, "include-file=") == 0) {
qlist = unquote_string(s+12);
dle->include_file =
append_sl(dle->include_file, qlist);
amfree(qlist);
} else if(strncmp_const(s-1, "include-list=") == 0) {
qlist = unquote_string(s+12);
dle->include_list =
append_sl(dle->include_list, qlist);
amfree(qlist);
} else {
err_extra = g_strdup_printf(_("Invalid parameter (%s)"), s-1);
goto err; /* should have gotten to end */
}
skip_quoted_string(s, ch);
skip_whitespace(s, ch); /* find the inclusion list */
amfree(qlist);
}
}
}
}
/*@ignore@*/
if (!dle_add_diskest(dle)) {
free_dle(dle);
}
/*@end@*/
dle = NULL;
}
if (g_options == NULL) {
g_printf(_("ERROR [Missing OPTIONS line in sendsize input]\n"));
error(_("Missing OPTIONS line in sendsize input\n"));
/*NOTREACHED*/
}
amfree(line);
if (am_has_feature(g_options->features, fe_req_xml)) {
char *errmsg = NULL;
dle_t *dles, *dle, *dle_next;
dles = amxml_parse_node_FILE(stdin, &errmsg);
if (errmsg) {
err_extra = errmsg;
goto err;
}
for (dle = dles; dle != NULL; dle = dle_next) {
dle_next = dle->next;
if (!dle_add_diskest(dle)) {
free_dle(dle);
}
}
}
if (amandates_started) {
finish_amandates();
free_amandates();
amandates_started = FALSE;
}
est_prev = NULL;
for(est = est_list; est != NULL; est = est_next) {
int good = merge_dles_properties(est->dle, 0);
est_next = est->next;
if (!good) {
if (est == est_list) {
est_list = est_next;
} else {
est_prev->next = est_next;
}
} else {
est_prev = est;
}
}
for (est = est_list; est != NULL; est = est->next) {
host_scripts_exit_status += run_client_scripts(EXECUTE_ON_PRE_HOST_ESTIMATE,
g_options, est->dle, stdout, NULL);
}
if (host_scripts_exit_status) {
dbclose();
return 1;
}
dumpsrunning = 0;
need_wait = 0;
done = 0;
while (!done) {
done = 1;
/*
* See if we need to wait for a child before we can do anything
* else in this pass.
*/
if(need_wait) {
pid_t child_pid;
amwait_t child_status;
need_wait = 0;
dbprintf(_("waiting for any estimate child: %d running\n"),
dumpsrunning);
child_pid = wait(&child_status);
if(child_pid == -1) {
error(_("wait failed: %s"), strerror(errno));
/*NOTREACHED*/
}
if (!WIFEXITED(child_status) || WEXITSTATUS(child_status) != 0) {
char *child_name = g_strdup_printf(_("child %ld"), (long)child_pid);
char *child_status_str = str_exit_status(child_name, child_status);
dbprintf("%s\n", child_status_str);
amfree(child_status_str);
amfree(child_name);
}
/*
* Find the child and mark it done.
*/
for(est = est_list; est != NULL; est = est->next) {
if(est->child == child_pid) {
break;
}
}
if(est == NULL) {
dbprintf(_("unexpected child %ld\n"), (long)child_pid);
} else {
est->done = 1;
est->child = 0;
dumpsrunning--;
est->dle_scripts_exit_status += run_client_scripts(
EXECUTE_ON_POST_DLE_ESTIMATE, g_options,
est->dle, stdout, NULL);
}
}
/*
* If we are already running the maximum number of children
* go back and wait until one of them finishes.
*/
if(dumpsrunning >= g_options->maxdumps) {
done = 0;
need_wait = 1;
continue; /* have to wait first */
}
/*
* Find a new child to start.
*/
for(est = est_list; est != NULL; est = est->next) {
if(est->done == 0) {
done = 0; /* more to do */
}
if(est->child != 0 || est->done) {
continue; /* child is running or done */
}
/*
* Make sure there is no spindle conflict.
*/
if(est->dle->spindle != -1) {
for(est1 = est_list; est1 != NULL; est1 = est1->next) {
if(est1->child == 0 || est == est1 || est1->done) {
/*
* Ignore anything not yet started, ourself,
* and anything completed.
*/
continue;
}
if(est1->dle->spindle == est->dle->spindle) {
break; /* oops -- they match */
}
}
if(est1 != NULL) {
continue; /* spindle conflict */
}
}
break; /* start this estimate */
}
if(est == NULL) {
if(dumpsrunning > 0) {
need_wait = 1; /* nothing to do but wait */
}
} else {
done = 0;
est->dle_scripts_exit_status += run_client_scripts(
EXECUTE_ON_PRE_DLE_ESTIMATE, g_options,
est->dle, stdout, NULL);
if (est->dle_scripts_exit_status == 0) {
if ((est->child = fork()) == 0) {
calc_estimates(est); /* child does the estimate */
exit(0);
} else if (est->child == -1) {
error(_("calc_estimates fork failed: %s"), strerror(errno));
/*NOTREACHED*/
}
dumpsrunning++; /* parent */
} else {
est->done = 1;
}
}
}
for(est = est_list; est != NULL; est = est->next) {
host_scripts_exit_status += run_client_scripts(EXECUTE_ON_POST_HOST_ESTIMATE,
g_options, est->dle, stdout, NULL);
}
est_prev = NULL;
for(est = est_list; est != NULL; est = est->next) {
free_estimates(est);
amfree(est_prev);
est_prev = est;
}
amfree(est_prev);
amfree(our_feature_string);
am_release_feature_set(our_features);
our_features = NULL;
free_g_options(g_options);
dbclose();
return 0;
err:
if (err_extra) {
g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET '%s'\n"), err_extra);
dbprintf(_("REQ packet is bogus: %s\n"), err_extra);
amfree(err_extra);
} else {
g_printf(_("ERROR FORMAT ERROR IN REQUEST PACKET\n"));
dbprintf(_("REQ packet is bogus\n"));
}
free_g_options(g_options);
if (dle)
free_dle(dle);
amfree(line);
amfree(qdisk);
dbclose();
return 1;
}
gboolean
dle_add_diskest(
dle_t *dle)
{
disk_estimates_t *newp, *curp;
amandates_t *amdp;
int dumplev, estlev;
time_t dumpdate;
levellist_t levellist;
char *amandates_file;
gboolean need_amandates = FALSE;
estimatelist_t el;
if (dle->levellist == NULL) {
g_printf(_("ERROR Missing level in request\n"));
return FALSE;
}
/* should we use amandates for this? */
for (el = dle->estimatelist; el != NULL; el=el->next) {
estimate_t estimate = (estimate_t)GPOINTER_TO_INT(el->data);
if (estimate == ES_CALCSIZE)
need_amandates = TRUE;
}
if (g_str_equal(dle->program, "GNUTAR")) {
/* GNUTAR only needs amandates if gnutar_list_dir is NULL */
char *gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
if (!gnutar_list_dir || !*gnutar_list_dir)
need_amandates = TRUE;
}
/* start amandates here, before adding this DLE to est_list, in case
* we encounter an error. */
if (need_amandates) {
if (!amandates_started) {
amandates_file = getconf_str(CNF_AMANDATES);
if(!start_amandates(amandates_file, 0)) {
char *errstr = strerror(errno);
char *qamname = quote_string(dle->disk);
char *errmsg = g_strdup_printf(_("could not open %s: %s"), amandates_file, errstr);
char *qerrmsg = quote_string(errmsg);
g_printf(_("%s %d ERROR %s\n"), qamname, 0, qerrmsg);
amfree(qamname);
amfree(errmsg);
amfree(qerrmsg);
return FALSE;
}
amandates_started = TRUE;
}
}
levellist = dle->levellist;
while (levellist != NULL) {
am_level_t *alevel = (am_level_t *)levellist->data;
if (alevel->level < 0)
alevel->level = 0;
if (alevel->level >= DUMP_LEVELS)
alevel->level = DUMP_LEVELS - 1;
levellist = g_slist_next(levellist);
}
for(curp = est_list; curp != NULL; curp = curp->next) {
if(g_str_equal(curp->dle->disk, dle->disk)) {
/* already have disk info, just note the level request */
levellist = dle->levellist;
while (levellist != NULL) {
am_level_t *alevel = (am_level_t *)levellist->data;
int level = alevel->level;
curp->est[level].needestimate = 1;
curp->est[level].server = alevel->server;
levellist = g_slist_next(levellist);
}
return FALSE;
}
}
newp = (disk_estimates_t *) g_malloc(sizeof(disk_estimates_t));
memset(newp, 0, sizeof(*newp));
newp->next = est_list;
est_list = newp;
newp->qamname = quote_string(dle->disk);
if (dle->device) {
newp->qamdevice = quote_string(dle->device);
newp->dirname = amname_to_dirname(dle->device);
newp->qdirname = quote_string(newp->dirname);
} else {
newp->qamdevice = g_strdup("");
newp->dirname = g_strdup("");
newp->qdirname = g_strdup("");
}
levellist = dle->levellist;
while (levellist != NULL) {
am_level_t *alevel = (am_level_t *)levellist->data;
newp->est[alevel->level].needestimate = 1;
newp->est[alevel->level].server = alevel->server;
levellist = g_slist_next(levellist);
}
newp->dle = dle;
/* fill in dump-since dates */
if (need_amandates) {
amdp = amandates_lookup(newp->dle->disk);
newp->est[0].dumpsince = EPOCH;
for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
dumpdate = amdp->dates[dumplev];
for(estlev = dumplev+1; estlev < DUMP_LEVELS; estlev++) {
if(dumpdate > newp->est[estlev].dumpsince)
newp->est[estlev].dumpsince = dumpdate;
}
}
} else {
/* just zero everything out */
for(dumplev = 0; dumplev < DUMP_LEVELS; dumplev++) {
newp->est[dumplev].dumpsince = 0;
}
}
return TRUE;
}
void
free_estimates(
disk_estimates_t * est)
{
amfree(est->qamname);
amfree(est->qamdevice);
amfree(est->dirname);
amfree(est->qdirname);
if (est->dle) {
free_dle(est->dle);
}
}
/*
* ------------------------------------------------------------------------
*
*/
void
calc_estimates(
disk_estimates_t * est)
{
dbprintf(_("calculating for amname %s, dirname %s, spindle %d %s\n"),
est->qamname, est->qdirname, est->dle->spindle, est->dle->program);
if(est->dle->program_is_application_api == 1)
application_api_calc_estimate(est);
else {
estimatelist_t el;
estimate_t estimate;
int level;
estimate_t estimate_method = ES_ES;
estimate_t client_method = ES_ES;
/* find estimate method to use */
for (el = est->dle->estimatelist; el != NULL; el = el->next) {
estimate = (estimate_t)GPOINTER_TO_INT(el->data);
if (estimate == ES_SERVER) {
if (estimate_method == ES_ES)
estimate_method = ES_SERVER;
}
if (estimate == ES_CLIENT ||
(estimate == ES_CALCSIZE &&
(est->dle->device[0] != '/' || est->dle->device[1] != '/'))) {
if (client_method == ES_ES)
client_method = estimate;
if (estimate_method == ES_ES)
estimate_method = estimate;
}
}
/* do server estimate */
if (estimate_method == ES_SERVER) {
for (level = 0; level < DUMP_LEVELS; level++) {
if (est->est[level].needestimate) {
if (est->est[level].server || client_method == ES_ES) {
g_printf(_("%s %d SIZE -1\n"), est->qamname, level);
est->est[level].needestimate = 0;
}
}
}
}
if (client_method == ES_ES && estimate_method != ES_SERVER) {
g_printf(_("%s %d SIZE -2\n"), est->qamname, 0);
dbprintf(_("Can't use CALCSIZE for samba estimate: %s %s\n"),
est->qamname, est->qdirname);
} else if (client_method == ES_CALCSIZE) {
generic_calc_estimates(est);
} else if (client_method == ES_CLIENT) {
#ifndef USE_GENERIC_CALCSIZE
if (g_str_equal(est->dle->program, "DUMP"))
dump_calc_estimates(est);
else
#endif
#ifdef SAMBA_CLIENT
if (g_str_equal(est->dle->program, "GNUTAR") &&
est->dle->device[0] == '/' && est->dle->device[1] == '/')
smbtar_calc_estimates(est);
else
#endif
#ifdef GNUTAR
if (g_str_equal(est->dle->program, "GNUTAR"))
gnutar_calc_estimates(est);
else
#endif
dbprintf(_("Invalid program: %s %s %s\n"),
est->qamname, est->qdirname, est->dle->program);
}
}
dbprintf(_("done with amname %s dirname %s spindle %d\n"),
est->qamname, est->qdirname, est->dle->spindle);
}
/*
* ------------------------------------------------------------------------
*
*/
/* local functions */
off_t getsize_dump(dle_t *dle, int level, char **errmsg);
off_t getsize_smbtar(dle_t *dle, int level, char **errmsg);
off_t getsize_gnutar(dle_t *dle, int level, time_t dumpsince, char **errmsg);
off_t getsize_application_api(disk_estimates_t *est, int nb_level,
int *levels, backup_support_option_t *bsu);
off_t handle_dumpline(char *str);
double first_num(char *str);
void
application_api_calc_estimate(
disk_estimates_t * est)
{
int level;
int i;
int levels[DUMP_LEVELS];
int nb_level = 0;
backup_support_option_t *bsu;
GPtrArray *errarray;
estimatelist_t el;
estimate_t estimate;
estimate_t estimate_method = ES_ES;
estimate_t client_method = ES_ES;
int has_calcsize = 0;
int has_client = 0;
bsu = backup_support_option(est->dle->program, &errarray);
if (!bsu) {
guint i;
for (i=0; i < errarray->len; i++) {
char *line;
char *errmsg;
char *qerrmsg;
line = g_ptr_array_index(errarray, i);
if(am_has_feature(g_options->features,
fe_rep_sendsize_quoted_error)) {
errmsg = g_strdup_printf(_("Application '%s': %s"),
est->dle->program, line);
qerrmsg = quote_string(errmsg);
for (level = 0; level < DUMP_LEVELS; level++) {
if (est->est[level].needestimate) {
g_printf(_("%s %d ERROR %s\n"),
est->dle->disk, level, qerrmsg);
dbprintf(_("%s %d ERROR %s\n"),
est->qamname, level, qerrmsg);
}
}
amfree(errmsg);
amfree(qerrmsg);
}
}
if (i == 0) { /* nothing in errarray */
char *errmsg;
char *qerrmsg;
errmsg = g_strdup_printf(
_("Application '%s': cannon execute support command"),
est->dle->program);
qerrmsg = quote_string(errmsg);
for (level = 0; level < DUMP_LEVELS; level++) {
if (est->est[level].needestimate) {
g_printf(_("%s %d ERROR %s\n"),
est->dle->disk, level, qerrmsg);
dbprintf(_("%s %d ERROR %s\n"),
est->qamname, level, qerrmsg);
}
}
amfree(errmsg);
amfree(qerrmsg);
}
for (level = 0; level < DUMP_LEVELS; level++) {
est->est[level].needestimate = 0;
}
g_ptr_array_free_full(errarray);
return;
}
if (est->dle->data_path == DATA_PATH_AMANDA &&
(bsu->data_path_set & DATA_PATH_AMANDA)==0) {
g_printf("%s %d ERROR application %s doesn't support amanda data-path\n", est->qamname, 0, est->dle->program);
amfree(bsu);
return;
}
if (est->dle->data_path == DATA_PATH_DIRECTTCP &&
(bsu->data_path_set & DATA_PATH_DIRECTTCP)==0) {
g_printf("%s %d ERROR application %s doesn't support directtcp data-path\n", est->qamname, 0, est->dle->program);
amfree(bsu);
return;
}
/* find estimate method to use */
for (el = est->dle->estimatelist; el != NULL; el = el->next) {
estimate = (estimate_t)GPOINTER_TO_INT(el->data);
if (estimate == ES_CLIENT)
has_client = 1;
if (estimate == ES_CALCSIZE)
has_calcsize = 1;
if (estimate == ES_SERVER) {
if (estimate_method == ES_ES)
estimate_method = ES_SERVER;
}
if ((estimate == ES_CLIENT && bsu->client_estimate) ||
(estimate == ES_CALCSIZE && bsu->calcsize)) {
if (client_method == ES_ES)
client_method = estimate;
if (estimate_method == ES_ES)
estimate_method = estimate;
}
}
for(level = 0; level < DUMP_LEVELS; level++) {
if (est->est[level].needestimate) {
if (level > bsu->max_level) {
/* planner will not even consider this level */
g_printf("%s %d SIZE %lld\n", est->qamname, level,
(long long)-2);
est->est[level].needestimate = 0;
dbprintf(_("Application '%s' can't estimate level %d\n"),
est->dle->program, level);
} else if (estimate_method == ES_ES) {
g_printf("%s %d SIZE %lld\n", est->qamname, level,
(long long)-2);
est->est[level].needestimate = 0;
if (am_has_feature(g_options->features,
fe_rep_sendsize_quoted_error)) {
char *errmsg, *qerrmsg;
if (has_client && !bsu->client_estimate &&
has_calcsize && !bsu->calcsize) {
errmsg = g_strdup_printf(_("Application '%s' can't do CLIENT or CALCSIZE estimate"),
est->dle->program);
} else if (has_client && !bsu->client_estimate) {
errmsg = g_strdup_printf(_("Application '%s' can't do CLIENT estimate"),
est->dle->program);
} else if (has_calcsize && !bsu->calcsize) {
errmsg = g_strdup_printf(_("Application '%s' can't do CALCSIZE estimate"),
est->dle->program);
} else {
errmsg = g_strdup_printf(_("Application '%s' can't do estimate"),
est->dle->program);
}
qerrmsg = quote_string(errmsg);
dbprintf(_("errmsg is %s\n"), errmsg);
g_printf("%s %d ERROR %s\n",
est->qamname, 0, qerrmsg);
amfree(errmsg);
amfree(qerrmsg);
}
} else if (estimate_method == ES_SERVER &&
(est->est[level].server || client_method == ES_ES)) {
/* planner will consider this level, */
/* but use a server-side estimate */
g_printf("%s %d SIZE -1\n", est->qamname, level);
est->est[level].needestimate = 0;
} else if (client_method == ES_CLIENT) {
levels[nb_level++] = level;
} else if (client_method == ES_CALCSIZE) {
levels[nb_level++] = level;
}
}
}
if (nb_level == 0) {
amfree(bsu);
return;
}
if (bsu->multi_estimate) {
for (i=0;i<nb_level;i++) {
dbprintf(_("getting size via application API for %s %s level %d\n"),
est->qamname, est->qamdevice, levels[i]);
}
getsize_application_api(est, nb_level, levels, bsu);
} else {
for(level = 0; level < DUMP_LEVELS; level++) {
if (est->est[level].needestimate) {
dbprintf(
_("getting size via application API for %s %s level %d\n"),
est->qamname, est->qamdevice, level);
levels[0] = level;
getsize_application_api(est, 1, levels, bsu);
}
}
}
amfree(bsu);
}
void
generic_calc_estimates(
disk_estimates_t * est)
{
int pipefd = -1, nullfd = -1;
char *cmd;
char *cmdline;
char *command;
GPtrArray *argv_ptr = g_ptr_array_new();
gchar **args, **ptr;
char number[NUM_STR_SIZE];
int level;
pid_t calcpid;
int nb_exclude = 0;
int nb_include = 0;
char *file_exclude = NULL;
char *file_include = NULL;
times_t start_time;
FILE *dumpout = NULL;
char *line = NULL;
char *match_expr;
amwait_t wait_status;
char *errmsg = NULL, *qerrmsg;
char tmppath[PATH_MAX];
int len;
int n;
messagelist_t mlist = NULL;
cmd = g_strjoin(NULL, amlibexecdir, "/", "calcsize", NULL);
g_ptr_array_add(argv_ptr, g_strdup("calcsize"));
if (g_options->config)
g_ptr_array_add(argv_ptr, g_strdup(g_options->config));
else
g_ptr_array_add(argv_ptr, g_strdup("NOCONFIG"));
g_ptr_array_add(argv_ptr, g_strdup(est->dle->program));
canonicalize_pathname(est->dle->disk, tmppath);
g_ptr_array_add(argv_ptr, g_strdup(tmppath));
canonicalize_pathname(est->dirname, tmppath);
g_ptr_array_add(argv_ptr, g_strdup(tmppath));
if (est->dle->exclude_file)
nb_exclude += est->dle->exclude_file->nb_element;
if (est->dle->exclude_list)
nb_exclude += est->dle->exclude_list->nb_element;
if (est->dle->include_file)
nb_include += est->dle->include_file->nb_element;
if (est->dle->include_list)
nb_include += est->dle->include_list->nb_element;
if (nb_exclude > 0)
file_exclude = build_exclude(est->dle, &mlist);
if (nb_include > 0)
file_include = build_include(est->dle, est->dirname, &mlist);
g_slist_free(mlist); // MUST also free the message
if(file_exclude) {
g_ptr_array_add(argv_ptr, g_strdup("-X"));
g_ptr_array_add(argv_ptr, g_strdup(file_exclude));
amfree(file_exclude);
}
if(file_include) {
g_ptr_array_add(argv_ptr, g_strdup("-I"));
g_ptr_array_add(argv_ptr, g_strdup(file_include));
amfree(file_include);
}
start_time = curclock();
/*
* We need a first version of the command line to display at this point. So,
* append a NULL to the GPtrArray, free(..., FALSE) it, and reallocate it
* later, with the number of elements in the result - including the final
* NULL.
*/
g_ptr_array_add(argv_ptr, NULL);
n = argv_ptr->len;
args = (gchar **)g_ptr_array_free(argv_ptr, FALSE);
command = args[0];
cmdline = g_strjoinv(" ", args);
dbprintf(_("running: \"%s\"\n"), cmdline);
g_free(cmdline);
argv_ptr = g_ptr_array_sized_new(n);
for (ptr = args; *ptr; ptr++)
g_ptr_array_add(argv_ptr, *ptr);
/* We must free the pointer ONLY, not the strings it points to! */
g_free(args);
for(level = 0; level < DUMP_LEVELS; level++) {
if(est->est[level].needestimate) {
g_snprintf(number, sizeof(number), "%d", level);
g_ptr_array_add(argv_ptr, g_strdup(number));
dbprintf(" %s", number);
g_snprintf(number, sizeof(number),
"%ld", (long)est->est[level].dumpsince);
g_ptr_array_add(argv_ptr, g_strdup(number));
dbprintf(" %s", number);
}
}
g_ptr_array_add(argv_ptr, NULL);
dbprintf("\n");
args = (gchar **)g_ptr_array_free(argv_ptr, FALSE);
fflush(stderr); fflush(stdout);
if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
errmsg = g_strdup_printf(_("Cannot access /dev/null : %s"),
strerror(errno));
dbprintf("%s\n", errmsg);
goto common_exit;
}
calcpid = pipespawnv(cmd, STDERR_PIPE, 0, &nullfd, &nullfd, &pipefd, args);
amfree(cmd);
close(nullfd);
dumpout = fdopen(pipefd,"r");
if (!dumpout) {
error(_("Can't fdopen: %s"), strerror(errno));
/*NOTREACHED*/
}
match_expr = g_strjoin(NULL, " %d SIZE %lld", NULL);
len = strlen(est->qamname);
for(; (line = pgets(dumpout)) != NULL; free(line)) {
long long size_ = (long long)0;
if (line[0] == '\0' || (int)strlen(line) <= len)
continue;
/* Don't use sscanf for est->qamname because it can have a '%'. */
if (g_str_has_prefix(line, est->qamname) &&
sscanf(line+len, match_expr, &level, &size_) == 2) {
amflock(1,"size");
g_printf("%s\n", line); /* write to amandad */
amfunlock(1,"size");
dbprintf(_("estimate size for %s level %d: %lld KB\n"),
est->qamname,
level,
size_);
}
}
fclose(dumpout);
amfree(match_expr);
dbprintf(_("waiting for %s %s child (pid=%d)\n"),
command, est->qamdevice, (int)calcpid);
waitpid(calcpid, &wait_status, 0);
if (WIFSIGNALED(wait_status)) {
errmsg = g_strdup_printf(_("%s terminated with signal %d: see %s"),
"calcsize", WTERMSIG(wait_status),
dbfn());
} else if (WIFEXITED(wait_status)) {
if (WEXITSTATUS(wait_status) != 0) {
errmsg = g_strdup_printf(_("%s exited with status %d: see %s"),
"calcsize", WEXITSTATUS(wait_status),
dbfn());
} else {
/* Normal exit */
}
} else {
errmsg = g_strdup_printf(_("%s got bad exit: see %s"),
"calcsize", dbfn());
}
dbprintf(_("after %s %s wait: child pid=%d status=%d\n"),
command, est->qamdevice,
(int)calcpid, WEXITSTATUS(wait_status));
dbprintf(_(".....\n"));
dbprintf(_("estimate time for %s: %s\n"),
est->qamname,
walltime_str(timessub(curclock(), start_time)));
common_exit:
if (errmsg && errmsg[0] != '\0') {
if(am_has_feature(g_options->features, fe_rep_sendsize_quoted_error)) {
qerrmsg = quote_string(errmsg);
dbprintf(_("errmsg is %s\n"), errmsg);
g_printf("%s %d ERROR %s\n",
est->qamname, 0, qerrmsg);
amfree(qerrmsg);
}
}
amfree(errmsg);
g_strfreev(args);
amfree(cmd);
}
void
dump_calc_estimates(
disk_estimates_t * est)
{
int level;
off_t size;
char *errmsg=NULL, *qerrmsg;
for(level = 0; level < DUMP_LEVELS; level++) {
if(est->est[level].needestimate) {
dbprintf(_("getting size via dump for %s level %d\n"),
est->qamname, level);
size = getsize_dump(est->dle, level, &errmsg);
amflock(1, "size");
g_printf(_("%s %d SIZE %lld\n"),
est->qamname, level, (long long)size);
if (errmsg && errmsg[0] != '\0') {
if(am_has_feature(g_options->features,
fe_rep_sendsize_quoted_error)) {
qerrmsg = quote_string(errmsg);
dbprintf(_("errmsg is %s\n"), errmsg);
g_printf("%s %d ERROR %s\n",
est->qamname, level, qerrmsg);
amfree(qerrmsg);
}
}
amfree(errmsg);
fflush(stdout);
amfunlock(1, "size");
}
}
}
#ifdef SAMBA_CLIENT
void
smbtar_calc_estimates(
disk_estimates_t * est)
{
int level;
off_t size;
char *errmsg = NULL, *qerrmsg;
for(level = 0; level < DUMP_LEVELS; level++) {
if(est->est[level].needestimate) {
dbprintf(_("getting size via smbclient for %s level %d\n"),
est->qamname, level);
size = getsize_smbtar(est->dle, level, &errmsg);
amflock(1, "size");
g_printf(_("%s %d SIZE %lld\n"),
est->qamname, level, (long long)size);
if (errmsg && errmsg[0] != '\0') {
if(am_has_feature(g_options->features,
fe_rep_sendsize_quoted_error)) {
qerrmsg = quote_string(errmsg);
dbprintf(_("errmsg is %s\n"), errmsg);
g_printf("%s %d ERROR %s\n",
est->qamname, level, qerrmsg);
amfree(qerrmsg);
}
}
amfree(errmsg);
fflush(stdout);
amfunlock(1, "size");
}
}
}
#endif
#ifdef GNUTAR
void
gnutar_calc_estimates(
disk_estimates_t * est)
{
int level;
off_t size;
char *errmsg = NULL, *qerrmsg;
for(level = 0; level < DUMP_LEVELS; level++) {
if (est->est[level].needestimate) {
dbprintf(_("getting size via gnutar for %s level %d\n"),
est->qamname, level);
size = getsize_gnutar(est->dle, level,
est->est[level].dumpsince,
&errmsg);
amflock(1, "size");
g_printf(_("%s %d SIZE %lld\n"),
est->qamname, level, (long long)size);
if (errmsg && errmsg[0] != '\0') {
if(am_has_feature(g_options->features,
fe_rep_sendsize_quoted_error)) {
qerrmsg = quote_string(errmsg);
dbprintf(_("errmsg is %s\n"), errmsg);
g_printf(_("%s %d ERROR %s\n"),
est->qamname, level, qerrmsg);
amfree(qerrmsg);
}
}
amfree(errmsg);
fflush(stdout);
amfunlock(1, "size");
}
}
}
#endif
typedef struct regex_scale_s {
char *regex;
int scale;
} regex_scale_t;
/*@ignore@*/
regex_scale_t re_size[] = {
#ifdef DUMP
{" DUMP: estimated -*[0-9][0-9]* tape blocks", 1024},
{" DUMP: [Ee]stimated [0-9][0-9]* blocks", 512},
{" DUMP: [Ee]stimated [0-9][0-9]* bytes", 1}, /* Ultrix 4.4 */
{" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* NEC EWS-UX */
{"dump: Estimate: [0-9][0-9]* tape blocks", 1024}, /* OSF/1 */
{"backup: There are an estimated [0-9][0-9]* tape blocks.",1024}, /* AIX */
{"backup: estimated [0-9][0-9]* 1k blocks", 1024}, /* AIX */
{"backup: estimated [0-9][0-9]* tape blocks", 1024}, /* AIX */
{"backup: [0-9][0-9]* tape blocks on [0-9][0-9]* tape(s)",1024}, /* AIX */
{"backup: [0-9][0-9]* 1k blocks on [0-9][0-9]* volume(s)",1024}, /* AIX */
{"dump: Estimate: [0-9][0-9]* blocks being output to pipe",1024},
/* DU 4.0 dump */
{"dump: Dumping [0-9][0-9]* bytes, ", 1}, /* DU 4.0 vdump */
{"DUMP: estimated [0-9][0-9]* KB output", 1024}, /* HPUX */
{"DUMP: estimated [0-9][0-9]* KB\\.", 1024}, /* NetApp */
{" UFSDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
#ifdef HAVE_DUMP_ESTIMATE
{"[0-9][0-9]* blocks, [0-9][0-9]*.[0-9][0-9]* volumes", 1024},
/* DU 3.2g dump -E */
{"^[0-9][0-9]* blocks$", 1024}, /* DU 4.0 dump -E */
{"^[0-9][0-9]*$", 1}, /* Solaris ufsdump -S */
#endif
#endif
#ifdef VDUMP
{"vdump: Dumping [0-9][0-9]* bytes, ", 1}, /* OSF/1 vdump */
#endif
#ifdef VXDUMP
{"vxdump: estimated [0-9][0-9]* blocks", 512}, /* HPUX's vxdump */
{" VXDUMP: estimated [0-9][0-9]* blocks", 512}, /* Sinix */
#endif
#ifdef XFSDUMP
{"xfsdump: estimated dump size: [0-9][0-9]* bytes", 1}, /* Irix 6.2 xfs */
#endif
#ifdef GNUTAR
{"Total bytes written: [0-9][0-9]*", 1}, /* Gnutar client */
#endif
#ifdef SAMBA_CLIENT
#if SAMBA_VERSION >= 2
#define SAMBA_DEBUG_LEVEL "0"
{"Total number of bytes: [0-9][0-9]*", 1}, /* Samba du */
#else
#define SAMBA_DEBUG_LEVEL "3"
{"Total bytes listed: [0-9][0-9]*", 1}, /* Samba dir */
#endif
#endif
{ NULL, 0 }
};
/*@end@*/
off_t
getsize_dump(
dle_t *dle,
int level,
char **errmsg)
{
int pipefd[2], nullfd, stdoutfd, killctl[2];
pid_t dumppid;
off_t size;
FILE *dumpout;
char *dumpkeys = NULL;
char *device = NULL;
char *fstype = NULL;
char *cmd = NULL;
char *name = NULL;
char *line = NULL;
char *rundump_cmd = NULL;
char level_str[NUM_STR_SIZE];
int s;
times_t start_time;
char *qdisk;
char *qdevice;
char *config;
char **env;
amwait_t wait_status;
#if defined(DUMP) || defined(VDUMP) || defined(VXDUMP) || defined(XFSDUMP)
int is_rundump = 1;
#endif
if (level > 9)
return -2; /* planner will not even consider this level */
g_snprintf(level_str, sizeof(level_str), "%d", level);
qdisk = quote_string(dle->disk);
device = amname_to_devname(dle->device);
qdevice = quote_string(device);
fstype = amname_to_fstype(dle->device);
dbprintf(_("calculating for device %s with %s\n"),
qdevice, fstype);
cmd = g_strjoin(NULL, amlibexecdir, "/rundump", NULL);
rundump_cmd = g_strdup(cmd);
if (g_options->config)
config = g_options->config;
else
config = "NOCONFIG";
if ((stdoutfd = nullfd = open("/dev/null", O_RDWR)) == -1) {
*errmsg = g_strdup_printf(_("getsize_dump could not open /dev/null: %s"),
strerror(errno));
dbprintf("%s\n", *errmsg);
amfree(cmd);
amfree(rundump_cmd);
amfree(fstype);
amfree(device);
amfree(qdevice);
amfree(qdisk);
return(-1);
}
pipefd[0] = pipefd[1] = killctl[0] = killctl[1] = -1;
if (pipe(pipefd) < 0) {
*errmsg = g_strdup_printf(_("getsize_dump could create data pipes: %s"),
strerror(errno));
dbprintf("%s\n", *errmsg);
amfree(cmd);
amfree(rundump_cmd);
amfree(fstype);
amfree(device);
amfree(qdevice);
amfree(qdisk);
aclose(nullfd);
return(-1);
}
#ifdef XFSDUMP /* { */
#ifdef DUMP /* { */
if (g_str_equal(fstype, "xfs"))
#else /* } { */
if (1)
#endif /* } */
{
name = g_strdup(" (xfsdump)");
dbprintf(_("running \"%s%s -F -J -l %s - %s\"\n"),
cmd, name, level_str, qdevice);
}
else
#endif /* } */
#ifdef VXDUMP /* { */
#ifdef DUMP /* { */
if (g_str_equal(fstype, "vxfs"))
#else /* } { */
if (1)
#endif /* } */
{
#ifdef USE_RUNDUMP
name = g_strdup(" (vxdump)");
#else
name = g_strdup("");
g_free(cmd);
cmd = g_strdup(VXDUMP);
config = skip_argument;
is_rundump = 0;
#endif
dumpkeys = g_strjoin(NULL, level_str, "s", "f", NULL);
dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
cmd, name, dumpkeys, qdevice);
}
else
#endif /* } */
#ifdef VDUMP /* { */
#ifdef DUMP /* { */
if (g_str_equal(fstype, "advfs"))
#else /* } { */
if (1)
#endif /* } */
{
name = g_strdup(" (vdump)");
dumpkeys = g_strjoin(NULL, level_str, "b", "f", NULL);
dbprintf(_("running \"%s%s %s 60 - %s\"\n"),
cmd, name, dumpkeys, qdevice);
}
else
#endif /* } */
#ifdef DUMP /* { */
if (1) {
# ifdef USE_RUNDUMP /* { */
# ifdef AIX_BACKUP /* { */
name = g_strdup(" (backup)");
# else /* } { */
name = g_strjoin(NULL, " (", DUMP, ")", NULL);
# endif /* } */
# else /* } { */
name = g_strdup("");
g_free(cmd);
cmd = g_strdup(DUMP);
config = skip_argument;
is_rundump = 0;
# endif /* } */
# ifdef AIX_BACKUP /* { */
dumpkeys = g_strjoin(NULL, "-", level_str, "f", NULL);
dbprintf(_("running \"%s%s %s - %s\"\n"),
cmd, name, dumpkeys, qdevice);
# else /* } { */
# ifdef HAVE_DUMP_ESTIMATE
# define PARAM_DUMP_ESTIMATE HAVE_DUMP_ESTIMATE
# else
# define PARAM_DUMP_ESTIMATE ""
# endif
# ifdef HAVE_HONOR_NODUMP
# define PARAM_HONOR_NODUMP "h"
# else
# define PARAM_HONOR_NODUMP ""
# endif
dumpkeys = g_strjoin(NULL, level_str,
PARAM_DUMP_ESTIMATE,
PARAM_HONOR_NODUMP,
"s", "f", NULL);
# ifdef HAVE_DUMP_ESTIMATE
stdoutfd = pipefd[1];
# endif
# ifdef HAVE_HONOR_NODUMP /* { */
dbprintf(_("running \"%s%s %s 0 1048576 - %s\"\n"),
cmd, name, dumpkeys, qdevice);
# else /* } { */
dbprintf(_("running \"%s%s %s 1048576 - %s\"\n"),
cmd, name, dumpkeys, qdevice);
# endif /* } */
# endif /* } */
}
else
#endif /* } */
{
error(_("no dump program available"));
/*NOTREACHED*/
}
if (pipe(killctl) < 0) {
dbprintf(_("Could not create pipe: %s\n"), strerror(errno));
/* Message will be printed later... */
killctl[0] = killctl[1] = -1;
}
start_time = curclock();
switch(dumppid = fork()) {
case -1:
*errmsg = g_strdup_printf(_("cannot fork for killpgrp: %s"),
strerror(errno));
dbprintf("%s\n", *errmsg);
amfree(dumpkeys);
amfree(cmd);
amfree(rundump_cmd);
amfree(device);
amfree(qdevice);
amfree(qdisk);
amfree(name);
amfree(fstype);
aclose(nullfd);
return -1;
default:
break;
case 0: /* child process */
if(SETPGRP == -1)
SETPGRP_FAILED();
else if (killctl[0] == -1 || killctl[1] == -1)
dbprintf(_("Trying without killpgrp\n"));
else {
switch(fork()) {
case -1:
dbprintf(_("fork failed, trying without killpgrp\n"));
break;
default:
{
char *killpgrp_cmd = g_strjoin(NULL, amlibexecdir, "/killpgrp", NULL);
dbprintf(_("running %s\n"), killpgrp_cmd);
dup2(killctl[0], 0);
dup2(nullfd, 1);
dup2(nullfd, 2);
aclose(pipefd[0]);
aclose(pipefd[1]);
aclose(killctl[1]);
aclose(nullfd);
safe_fd(-1, 0);
env = safe_env();
execle(killpgrp_cmd, killpgrp_cmd, config, (char *)0, env);
dbprintf(_("cannot execute %s: %s\n"),
killpgrp_cmd, strerror(errno));
free_env(env);
exit(-1);
}
case 0: /* child process */
break;
}
}
dup2(nullfd, 0);
dup2(stdoutfd, 1);
dup2(pipefd[1], 2);
aclose(pipefd[0]);
if (killctl[0] != -1)
aclose(killctl[0]);
if (killctl[1] != -1)
aclose(killctl[1]);
safe_fd(-1, 0);
env = safe_env();
#ifdef XFSDUMP
#ifdef DUMP
if (g_str_equal(fstype, "xfs")) {
#else
if (1) {
#endif
if (is_rundump)
execle(cmd, "rundump", config, "xfsdump", "-F", "-J", "-l",
level_str, "-", device, (char *)0, env);
else
execle(cmd, "xfsdump", "-F", "-J", "-l",
level_str, "-", device, (char *)0, env);
} else
#endif
#ifdef VXDUMP
#ifdef DUMP
if (g_str_equal(fstype, "vxfs")) {
#else
if (1) {
#endif
if (is_rundump)
execle(cmd, "rundump", config, "vxdump", dumpkeys, "1048576",
"-", device, (char *)0, env);
else
execle(cmd, "vxdump", dumpkeys, "1048576", "-",
device, (char *)0, env);
} else
#endif
#ifdef VDUMP
#ifdef DUMP
if (g_str_equal(fstype, "advfs")) {
#else
if (1) {
#endif
if (is_rundump)
execle(cmd, "rundump", config, "vdump", dumpkeys, "60", "-",
device, (char *)0, env);
else
execle(cmd, "vdump", dumpkeys, "60", "-",
device, (char *)0, env);
} else
#endif
#ifdef DUMP
if (1) {
# ifdef AIX_BACKUP
if (is_rundump)
execle(cmd, "rundump", config, "backup", dumpkeys, "-",
device, (char *)0, env);
else
execle(cmd, "backup", dumpkeys, "-",
device, (char *)0, env);
# else
if (is_rundump) {
execle(cmd, "rundump", config, "dump", dumpkeys,
#ifdef HAVE_HONOR_NODUMP
"0",
#endif
"1048576", "-", device, (char *)0, env);
} else {
execle(cmd, "dump", dumpkeys,
#ifdef HAVE_HONOR_NODUMP
"0",
#endif
"1048576", "-", device, (char *)0, env);
}
# endif
} else
#endif
{
free_env(env);
error(_("exec %s failed or no dump program available: %s"),
cmd, strerror(errno));
/*NOTREACHED*/
}
free_env(env);
}
amfree(dumpkeys);
amfree(rundump_cmd);
aclose(pipefd[1]);
if (killctl[0] != -1)
aclose(killctl[0]);
dumpout = fdopen(pipefd[0],"r");
if (!dumpout) {
error(_("Can't fdopen: %s"), strerror(errno));
/*NOTREACHED*/
}
for(size = (off_t)-1; (line = pgets(dumpout)) != NULL; free(line)) {
if (line[0] == '\0')
continue;
dbprintf("%s\n", line);
size = handle_dumpline(line);
if(size > (off_t)-1) {
amfree(line);
while ((line = pgets(dumpout)) != NULL) {
if (line[0] != '\0')
break;
amfree(line);
}
if (line != NULL) {
dbprintf("%s\n", line);
}
break;
}
}
amfree(line);
dbprintf(".....\n");
dbprintf(_("estimate time for %s level %d: %s\n"),
qdisk,
level,
walltime_str(timessub(curclock(), start_time)));
if(size == (off_t)-1) {
*errmsg = g_strdup_printf(_("no size line match in %s%s output"),
cmd, name);
dbprintf(_("%s for %s\n"),
*errmsg, qdisk);
dbprintf(".....\n");
dbprintf(_("Run %s%s manually to check for errors\n"),
cmd, name);
} else if(size == (off_t)0 && level == 0) {
dbprintf(_("possible %s%s problem -- is \"%s\" really empty?\n"),
cmd, name, dle->disk);
dbprintf(".....\n");
} else {
dbprintf(_("estimate size for %s level %d: %lld KB\n"),
qdisk,
level,
(long long)size);
}
if (killctl[1] != -1) {
dbprintf(_("asking killpgrp to terminate\n"));
aclose(killctl[1]);
for(s = 5; s > 0; --s) {
sleep(1);
if (waitpid(dumppid, NULL, WNOHANG) != -1)
goto terminated;
}
}
/*
* First, try to kill the dump process nicely. If it ignores us
* for several seconds, hit it harder.
*/
dbprintf(_("sending SIGTERM to process group %ld\n"), (long)dumppid);
if (kill(-dumppid, SIGTERM) == -1) {
dbprintf(_("kill failed: %s\n"), strerror(errno));
}
/* Now check whether it dies */
for(s = 5; s > 0; --s) {
sleep(1);
if (waitpid(dumppid, NULL, WNOHANG) != -1)
goto terminated;
}
dbprintf(_("sending SIGKILL to process group %ld\n"), (long)dumppid);
if (kill(-dumppid, SIGKILL) == -1) {
dbprintf(_("kill failed: %s\n"), strerror(errno));
}
for(s = 5; s > 0; --s) {
sleep(1);
if (waitpid(dumppid, NULL, WNOHANG) != -1)
goto terminated;
}
dbprintf(_("waiting for %s%s \"%s\" child\n"), cmd, name, qdisk);
waitpid(dumppid, &wait_status, 0);
if (WIFSIGNALED(wait_status)) {
*errmsg = g_strdup_printf(_("%s terminated with signal %d: see %s"),
cmd, WTERMSIG(wait_status), dbfn());
} else if (WIFEXITED(wait_status)) {
if (WEXITSTATUS(wait_status) != 0) {
*errmsg = g_strdup_printf(_("%s exited with status %d: see %s"),
cmd, WEXITSTATUS(wait_status), dbfn());
} else {
/* Normal exit */
}
} else {
*errmsg = g_strdup_printf(_("%s got bad exit: see %s"),
cmd, dbfn());
}
dbprintf(_("after %s%s %s wait\n"), cmd, name, qdisk);
terminated:
aclose(nullfd);
afclose(dumpout);
amfree(device);
amfree(qdevice);
amfree(qdisk);
amfree(fstype);
amfree(cmd);
amfree(name);
return size;
}
#ifdef SAMBA_CLIENT
off_t
getsize_smbtar(
dle_t *dle,
int level,
char **errmsg)
{
int pipefd = -1, nullfd = -1, passwdfd = -1;
pid_t dumppid;
off_t size;
FILE *dumpout;
char *tarkeys, *sharename, *user_and_password = NULL, *domain = NULL;
char *share = NULL, *subdir = NULL;
size_t lpass;
char *pwtext;
size_t pwtext_len;
char *line;
char *pw_fd_env;
times_t start_time;
char *error_pn = NULL;
char *qdisk;
amwait_t wait_status;
if (level > 1) {
return -2; /* planner will not even consider this level */
}
error_pn = g_strconcat(get_pname(), "-smbclient", NULL);
qdisk = quote_string(dle->disk);
parsesharename(dle->device, &share, &subdir);
if (!share) {
amfree(share);
amfree(subdir);
set_pname(error_pn);
amfree(error_pn);
error(_("cannot parse disk entry %s for share/subdir"), qdisk);
/*NOTREACHED*/
}
if ((subdir) && (SAMBA_VERSION < 2)) {
amfree(share);
amfree(subdir);
set_pname(error_pn);
amfree(error_pn);
error(_("subdirectory specified for share %s but samba not v2 or better"), qdisk);
/*NOTREACHED*/
}
if ((user_and_password = findpass(share, &domain)) == NULL) {
if(domain) {
memset(domain, '\0', strlen(domain));
amfree(domain);
}
set_pname(error_pn);
amfree(error_pn);
error(_("cannot find password for %s"), dle->disk);
/*NOTREACHED*/
}
lpass = strlen(user_and_password);
if ((pwtext = strchr(user_and_password, '%')) == NULL) {
memset(user_and_password, '\0', (size_t)lpass);
amfree(user_and_password);
if(domain) {
memset(domain, '\0', strlen(domain));
amfree(domain);
}
set_pname(error_pn);
amfree(error_pn);
error(_("password field not \'user%%pass\' for %s"), dle->disk);
/*NOTREACHED*/
}
*pwtext++ = '\0';
pwtext_len = strlen(pwtext);
if ((sharename = makesharename(share, 0)) == NULL) {
memset(user_and_password, '\0', (size_t)lpass);
amfree(user_and_password);
if(domain) {
memset(domain, '\0', strlen(domain));
amfree(domain);
}
set_pname(error_pn);
amfree(error_pn);
error(_("cannot make share name of %s"), share);
/*NOTREACHED*/
}
if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
memset(user_and_password, '\0', (size_t)lpass);
amfree(user_and_password);
if(domain) {
memset(domain, '\0', strlen(domain));
amfree(domain);
}
set_pname(error_pn);
amfree(error_pn);
amfree(sharename);
error(_("could not open /dev/null: %s\n"),
strerror(errno));
/*NOTREACHED*/
}
#if SAMBA_VERSION >= 2
if (level == 0)
tarkeys = "archive 0;recurse;du";
else
tarkeys = "archive 1;recurse;du";
#else
if (level == 0)
tarkeys = "archive 0;recurse;dir";
else
tarkeys = "archive 1;recurse;dir";
#endif
start_time = curclock();
if (pwtext_len > 0) {
pw_fd_env = "PASSWD_FD";
} else {
pw_fd_env = "dummy_PASSWD_FD";
}
dumppid = pipespawn(SAMBA_CLIENT, STDERR_PIPE|PASSWD_PIPE, 0,
&nullfd, &nullfd, &pipefd,
pw_fd_env, &passwdfd,
"smbclient",
sharename,
"-d", SAMBA_DEBUG_LEVEL,
*user_and_password ? "-U" : skip_argument,
*user_and_password ? user_and_password : skip_argument,
"-E",
domain ? "-W" : skip_argument,
domain ? domain : skip_argument,
#if SAMBA_VERSION >= 2
subdir ? "-D" : skip_argument,
subdir ? subdir : skip_argument,
#endif
"-c", tarkeys,
NULL);
if(domain) {
memset(domain, '\0', strlen(domain));
amfree(domain);
}
aclose(nullfd);
if(pwtext_len > 0 && full_write(passwdfd, pwtext, pwtext_len) < pwtext_len) {
int save_errno = errno;
memset(user_and_password, '\0', (size_t)lpass);
amfree(user_and_password);
aclose(passwdfd);
set_pname(error_pn);
amfree(error_pn);
error(_("password write failed: %s"), strerror(save_errno));
/*NOTREACHED*/
}
memset(user_and_password, '\0', (size_t)lpass);
amfree(user_and_password);
aclose(passwdfd);
amfree(sharename);
amfree(share);
amfree(subdir);
amfree(error_pn);
dumpout = fdopen(pipefd,"r");
if (!dumpout) {
error(_("Can't fdopen: %s"), strerror(errno));
/*NOTREACHED*/
}
for(size = (off_t)-1; (line = pgets(dumpout)) != NULL; free(line)) {
if (line[0] == '\0')
continue;
dbprintf("%s\n", line);
size = handle_dumpline(line);
if(size > -1) {
amfree(line);
while ((line = pgets(dumpout)) != NULL) {
if (line[0] != '\0')
break;
amfree(line);
}
if(line != NULL) {
dbprintf("%s\n", line);
}
break;
}
}
amfree(line);
dbprintf(".....\n");
dbprintf(_("estimate time for %s level %d: %s\n"),
qdisk,
level,
walltime_str(timessub(curclock(), start_time)));
if(size == (off_t)-1) {
*errmsg = g_strdup_printf(_("no size line match in %s output"),
SAMBA_CLIENT);
dbprintf(_("%s for %s\n"),
*errmsg, qdisk);
dbprintf(".....\n");
} else if(size == (off_t)0 && level == 0) {
dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
SAMBA_CLIENT, dle->disk);
dbprintf(".....\n");
}
dbprintf(_("estimate size for %s level %d: %lld KB\n"),
qdisk,
level,
(long long)size);
(void)kill(-dumppid, SIGTERM);
dbprintf(_("waiting for %s \"%s\" child\n"), SAMBA_CLIENT, qdisk);
waitpid(dumppid, &wait_status, 0);
if (WIFSIGNALED(wait_status)) {
amfree(*errmsg);
*errmsg = g_strdup_printf(_("%s terminated with signal %d: see %s"),
SAMBA_CLIENT, WTERMSIG(wait_status), dbfn());
} else if (WIFEXITED(wait_status)) {
if (WEXITSTATUS(wait_status) != 0) {
amfree(*errmsg);
*errmsg = g_strdup_printf(_("%s exited with status %d: see %s"),
SAMBA_CLIENT, WEXITSTATUS(wait_status),
dbfn());
} else {
/* Normal exit */
}
} else {
amfree(*errmsg);
*errmsg = g_strdup_printf(_("%s got bad exit: see %s"),
SAMBA_CLIENT, dbfn());
}
dbprintf(_("after %s %s wait\n"), SAMBA_CLIENT, qdisk);
afclose(dumpout);
pipefd = -1;
amfree(error_pn);
amfree(qdisk);
return size;
}
#endif
#ifdef GNUTAR
off_t
getsize_gnutar(
dle_t *dle,
int level,
time_t dumpsince,
char **errmsg)
{
int pipefd = -1, nullfd = -1;
pid_t dumppid;
off_t size = (off_t)-1;
FILE *dumpout = NULL;
char *incrname = NULL;
char *basename = NULL;
char *dirname = NULL;
char *inputname = NULL;
FILE *in = NULL;
FILE *out = NULL;
char *line = NULL;
char *cmd = NULL;
char *command = NULL;
char dumptimestr[80];
struct tm *gmtm;
int nb_exclude = 0;
int nb_include = 0;
GPtrArray *argv_ptr = g_ptr_array_new();
char *file_exclude = NULL;
char *file_include = NULL;
times_t start_time;
int infd = -1, outfd = -1;
ssize_t nb;
char buf[32768];
char *qdisk;
char *gnutar_list_dir;
amwait_t wait_status;
char tmppath[PATH_MAX];
messagelist_t mlist = NULL;
if (level > 9) {
return -2; /* planner will not even consider this level */
}
dirname = amname_to_dirname(dle->device);
qdisk = quote_string(dle->disk);
if(dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
if(dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
if(dle->include_file) nb_include += dle->include_file->nb_element;
if(dle->include_list) nb_include += dle->include_list->nb_element;
if(nb_exclude > 0) file_exclude = build_exclude(dle, &mlist);
if(nb_include > 0) file_include = build_include(dle, dirname, &mlist);
g_slist_free(mlist); // MUST also free the message
gnutar_list_dir = getconf_str(CNF_GNUTAR_LIST_DIR);
if (strlen(gnutar_list_dir) == 0)
gnutar_list_dir = NULL;
if (gnutar_list_dir) {
char number[NUM_STR_SIZE];
int baselevel;
char *sdisk = sanitise_filename(dle->disk);
basename = g_strjoin(NULL, gnutar_list_dir,
"/",
g_options->hostname,
sdisk,
NULL);
amfree(sdisk);
g_snprintf(number, sizeof(number), "%d", level);
incrname = g_strjoin(NULL, basename, "_", number, ".new", NULL);
unlink(incrname);
/*
* Open the listed incremental file from the previous level. Search
* backward until one is found. If none are found (which will also
* be true for a level 0), arrange to read from /dev/null.
*/
baselevel = level;
infd = -1;
while (infd == -1) {
if (--baselevel >= 0) {
g_snprintf(number, sizeof(number), "%d", baselevel);
g_free(inputname);
inputname = g_strconcat(basename, "_", number, NULL);
} else {
g_free(inputname);
inputname = g_strdup("/dev/null");
}
if ((infd = open(inputname, O_RDONLY)) == -1) {
*errmsg = g_strdup_printf(_("gnutar: error opening %s: %s"),
inputname, strerror(errno));
dbprintf("%s\n", *errmsg);
if (baselevel < 0) {
goto common_exit;
}
amfree(*errmsg);
}
}
/*
* Copy the previous listed incremental file to the new one.
*/
if ((outfd = open(incrname, O_WRONLY|O_CREAT, 0600)) == -1) {
*errmsg = g_strdup_printf(_("opening %s: %s"),
incrname, strerror(errno));
dbprintf("%s\n", *errmsg);
goto common_exit;
}
while ((nb = read(infd, &buf, sizeof(buf))) > 0) {
if (full_write(outfd, &buf, (size_t)nb) < (size_t)nb) {
*errmsg = g_strdup_printf(_("writing to %s: %s"),
incrname, strerror(errno));
dbprintf("%s\n", *errmsg);
goto common_exit;
}
}
if (nb < 0) {
*errmsg = g_strdup_printf(_("reading from %s: %s"),
inputname, strerror(errno));
dbprintf("%s\n", *errmsg);
goto common_exit;
}
if (close(infd) != 0) {
infd = -1;
*errmsg = g_strdup_printf(_("closing %s: %s"),
inputname, strerror(errno));
dbprintf("%s\n", *errmsg);
goto common_exit;
}
infd = -1;
if (close(outfd) != 0) {
outfd = -1;
*errmsg = g_strdup_printf(_("closing %s: %s"),
incrname, strerror(errno));
dbprintf("%s\n", *errmsg);
goto common_exit;
}
outfd = -1;
amfree(inputname);
amfree(basename);
}
gmtm = gmtime(&dumpsince);
g_snprintf(dumptimestr, sizeof(dumptimestr),
"%04d-%02d-%02d %2d:%02d:%02d GMT",
gmtm->tm_year + 1900, gmtm->tm_mon+1, gmtm->tm_mday,
gmtm->tm_hour, gmtm->tm_min, gmtm->tm_sec);
cmd = g_strjoin(NULL, amlibexecdir, "/", "runtar", NULL);
g_ptr_array_add(argv_ptr, g_strdup("runtar"));
if (g_options->config)
g_ptr_array_add(argv_ptr, g_strdup(g_options->config));
else
g_ptr_array_add(argv_ptr, g_strdup("NOCONFIG"));
#ifdef GNUTAR
g_ptr_array_add(argv_ptr, g_strdup(GNUTAR));
#else
g_ptr_array_add(argv_ptr, g_strdup("tar"));
#endif
g_ptr_array_add(argv_ptr, g_strdup("--create"));
g_ptr_array_add(argv_ptr, g_strdup("--file"));
g_ptr_array_add(argv_ptr, g_strdup("/dev/null"));
/* use --numeric-owner for estimates, to reduce the number of user/group
* lookups required */
g_ptr_array_add(argv_ptr, g_strdup("--numeric-owner"));
g_ptr_array_add(argv_ptr, g_strdup("--directory"));
canonicalize_pathname(dirname, tmppath);
g_ptr_array_add(argv_ptr, g_strdup(tmppath));
g_ptr_array_add(argv_ptr, g_strdup("--one-file-system"));
if (gnutar_list_dir) {
g_ptr_array_add(argv_ptr, g_strdup("--listed-incremental"));
g_ptr_array_add(argv_ptr, g_strdup(incrname));
} else {
g_ptr_array_add(argv_ptr, g_strdup("--incremental"));
g_ptr_array_add(argv_ptr, g_strdup("--newer"));
g_ptr_array_add(argv_ptr, g_strdup(dumptimestr));
}
#ifdef ENABLE_GNUTAR_ATIME_PRESERVE
/* --atime-preserve causes gnutar to call
* utime() after reading files in order to
* adjust their atime. However, utime()
* updates the file's ctime, so incremental
* dumps will think the file has changed. */
g_ptr_array_add(argv_ptr, g_strdup("--atime-preserve"));
#endif
g_ptr_array_add(argv_ptr, g_strdup("--sparse"));
g_ptr_array_add(argv_ptr, g_strdup("--ignore-failed-read"));
g_ptr_array_add(argv_ptr, g_strdup("--totals"));
if(file_exclude) {
g_ptr_array_add(argv_ptr, g_strdup("--exclude-from"));
g_ptr_array_add(argv_ptr, g_strdup(file_exclude));
}
if(file_include) {
g_ptr_array_add(argv_ptr, g_strdup("--files-from"));
g_ptr_array_add(argv_ptr, g_strdup(file_include));
}
else {
g_ptr_array_add(argv_ptr, g_strdup("."));
}
g_ptr_array_add(argv_ptr, NULL);
start_time = curclock();
if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
*errmsg = g_strdup_printf(_("Cannot access /dev/null : %s"),
strerror(errno));
dbprintf("%s\n", *errmsg);
goto common_exit;
}
command = (char *)g_ptr_array_index(argv_ptr, 0);
dumppid = pipespawnv(cmd, STDERR_PIPE, 0,
&nullfd, &nullfd, &pipefd, (char **)argv_ptr->pdata);
dumpout = fdopen(pipefd,"r");
if (!dumpout) {
error(_("Can't fdopen: %s"), strerror(errno));
/*NOTREACHED*/
}
for(size = (off_t)-1; (line = pgets(dumpout)) != NULL; free(line)) {
if (line[0] == '\0')
continue;
dbprintf("%s\n", line);
if (g_str_has_prefix(line, "ERROR ")) {
char *errmsg, *qerrmsg;
if (g_str_has_prefix(line, "ERROR [")) {
int len;
errmsg = g_strdup(line+7);
len = strlen(errmsg);
errmsg[len-1] = '\0';
} else {
errmsg = g_strdup(line+6);
}
qerrmsg = quote_string(errmsg);
dbprintf(_("errmsg is %s\n"), errmsg);
g_printf(_("%s %d ERROR %s\n"), dle->disk, 0, qerrmsg);
amfree(qerrmsg);
amfree(errmsg);
continue;
} else if (g_str_has_prefix(line, "runtar: error ")) {
char *errmsg, *qerrmsg;
if (g_str_has_prefix(line, "runtar: error [")) {
int len;
errmsg = g_strdup(line+15);
len = strlen(errmsg);
errmsg[len-1] = '\0';
} else {
errmsg = g_strdup(line+14);
}
qerrmsg = quote_string(errmsg);
dbprintf(_("errmsg is %s\n"), errmsg);
g_printf(_("%s %d ERROR %s\n"), dle->disk, 0, qerrmsg);
amfree(qerrmsg);
amfree(errmsg);
continue;
} else {
size = handle_dumpline(line);
if(size > (off_t)-1) {
amfree(line);
while ((line = pgets(dumpout)) != NULL) {
if (line[0] != '\0') {
break;
}
amfree(line);
}
if (line != NULL) {
dbprintf("%s\n", line);
break;
}
break;
}
}
}
amfree(line);
dbprintf(".....\n");
dbprintf(_("estimate time for %s level %d: %s\n"),
qdisk,
level,
walltime_str(timessub(curclock(), start_time)));
if(size == (off_t)-1) {
*errmsg = g_strdup_printf(_("no size line match in %s output"),
command);
dbprintf(_("%s for %s\n"), *errmsg, qdisk);
dbprintf(".....\n");
} else if(size == (off_t)0 && level == 0) {
dbprintf(_("possible %s problem -- is \"%s\" really empty?\n"),
command, dle->disk);
dbprintf(".....\n");
}
dbprintf(_("estimate size for %s level %d: %lld KB\n"),
qdisk,
level,
(long long)size);
(void)kill(-dumppid, SIGTERM);
dbprintf(_("waiting for %s \"%s\" child\n"),
command, qdisk);
waitpid(dumppid, &wait_status, 0);
if (WIFSIGNALED(wait_status)) {
amfree(*errmsg);
*errmsg = g_strdup_printf(_("%s terminated with signal %d: see %s"),
cmd, WTERMSIG(wait_status), dbfn());
} else if (WIFEXITED(wait_status)) {
if (WEXITSTATUS(wait_status) != 0) {
amfree(*errmsg);
*errmsg = g_strdup_printf(_("%s exited with status %d: see %s"),
cmd, WEXITSTATUS(wait_status), dbfn());
} else {
/* Normal exit */
}
} else {
amfree(*errmsg);
*errmsg = g_strdup_printf(_("%s got bad exit: see %s"),
cmd, dbfn());
}
dbprintf(_("after %s %s wait\n"), command, qdisk);
common_exit:
if (incrname) {
unlink(incrname);
}
amfree(incrname);
amfree(basename);
amfree(dirname);
amfree(inputname);
g_ptr_array_free_full(argv_ptr);
amfree(qdisk);
amfree(cmd);
amfree(file_exclude);
amfree(file_include);
aclose(nullfd);
afclose(dumpout);
afclose(in);
afclose(out);
aclose(infd);
aclose(outfd);
return size;
}
#endif
off_t
getsize_application_api(
disk_estimates_t *est,
int nb_level,
int *levels,
backup_support_option_t *bsu)
{
dle_t *dle = est->dle;
int pipeinfd[2], pipeoutfd[2], pipeerrfd[2];
pid_t dumppid;
off_t size = (off_t)-1;
FILE *dumpout;
FILE *dumperr;
char *line = NULL;
char *cmd = NULL;
char *cmdline;
char *cmdargs;
guint i;
int j;
GPtrArray *argv_ptr = g_ptr_array_new();
gchar **args;
char *newoptstr = NULL;
off_t size1, size2;
times_t start_time;
char *qdisk = quote_string(dle->disk);
char *qamdevice = quote_string(dle->device);
amwait_t wait_status;
char levelstr[NUM_STR_SIZE];
GSList *scriptlist;
script_t *script;
char *errmsg = NULL;
estimate_t estimate;
estimatelist_t el;
char **env;
cmd = g_strjoin(NULL, APPLICATION_DIR, "/", dle->program, NULL);
g_ptr_array_add(argv_ptr, g_strdup(dle->program));
g_ptr_array_add(argv_ptr, g_strdup("estimate"));
if (bsu->message_line == 1) {
g_ptr_array_add(argv_ptr, g_strdup("--message"));
g_ptr_array_add(argv_ptr, g_strdup("line"));
}
if (g_options->config && bsu->config == 1) {
g_ptr_array_add(argv_ptr, g_strdup("--config"));
g_ptr_array_add(argv_ptr, g_strdup(g_options->config));
}
if (g_options->timestamp && bsu->timestamp == 1) {
g_ptr_array_add(argv_ptr, g_strdup("--timestamp"));
g_ptr_array_add(argv_ptr, g_strdup(g_options->timestamp));
}
if (g_options->hostname && bsu->host == 1) {
g_ptr_array_add(argv_ptr, g_strdup("--host"));
g_ptr_array_add(argv_ptr, g_strdup(g_options->hostname));
}
g_ptr_array_add(argv_ptr, g_strdup("--device"));
g_ptr_array_add(argv_ptr, g_strdup(dle->device));
if (dle->disk && bsu->disk == 1) {
g_ptr_array_add(argv_ptr, g_strdup("--disk"));
g_ptr_array_add(argv_ptr, g_strdup(dle->disk));
}
for (j=0; j < nb_level; j++) {
g_ptr_array_add(argv_ptr, g_strdup("--level"));
g_snprintf(levelstr, sizeof(levelstr), "%d", levels[j]);
g_ptr_array_add(argv_ptr, g_strdup(levelstr));
}
/* find the first in ES_CLIENT and ES_CALCSIZE */
estimate = ES_CLIENT;
for (el = dle->estimatelist; el != NULL; el = el->next) {
estimate = (estimate_t)GPOINTER_TO_INT(el->data);
if ((estimate == ES_CLIENT && bsu->client_estimate) ||
(estimate == ES_CALCSIZE && bsu->calcsize))
break;
estimate = ES_CLIENT;
}
if (estimate == ES_CALCSIZE && bsu->calcsize) {
g_ptr_array_add(argv_ptr, g_strdup("--calcsize"));
}
application_property_add_to_argv(argv_ptr, dle, bsu, g_options->features);
for (scriptlist = dle->scriptlist; scriptlist != NULL;
scriptlist = scriptlist->next) {
script = (script_t *)scriptlist->data;
if (script->result && script->result->proplist) {
property_add_to_argv(argv_ptr, script->result->proplist);
}
}
g_ptr_array_add(argv_ptr, NULL);
args = (gchar **)g_ptr_array_free(argv_ptr, FALSE);
/*
* We need to operate a trick here: we want to display the full command
* path, but args contains the short command form...
*/
cmdargs = g_strjoinv(" ", args + 1);
cmdline = g_strconcat(cmd, " ", cmdargs, NULL);
g_free(cmdargs);
dbprintf("running: \"%s\"\n", cmdline);
amfree(cmdline);
if (pipe(pipeerrfd) < 0) {
errmsg = g_strdup_printf(_("getsize_application_api could not create data pipes: %s"),
strerror(errno));
goto common_exit;
}
if (pipe(pipeinfd) < 0) {
errmsg = g_strdup_printf(_("getsize_application_api could not create data pipes: %s"),
strerror(errno));
goto common_exit;
}
if (pipe(pipeoutfd) < 0) {
errmsg = g_strdup_printf(_("getsize_application_api could not create data pipes: %s"),
strerror(errno));
goto common_exit;
}
start_time = curclock();
switch(dumppid = fork()) {
case -1:
size = (off_t)-1;
goto common_exit;
default:
break; /* parent */
case 0:
dup2(pipeinfd[0], 0);
dup2(pipeoutfd[1], 1);
dup2(pipeerrfd[1], 2);
aclose(pipeinfd[1]);
aclose(pipeoutfd[0]);
aclose(pipeerrfd[0]);
safe_fd(-1, 0);
env = safe_env();
execve(cmd, args, env);
error(_("exec %s failed: %s"), cmd, strerror(errno));
free_env(env);
/*NOTREACHED*/
}
amfree(newoptstr);
aclose(pipeinfd[0]);
aclose(pipeoutfd[1]);
aclose(pipeerrfd[1]);
aclose(pipeinfd[1]);
dumpout = fdopen(pipeoutfd[0],"r");
if (!dumpout) {
error(_("Can't fdopen: %s"), strerror(errno));
/*NOTREACHED*/
}
for(size = (off_t)-1; (line = pgets(dumpout)) != NULL; free(line)) {
long long size1_ = (long long)0;
long long size2_ = (long long)0;
int level = 0;
if (line[0] == '\0')
continue;
dbprintf("%s\n", line);
if (g_str_has_prefix(line, "ERROR ")) {
char *errmsg, *qerrmsg;
errmsg = g_strdup(line+6);
qerrmsg = quote_string(errmsg);
dbprintf(_("errmsg is %s\n"), errmsg);
g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
amfree(qerrmsg);
amfree(errmsg);
continue;
}
i = sscanf(line, "%d %lld %lld", &level, &size1_, &size2_);
if (i != 3) {
i = sscanf(line, "%lld %lld", &size1_, &size2_);
level = levels[0];
if (i != 2) {
char *errmsg, *qerrmsg;
errmsg = g_strdup_printf(_("bad line %s"), line);
qerrmsg = quote_string(errmsg);
dbprintf(_("errmsg is %s\n"), errmsg);
g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[0], qerrmsg);
amfree(qerrmsg);
amfree(errmsg);
continue;
}
}
size1 = (off_t)size1_;
size2 = (off_t)size2_;
if (size1 <= -2 && size2 <= -2)
size = -2;
else if (size1 <= 0 || size2 <=0)
size = -1;
else if (size1 * size2 > 0)
size = size1 * size2;
dbprintf(_("estimate size for %s level %d: %lld KB\n"),
qamdevice,
level,
(long long)size);
g_printf("%s %d SIZE %lld\n", est->qamname, level, (long long)size);
}
amfree(line);
dumperr = fdopen(pipeerrfd[0],"r");
if (!dumperr) {
error(_("Can't fdopen: %s"), strerror(errno));
/*NOTREACHED*/
}
while ((line = pgets(dumperr)) != NULL) {
if (strlen(line) > 0) {
char *err = g_strdup_printf(_("Application '%s': %s"),
dle->program, line);
char *qerr = quote_string(err);
for (j=0; j < nb_level; j++) {
fprintf(stdout, "%s %d ERROR %s\n",
est->qamname, levels[j], qerr);
}
dbprintf("ERROR %s", qerr);
amfree(err);
amfree(qerr);
}
amfree(line);
}
dbprintf(".....\n");
if (nb_level == 1) {
dbprintf(_("estimate time for %s level %d: %s\n"), qamdevice,
levels[0], walltime_str(timessub(curclock(), start_time)));
} else {
dbprintf(_("estimate time for %s all level: %s\n"), qamdevice,
walltime_str(timessub(curclock(), start_time)));
}
(void)kill(-dumppid, SIGTERM);
dbprintf(_("waiting for %s \"%s\" child\n"), cmd, qdisk);
waitpid(dumppid, &wait_status, 0);
if (WIFSIGNALED(wait_status)) {
errmsg = g_strdup_printf(_("%s terminated with signal %d: see %s"),
cmd, WTERMSIG(wait_status), dbfn());
} else if (WIFEXITED(wait_status)) {
if (WEXITSTATUS(wait_status) != 0) {
errmsg = g_strdup_printf(_("%s exited with status %d: see %s"), cmd,
WEXITSTATUS(wait_status), dbfn());
} else {
/* Normal exit */
}
} else {
errmsg = g_strdup_printf(_("%s got bad exit: see %s"),
cmd, dbfn());
}
dbprintf(_("after %s %s wait\n"), cmd, qdisk);
afclose(dumpout);
afclose(dumperr);
common_exit:
amfree(cmd);
g_strfreev(args);
amfree(newoptstr);
amfree(qdisk);
amfree(qamdevice);
if (errmsg) {
char *qerrmsg = quote_string(errmsg);
dbprintf(_("errmsg is %s\n"), errmsg);
for (j=0; j < nb_level; j++) {
g_printf(_("%s %d ERROR %s\n"), est->qamname, levels[j], qerrmsg);
}
amfree(errmsg);
amfree(qerrmsg);
}
return size;
}
/*
* Returns the value of the first integer in a string.
*/
double
first_num(
char * str)
{
char *start;
int ch;
double d;
ch = *str++;
while(ch && !isdigit(ch)) ch = *str++;
start = str-1;
while(isdigit(ch) || (ch == '.')) ch = *str++;
str[-1] = '\0';
d = atof(start);
str[-1] = (char)ch;
return d;
}
/*
* Checks the dump output line against the error and size regex tables.
*/
off_t
handle_dumpline(
char * str)
{
regex_scale_t *rp;
double size;
/* check for size match */
/*@ignore@*/
for(rp = re_size; rp->regex != NULL; rp++) {
if(match(rp->regex, str)) {
size = ((first_num(str)*rp->scale+1023.0)/1024.0);
if(size < 0.0)
size = 1.0; /* found on NeXT -- sigh */
return (off_t)size;
}
}
/*@end@*/
return (off_t)-1;
}