/*
* 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.
*/
/* PROPERTY:
*
* BSDTAR-PATH (default /usr/bin/tar))
* STATE-DIR (default $AMSTATDIR/bsdtar)
* TARGET (DIRECTORY) (no default, if set, the backup will be from that
* directory instead of from the --device)
* ONE-FILE-SYSTEM (default YES)
* SPARSE (default YES) -S on restore
* INCLUDE-FILE
* INCLUDE-LIST
* INCLUDE-OPTIONAL
* EXCLUDE-FILE
* EXCLUDE-LIST
* EXCLUDE-OPTIONAL
* NORMAL
* IGNORE
* STRANGE
* EXIT-HANDLING (1=GOOD 2=BAD)
* TAR-BLOCKSIZE (default does not add --block-size option,
* using tar's default)
* VERBOSE
*/
#include "amanda.h"
#include "match.h"
#include "pipespawn.h"
#include "amfeatures.h"
#include "clock.h"
#include "amutil.h"
#include "getfsent.h"
#include "client_util.h"
#include "conffile.h"
#include "getopt.h"
#include "event.h"
#include "security-file.h"
#include "ammessage.h"
int debug_application = 1;
#define application_debug(i, ...) do { \
if ((i) <= debug_application) { \
g_debug(__VA_ARGS__); \
} \
} while (0)
static amregex_t init_re_table[] = {
/* tar prints the size in bytes */
AM_SIZE_RE("^ *Total bytes written: [0-9][0-9]*", 1, 1),
AM_NORMAL_RE("^could not open conf file"),
AM_NORMAL_RE("^Elapsed time:"),
AM_NORMAL_RE("^Throughput"),
AM_IGNORE_RE(": Directory is new$"),
AM_IGNORE_RE(": Directory has been renamed"),
/* GNU tar 1.13.17 will print this warning when (not) backing up a
Unix named socket. */
AM_NORMAL_RE(": socket ignored$"),
/* GNUTAR produces a few error messages when files are modified or
removed while it is running. They may cause data to be lost, but
then they may not. We shouldn't consider them NORMAL until
further investigation. */
AM_NORMAL_RE(": File .* shrunk by [0-9][0-9]* bytes, padding with zeros"),
AM_NORMAL_RE(": Cannot add file .*: No such file or directory$"),
AM_NORMAL_RE(": Error exit delayed from previous errors"),
AM_ERROR_RE("ambsdtar: error"),
/* catch-all: DMP_STRANGE is returned for all other lines */
AM_STRANGE_RE(NULL)
};
static amregex_t *re_table;
/* local functions */
int main(int argc, char **argv);
typedef struct application_argument_s {
char *config;
char *host;
int message;
int collection;
int calcsize;
char *tar_blocksize;
GSList *level;
GSList *command_options;
dle_t dle;
int argc;
char **argv;
int verbose;
} application_argument_t;
enum { CMD_ESTIMATE, CMD_BACKUP };
static void ambsdtar_support(application_argument_t *argument);
static void ambsdtar_selfcheck(application_argument_t *argument);
static void ambsdtar_estimate(application_argument_t *argument);
static void ambsdtar_backup(application_argument_t *argument);
static void ambsdtar_restore(application_argument_t *argument);
static void ambsdtar_validate(application_argument_t *argument);
static void ambsdtar_index(application_argument_t *argument);
static void ambsdtar_build_exinclude(dle_t *dle,
int *nb_exclude, char **file_exclude,
int *nb_include, char **file_include,
char *dirname, messagelist_t *mlist);
static char *ambsdtar_get_timestamps(application_argument_t *argument,
int level,
FILE *mesgstream, int command);
static void ambsdtar_set_timestamps(application_argument_t *argument,
int level,
char *timestamps,
FILE *mesgstream);
static GPtrArray *ambsdtar_build_argv(char *bsdtar_realpath,
application_argument_t *argument,
char *timestamps,
char **file_exclude, char **file_include,
int command, messagelist_t *mlist);
static char *command = NULL;
static char *bsdtar_path;
static char *state_dir;
static char *bsdtar_target;
static int bsdtar_onefilesystem;
static int bsdtar_sparse;
static GSList *normal_message = NULL;
static GSList *ignore_message = NULL;
static GSList *strange_message = NULL;
static char *exit_handling;
static int exit_value[256];
static off_t gblocksize = 0;
static int ambsdtar_exit_value = 0;
static FILE *mesgstream;
static gboolean restore_ok = TRUE;
static struct option long_options[] = {
{"config" , 1, NULL, 1},
{"host" , 1, NULL, 2},
{"disk" , 1, NULL, 3},
{"device" , 1, NULL, 4},
{"level" , 1, NULL, 5},
{"index" , 1, NULL, 6},
{"message" , 1, NULL, 7},
{"collection" , 0, NULL, 8},
{"record" , 0, NULL, 9},
{"bsdtar-path" , 1, NULL, 10},
{"state-dir" , 1, NULL, 11},
{"one-file-system" , 1, NULL, 12},
{"include-file" , 1, NULL, 16},
{"include-list" , 1, NULL, 17},
{"include-optional", 1, NULL, 18},
{"exclude-file" , 1, NULL, 19},
{"exclude-list" , 1, NULL, 20},
{"exclude-optional", 1, NULL, 21},
{"directory" , 1, NULL, 22},
{"normal" , 1, NULL, 23},
{"ignore" , 1, NULL, 24},
{"strange" , 1, NULL, 25},
{"exit-handling" , 1, NULL, 26},
{"calcsize" , 0, NULL, 27},
{"tar-blocksize" , 1, NULL, 28},
{"no-unquote" , 1, NULL, 29},
{"command-options" , 1, NULL, 33},
{"verbose" , 1, NULL, 36},
{"sparse" , 1, NULL, 37},
{"target" , 1, NULL, 38},
{NULL, 0, NULL, 0}
};
static message_t *
ambsdtar_print_message(
message_t *message)
{
if (strcasecmp(command, "selfcheck") == 0) {
return print_message(message);
}
if (message_get_severity(message) <= MSG_INFO) {
if (g_str_equal(command, "estimate")) {
fprintf(stdout, "OK %s\n", get_message(message));
} else if (g_str_equal(command, "backup")) {
fprintf(mesgstream, "| %s\n", get_message(message));
}
} else {
ambsdtar_exit_value = 1;
if (g_str_equal(command, "estimate")) {
fprintf(stdout, "ERROR %s\n", get_message(message));
} else if (g_str_equal(command, "backup")) {
fprintf(mesgstream, "sendbackup: error [%s]\n", get_message(message));
}
}
return message;
}
static char *
escape_tar_glob(
char *str,
int *in_argv)
{
char *result = malloc(4*strlen(str)+1);
char *r = result;
char *s;
*in_argv = 0;
for (s = str; *s != '\0'; s++) {
if (*s == '\\') {
char c = *(s+1);
if (c == '\\') {
*r++ = '\\';
*r++ = '\\';
*r++ = '\\';
s++;
} else if (c == '?') {
*r++ = 127;
s++;
continue;
} else if (c == 'a') {
*r++ = 7;
s++;
continue;
} else if (c == 'b') {
*r++ = 8;
s++;
continue;
} else if (c == 'f') {
*r++ = 12;
s++;
continue;
} else if (c == 'n') {
*r++ = 10;
s++;
*in_argv = 1;
continue;
} else if (c == 'r') {
*r++ = 13;
s++;
*in_argv = 1;
continue;
} else if (c == 't') {
*r++ = 9;
s++;
continue;
} else if (c == 'v') {
*r++ = 11;
s++;
continue;
} else if (c >= '0' && c <= '9') {
char d = c-'0';
s++;
c = *(s+1);
if (c >= '0' && c <= '9') {
d = (d*8)+(c-'0');
s++;
c = *(s+1);
if (c >= '0' && c <= '9') {
d = (d*8)+(c-'0');
s++;
}
}
*r++ = d;
continue;
} else {
*r++ = '\\';
}
} else if (*s == '?') {
*r++ = '\\';
*r++ = '\\';
} else if (*s == '*' || *s == '[') {
*r++ = '\\';
}
*r++ = *s;
}
*r = '\0';
return result;
}
int
main(
int argc,
char ** argv)
{
int c;
application_argument_t argument;
int i;
char *bsdtar_onefilesystem_value = NULL;
#ifdef BSDTAR
bsdtar_path = g_strdup(BSDTAR);
#else
bsdtar_path = NULL;
#endif
state_dir = NULL;
bsdtar_target = NULL;
bsdtar_onefilesystem = 1;
bsdtar_sparse = 1;
exit_handling = NULL;
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");
if (argc < 2) {
printf("ERROR no command given to ambsdtar\n");
error(_("No command given to ambsdtar"));
}
/* drop root privileges */
if (!set_root_privs(0)) {
if (g_str_equal(argv[1], "selfcheck")) {
printf("ERROR ambsdtar must be run setuid root\n");
}
error(_("ambsdtar must be run setuid root"));
}
safe_fd(3, 2);
set_pname("ambsdtar");
set_pcomponent("application");
set_pmodule("ambsdtar");
/* Don't die when child closes pipe */
signal(SIGPIPE, SIG_IGN);
#if defined(USE_DBMALLOC)
malloc_size_1 = malloc_inuse(&malloc_hist_1);
#endif
add_amanda_log_handler(amanda_log_stderr);
add_amanda_log_handler(amanda_log_syslog);
dbopen(DBG_SUBDIR_CLIENT);
startclock();
g_debug(_("version %s"), VERSION);
config_init(CONFIG_INIT_CLIENT|CONFIG_INIT_GLOBAL, NULL);
//check_running_as(RUNNING_AS_DUMPUSER_PREFERRED);
//root for amrecover
//RUNNING_AS_CLIENT_LOGIN from selfcheck, sendsize, sendbackup
/* parse argument */
command = argv[1];
if (strcasecmp(command,"selfcheck") == 0) {
fprintf(stdout, "MESSAGE JSON\n");
}
argument.config = NULL;
argument.host = NULL;
argument.message = 0;
argument.collection = 0;
argument.calcsize = 0;
argument.tar_blocksize = NULL;
argument.level = NULL;
argument.command_options = NULL;
argument.verbose = 0;
init_dle(&argument.dle);
argument.dle.record = 0;
while (1) {
int option_index = 0;
c = getopt_long (argc, argv, "", long_options, &option_index);
if (c == -1) {
break;
}
switch (c) {
case 1: amfree(argument.config);
argument.config = g_strdup(optarg);
break;
case 2: amfree(argument.host);
argument.host = g_strdup(optarg);
break;
case 3: amfree(argument.dle.disk);
argument.dle.disk = g_strdup(optarg);
break;
case 4: amfree(argument.dle.device);
argument.dle.device = g_strdup(optarg);
break;
case 5: argument.level = g_slist_append(argument.level,
GINT_TO_POINTER(atoi(optarg)));
break;
case 6: argument.dle.create_index = 1;
break;
case 7: argument.message = 1;
break;
case 8: argument.collection = 1;
break;
case 9: argument.dle.record = 1;
break;
case 10: amfree(bsdtar_path);
bsdtar_path = g_strdup(optarg);
break;
case 11: amfree(state_dir);
state_dir = g_strdup(optarg);
break;
case 12: amfree(bsdtar_onefilesystem_value);
bsdtar_onefilesystem_value = g_strdup(optarg);
break;
case 16: argument.dle.include_file =
append_sl(argument.dle.include_file, optarg);
break;
case 17: argument.dle.include_list =
append_sl(argument.dle.include_list, optarg);
break;
case 18: argument.dle.include_optional = 1;
break;
case 19: argument.dle.exclude_file =
append_sl(argument.dle.exclude_file, optarg);
break;
case 20: argument.dle.exclude_list =
append_sl(argument.dle.exclude_list, optarg);
break;
case 21: argument.dle.exclude_optional = 1;
break;
case 22: amfree(bsdtar_target);
bsdtar_target = g_strdup(optarg);
break;
case 23: normal_message =
g_slist_append(normal_message, optarg);
break;
case 24: ignore_message =
g_slist_append(ignore_message, optarg);
break;
case 25: strange_message =
g_slist_append(strange_message, optarg);
break;
case 26: amfree(exit_handling);
exit_handling = g_strdup(optarg);
break;
case 27: argument.calcsize = 1;
break;
case 28: amfree(argument.tar_blocksize);
argument.tar_blocksize = g_strdup(optarg);
gblocksize = atoi(argument.tar_blocksize);
break;
case 33: argument.command_options =
g_slist_append(argument.command_options,
g_strdup(optarg));
break;
case 36: if (strcasecmp(optarg, "YES") == 0)
argument.verbose = 1;
break;
case 37: if (strcasecmp(optarg, "NO") == 0)
bsdtar_sparse = 0;
break;
case 38: amfree(bsdtar_target);
bsdtar_target = g_strdup(optarg);
break;
case ':':
case '?':
break;
}
}
if (g_str_equal(command, "backup")) {
mesgstream = fdopen(3, "w");
if (!mesgstream) {
error(_("error mesgstream(%d): %s\n"), 3, strerror(errno));
}
}
if (!argument.dle.disk && argument.dle.device)
argument.dle.disk = g_strdup(argument.dle.device);
if (!argument.dle.device && argument.dle.disk)
argument.dle.device = g_strdup(argument.dle.disk);
if (bsdtar_onefilesystem_value) {
if (strcasecmp(bsdtar_onefilesystem_value, "NO") == 0) {
bsdtar_onefilesystem = 0;
} else if (strcasecmp(bsdtar_onefilesystem_value, "YES") == 0) {
bsdtar_onefilesystem = 1;
} else {
delete_message(ambsdtar_print_message(build_message(
AMANDA_FILE, __LINE__, 3702007, MSG_ERROR, 4,
"value", bsdtar_onefilesystem_value,
"disk", argument.dle.disk,
"device", argument.dle.device,
"hostname", argument.host)));
}
}
argument.argc = argc - optind;
argument.argv = argv + optind;
if (argument.config) {
config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
argument.config);
dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
}
if (config_errors(NULL) >= CFGERR_ERRORS) {
g_critical(_("errors processing config file"));
}
if (state_dir && strlen(state_dir) == 0)
amfree(state_dir);
if (!state_dir) {
state_dir = g_strdup_printf("%s/%s", amdatadir, "bsdtar");
}
re_table = build_re_table(init_re_table, normal_message, ignore_message,
strange_message);
for(i=0;i<256;i++)
exit_value[i] = 1; /* BAD */
exit_value[0] = 0; /* GOOD */
if (exit_handling) {
char *s = exit_handling;
while (s) {
char *r = strchr(s, '=');
if (r) {
int j = atoi(s);
if (j >= 0 && j < 256) {
r++;
if (strncasecmp(r, "GOOD", 4) == 0) {
exit_value[j] = 0;
}
}
}
s = strchr(s+1, ' ');
}
}
if (bsdtar_path) {
g_debug("BSDTAR-PATH %s", bsdtar_path);
} else {
g_debug("BSDTAR-PATH is not set");
}
if (state_dir) {
g_debug("STATE-DIR %s", state_dir);
} else {
g_debug("STATE-DIR is not set");
}
if (bsdtar_target) {
g_debug("TARGET %s", bsdtar_target);
}
g_debug("ONE-FILE-SYSTEM %s", bsdtar_onefilesystem? "yes":"no");
{
amregex_t *rp;
for (rp = re_table; rp->regex != NULL; rp++) {
switch (rp->typ) {
case DMP_NORMAL : g_debug("NORMAL %s", rp->regex); break;
case DMP_IGNORE : g_debug("IGNORE %s", rp->regex); break;
case DMP_STRANGE: g_debug("STRANGE %s", rp->regex); break;
case DMP_SIZE : g_debug("SIZE %s", rp->regex); break;
case DMP_ERROR : g_debug("ERROR %s", rp->regex); break;
}
}
}
if (g_str_equal(command, "support")) {
ambsdtar_support(&argument);
} else if (g_str_equal(command, "selfcheck")) {
ambsdtar_selfcheck(&argument);
} else if (g_str_equal(command, "estimate")) {
ambsdtar_estimate(&argument);
} else if (g_str_equal(command, "backup")) {
ambsdtar_backup(&argument);
} else if (g_str_equal(command, "restore")) {
ambsdtar_restore(&argument);
} else if (g_str_equal(command, "validate")) {
ambsdtar_validate(&argument);
} else if (g_str_equal(command, "index")) {
ambsdtar_index(&argument);
} else {
g_debug("Unknown command `%s'.", command);
fprintf(stderr, "Unknown command `%s'.\n", command);
exit (1);
}
g_free(argument.config);
g_free(argument.host);
g_free(argument.dle.disk);
g_free(argument.dle.device);
g_free(argument.tar_blocksize);
g_slist_free(argument.level);
dbclose();
return ambsdtar_exit_value;
}
static char *validate_command_options(
application_argument_t *argument)
{
GSList *copt;
for (copt = argument->command_options; copt != NULL; copt = copt->next) {
char *opt = (char *)copt->data;
if (g_str_has_prefix(opt, "--use-compress-program")) {
return opt;
}
}
return NULL;
}
static void
ambsdtar_support(
application_argument_t *argument)
{
(void)argument;
fprintf(stdout, "CONFIG YES\n");
fprintf(stdout, "HOST YES\n");
fprintf(stdout, "DISK YES\n");
fprintf(stdout, "MAX-LEVEL 399\n");
fprintf(stdout, "INDEX-LINE YES\n");
fprintf(stdout, "INDEX-XML NO\n");
fprintf(stdout, "MESSAGE-LINE YES\n");
fprintf(stdout, "MESSAGE-SELFCHECK-JSON YES\n");
fprintf(stdout, "MESSAGE-XML NO\n");
fprintf(stdout, "RECORD YES\n");
fprintf(stdout, "INCLUDE-FILE YES\n");
fprintf(stdout, "INCLUDE-LIST YES\n");
fprintf(stdout, "INCLUDE-OPTIONAL YES\n");
fprintf(stdout, "EXCLUDE-FILE YES\n");
fprintf(stdout, "EXCLUDE-LIST YES\n");
fprintf(stdout, "EXCLUDE-OPTIONAL YES\n");
fprintf(stdout, "COLLECTION NO\n");
fprintf(stdout, "MULTI-ESTIMATE YES\n");
fprintf(stdout, "CALCSIZE YES\n");
fprintf(stdout, "CLIENT-ESTIMATE YES\n");
}
static void
ambsdtar_selfcheck(
application_argument_t *argument)
{
char *option;
messagelist_t mlist = NULL;
messagelist_t mesglist = NULL;
char *dirname;
if (argument->dle.disk) {
delete_message(ambsdtar_print_message(build_message(
AMANDA_FILE, __LINE__, 3702000, MSG_INFO, 3,
"disk", argument->dle.disk,
"device", argument->dle.device,
"hostname", argument->host)));
}
delete_message(ambsdtar_print_message(build_message(
AMANDA_FILE, __LINE__, 3702001, MSG_INFO, 4,
"version", VERSION,
"disk", argument->dle.disk,
"device", argument->dle.device,
"hostname", argument->host)));
if (bsdtar_target) {
dirname = bsdtar_target;
} else {
dirname = argument->dle.device;
}
ambsdtar_build_exinclude(&argument->dle, NULL, NULL, NULL, NULL,
dirname, &mlist);
for (mesglist = mlist; mesglist != NULL; mesglist = mesglist->next){
message_t *message = mesglist->data;
if (message_get_severity(message) > MSG_INFO)
ambsdtar_print_message(message);
delete_message(message);
}
g_slist_free(mlist);
delete_message(ambsdtar_print_message(build_message(
AMANDA_FILE, __LINE__, 3702004, MSG_INFO, 3,
"disk", argument->dle.disk,
"device", argument->dle.device,
"hostname", argument->host)));
if ((option = validate_command_options(argument))) {
delete_message(ambsdtar_print_message(build_message(
AMANDA_FILE, __LINE__, 3702014, MSG_ERROR, 4,
"disk", argument->dle.disk,
"device", argument->dle.device,
"hostname", argument->host,
"command-options", option)));
}
if (bsdtar_path) {
char *bsdtar_realpath;
message_t *message;
if ((message = check_exec_for_suid_message("BSDTAR_PATH", bsdtar_path, &bsdtar_realpath))) {
delete_message(ambsdtar_print_message(message));
} else {
message = ambsdtar_print_message(check_file_message(bsdtar_path, X_OK));
if (message && message_get_severity(message) <= MSG_INFO) {
} else {
char *bsdtar_version;
GPtrArray *argv_ptr = g_ptr_array_new();
g_ptr_array_add(argv_ptr, bsdtar_realpath);
g_ptr_array_add(argv_ptr, "--version");
g_ptr_array_add(argv_ptr, NULL);
bsdtar_version = get_first_line(argv_ptr);
if (bsdtar_version) {
char *tv, *bv;
for (tv = bsdtar_version; *tv && !g_ascii_isdigit(*tv); tv++);
for (bv = tv; *bv && *bv != ' '; bv++);
if (*bv) *bv = '\0';
delete_message(ambsdtar_print_message(build_message(
AMANDA_FILE, __LINE__, 3702002, MSG_INFO, 4,
"bsdtar-version", bv,
"disk", argument->dle.disk,
"device", argument->dle.device,
"hostname", argument->host)));
} else {
delete_message(ambsdtar_print_message(build_message(
AMANDA_FILE, __LINE__, 3702003, MSG_ERROR, 4,
"bsdtar-path", bsdtar_path,
"disk", argument->dle.disk,
"device", argument->dle.device,
"hostname", argument->host)));
}
g_ptr_array_free(argv_ptr, TRUE);
amfree(bsdtar_version);
}
if (message)
delete_message(message);
}
amfree(bsdtar_realpath);
} else {
delete_message(ambsdtar_print_message(build_message(
AMANDA_FILE, __LINE__, 3702005, MSG_ERROR, 3,
"disk", argument->dle.disk,
"device", argument->dle.device,
"hostname", argument->host)));
}
set_root_privs(1);
if (state_dir && strlen(state_dir) == 0)
state_dir = NULL;
if (state_dir) {
delete_message(ambsdtar_print_message(check_dir_message(state_dir, R_OK|W_OK)));
} else {
delete_message(ambsdtar_print_message(build_message(
AMANDA_FILE, __LINE__, 3702020, MSG_ERROR, 3,
"disk", argument->dle.disk,
"device", argument->dle.device,
"hostname", argument->host)));
}
if (bsdtar_target) {
delete_message(ambsdtar_print_message(check_dir_message(bsdtar_target, R_OK)));
} else if (argument->dle.device) {
delete_message(ambsdtar_print_message(check_dir_message(argument->dle.device, R_OK)));
}
if (argument->calcsize) {
char *calcsize = g_strjoin(NULL, amlibexecdir, "/", "calcsize", NULL);
delete_message(ambsdtar_print_message(check_file_message(calcsize, X_OK)));
delete_message(ambsdtar_print_message(check_suid_message(calcsize)));
amfree(calcsize);
}
set_root_privs(0);
}
static void
ambsdtar_estimate(
application_argument_t *argument)
{
char *incrname = NULL;
GPtrArray *argv_ptr;
int nullfd = -1;
int pipefd = -1;
FILE *dumpout = NULL;
off_t size = -1;
char line[32768];
char *errmsg = NULL;
char *qerrmsg = NULL;
char *qdisk = NULL;
amwait_t wait_status;
int tarpid;
amregex_t *rp;
times_t start_time;
int level;
GSList *levels;
char *file_exclude = NULL;
char *file_include = NULL;
GString *strbuf;
char *option;
char *bsdtar_realpath;
message_t *message;
if (!argument->level) {
fprintf(stderr, "ERROR No level argument\n");
error(_("No level argument"));
}
if (!argument->dle.disk) {
fprintf(stderr, "ERROR No disk argument\n");
error(_("No disk argument"));
}
if (!argument->dle.device) {
fprintf(stderr, "ERROR No device argument\n");
error(_("No device argument"));
}
if ((option = validate_command_options(argument))) {
fprintf(stdout, "ERROR Invalid '%s' COMMAND-OPTIONS\n", option);
error("Invalid '%s' COMMAND-OPTIONS", option);
}
if (state_dir && strlen(state_dir) == 0)
state_dir = NULL;
if (state_dir) {
message = check_dir_message(state_dir, R_OK|W_OK);
if (message) {
if (message_get_severity(message) > MSG_INFO) {
ambsdtar_print_message(message);
}
delete_message(message);
}
} else {
fprintf(mesgstream, "ERROR No STATE-DIR\n");
exit(1);
}
if (argument->calcsize) {
char *dirname;
int nb_exclude;
int nb_include;
messagelist_t mlist = NULL;
messagelist_t mesglist = NULL;
if (bsdtar_target) {
dirname = bsdtar_target;
} else {
dirname = argument->dle.device;
}
ambsdtar_build_exinclude(&argument->dle,
&nb_exclude, &file_exclude,
&nb_include, &file_include, dirname, &mlist);
for (mesglist = mlist; mesglist != NULL; mesglist = mesglist->next){
message_t *message = mesglist->data;
if (message_get_severity(message) > MSG_INFO)
fprintf(stdout, "ERROR %s\n", get_message(message));
delete_message(message);
}
g_slist_free(mlist);
mlist = NULL;
run_calcsize(argument->config, "BSDTAR", argument->dle.disk, dirname,
argument->level, file_exclude, file_include);
if (argument->verbose == 0) {
if (file_exclude)
unlink(file_exclude);
if (file_include)
unlink(file_include);
}
amfree(file_exclude);
amfree(file_include);
amfree(qdisk);
return;
}
if (!bsdtar_path) {
errmsg = g_strdup(_("BSDTAR-PATH not defined"));
goto common_error;
}
if ((message = check_exec_for_suid_message("BSDTAR_PATH", bsdtar_path, &bsdtar_realpath))) {
errmsg = g_strdup(get_message(message));
delete_message(message);
goto common_error;
}
qdisk = quote_string(argument->dle.disk);
for (levels = argument->level; levels != NULL; levels = levels->next) {
char *timestamps;
messagelist_t mlist = NULL;
messagelist_t mesglist = NULL;
level = GPOINTER_TO_INT(levels->data);
timestamps = ambsdtar_get_timestamps(argument, level, stdout, CMD_ESTIMATE);
argv_ptr = ambsdtar_build_argv(bsdtar_realpath,
argument, timestamps, &file_exclude,
&file_include, CMD_ESTIMATE, &mlist);
for (mesglist = mlist; mesglist != NULL; mesglist = mesglist->next){
message_t *message = mesglist->data;
if (message_get_severity(message) > MSG_INFO)
fprintf(stdout, "ERROR %s\n", get_message(message));
delete_message(message);
}
g_slist_free(mlist);
mlist = NULL;
amfree(timestamps);
start_time = curclock();
if ((nullfd = open("/dev/null", O_RDWR)) == -1) {
errmsg = g_strdup_printf(_("Cannot access /dev/null : %s"),
strerror(errno));
goto common_exit;
}
tarpid = pipespawnv(bsdtar_realpath, STDERR_PIPE, 1,
&nullfd, &nullfd, &pipefd,
(char **)argv_ptr->pdata);
dumpout = fdopen(pipefd,"r");
if (!dumpout) {
error(_("Can't fdopen: %s"), strerror(errno));
/*NOTREACHED*/
}
size = (off_t)-1;
while (size < 0 && (fgets(line, sizeof(line), dumpout) != NULL)) {
if (strlen(line) > 0 && line[strlen(line)-1] == '\n') {
/* remove trailling \n */
line[strlen(line)-1] = '\0';
}
if (line[0] == '\0')
continue;
g_debug("%s", line);
/* check for size match */
/*@ignore@*/
for(rp = re_table; rp->regex != NULL; rp++) {
if(match(rp->regex, line)) {
if (rp->typ == DMP_SIZE) {
off_t blocksize = gblocksize;
size = ((the_num(line, rp->field)*rp->scale+1023.0)/1024.0);
if(size < 0.0)
size = 1.0; /* found on NeXT -- sigh */
if (!blocksize) {
blocksize = 20;
}
blocksize /= 2;
size = (size+blocksize-1) / blocksize;
size *= blocksize;
}
break;
}
}
/*@end@*/
}
while (fgets(line, sizeof(line), dumpout) != NULL) {
g_debug("%s", line);
}
g_debug(".....");
g_debug(_("estimate time for %s level %d: %s"),
qdisk,
level,
walltime_str(timessub(curclock(), start_time)));
if(size == (off_t)-1) {
errmsg = g_strdup_printf(_("no size line match in %s output"), bsdtar_path);
g_debug(_("%s for %s"), errmsg, qdisk);
g_debug(".....");
} else if(size == (off_t)0 && argument->level == 0) {
g_debug(_("possible %s problem -- is \"%s\" really empty?"),
bsdtar_path, argument->dle.disk);
g_debug(".....");
}
g_debug(_("estimate size for %s level %d: %lld KB"),
qdisk,
level,
(long long)size);
(void)kill(-tarpid, SIGTERM);
g_debug(_("waiting for %s \"%s\" child"), bsdtar_path, qdisk);
waitpid(tarpid, &wait_status, 0);
if (WIFSIGNALED(wait_status)) {
strbuf = g_string_new(errmsg);
g_string_append_printf(strbuf, "%s terminated with signal %d: see %s",
bsdtar_path, WTERMSIG(wait_status), dbfn());
g_free(errmsg);
errmsg = g_string_free(strbuf, FALSE);
ambsdtar_exit_value = 1;
} else if (WIFEXITED(wait_status)) {
if (exit_value[WEXITSTATUS(wait_status)] == 1) {
strbuf = g_string_new(errmsg);
g_string_append_printf(strbuf, "%s exited with status %d: see %s",
bsdtar_path, WEXITSTATUS(wait_status), dbfn());
g_free(errmsg);
errmsg = g_string_free(strbuf, FALSE);
ambsdtar_exit_value = 1;
} else {
/* Normal exit */
}
} else {
errmsg = g_strdup_printf(_("%s got bad exit: see %s"),
bsdtar_path, dbfn());
ambsdtar_exit_value = 1;
}
g_debug(_("after %s %s wait"), bsdtar_path, qdisk);
common_exit:
if (errmsg) {
g_debug("%s", errmsg);
fprintf(stdout, "ERROR %s\n", errmsg);
amfree(errmsg);
}
if (argument->verbose == 0) {
if (file_exclude)
unlink(file_exclude);
if (file_include)
unlink(file_include);
}
g_ptr_array_free_full(argv_ptr);
amfree(bsdtar_path);
amfree(incrname);
aclose(nullfd);
afclose(dumpout);
fprintf(stdout, "%d %lld 1\n", level, (long long)size);
}
amfree(qdisk);
amfree(file_exclude);
amfree(file_include);
amfree(errmsg);
amfree(qerrmsg);
amfree(incrname);
return;
common_error:
qerrmsg = quote_string(errmsg);
amfree(qdisk);
g_debug("%s", errmsg);
fprintf(stdout, "ERROR %s\n", qerrmsg);
ambsdtar_exit_value = 1;
amfree(file_exclude);
amfree(file_include);
amfree(errmsg);
amfree(qerrmsg);
amfree(incrname);
return;
}
typedef struct filter_s {
int fd;
char *name;
char *buffer;
gint64 first; /* first byte used */
gint64 size; /* number of byte use in the buffer */
gint64 allocated_size; /* allocated size of the buffer */
event_handle_t *event;
int out;
} filter_t;
static off_t dump_size = -1;
static int dataf = 1;
static int index_in;
static FILE *indexstream = NULL;
static void read_fd(int fd, char *name, event_fn_t fn);
static void read_data_out(void *cookie);
static void read_text(void *cookie);
static void
read_fd(
int fd,
char *name,
event_fn_t fn)
{
filter_t *filter = g_new0(filter_t, 1);
filter->fd = fd;
filter->name = g_strdup(name);
filter->event = event_create((event_id_t)filter->fd, EV_READFD,
fn, filter);
event_activate(filter->event);
}
static void
read_data_out(
void *cookie)
{
filter_t *filter = cookie;
ssize_t nread;
ssize_t nwrite;
if (!filter->buffer) {
filter->buffer = malloc(32768);
}
nread = read(filter->fd, filter->buffer, 32768);
if (nread <= 0) {
aclose(filter->fd);
aclose(dataf);
aclose(index_in);
event_release(filter->event);
if (nread < 0) {
}
g_free(filter->buffer);
g_free(filter->name);
g_free(filter);
} else {
nwrite = full_write(dataf, filter->buffer, nread);
if (nwrite != nread) {
}
nwrite = full_write(index_in, filter->buffer, nread);
if (nwrite != nread) {
}
}
}
static void
read_text(
void *cookie)
{
filter_t *filter = cookie;
char *line;
char *p;
amregex_t *rp;
char *type;
char startchr;
ssize_t nread;
int len;
if (filter->buffer == NULL) {
/* allocate initial buffer */
filter->buffer = g_malloc(32768);
filter->first = 0;
filter->size = 0;
filter->allocated_size = 32768;
} else if (filter->first > 0) {
if (filter->allocated_size - filter->size - filter->first < 1024) {
memmove(filter->buffer, filter->buffer + filter->first,
filter->size);
filter->first = 0;
}
} else if (filter->allocated_size - filter->size < 1024) {
/* double the size of the buffer */
filter->allocated_size *= 2;
filter->buffer = g_realloc(filter->buffer, filter->allocated_size);
}
nread = read(filter->fd, filter->buffer + filter->first + filter->size,
filter->allocated_size - filter->first - filter->size - 2);
if (nread <= 0) {
event_release(filter->event);
aclose(filter->fd);
if (filter->size > 0 && filter->buffer[filter->first + filter->size - 1] != '\n') {
/* Add a '\n' at end of buffer */
filter->buffer[filter->first + filter->size] = '\n';
filter->size++;
}
} else {
filter->size += nread;
}
/* process all complete lines */
line = filter->buffer + filter->first;
line[filter->size] = '\0';
while (line < filter->buffer + filter->first + filter->size &&
(p = strchr(line, '\n')) != NULL) {
*p = '\0';
if (g_str_equal(filter->name, "data err")) {
g_debug("data err: %s", line);
for(rp = re_table; rp->regex != NULL; rp++) {
if(match(rp->regex, line)) {
break;
}
}
if(rp->typ == DMP_SIZE) {
off_t blocksize = gblocksize;
dump_size = (off_t)((the_num(line, rp->field)* rp->scale+1023.0)/1024.0);
if(dump_size < 0.0)
dump_size = 1.0; /* found on NeXT -- sigh */
if (!blocksize) {
blocksize = 20;
}
blocksize /= 2;
dump_size = (dump_size+blocksize-1) / blocksize;
dump_size *= blocksize;
}
switch(rp->typ) {
case DMP_NORMAL:
type = "normal";
startchr = '|';
break;
case DMP_IGNORE:
continue;
case DMP_STRANGE:
type = "strange";
startchr = '?';
break;
case DMP_SIZE:
type = "size";
startchr = '|';
break;
case DMP_ERROR:
type = "error";
startchr = '?';
break;
default:
type = "unknown";
startchr = '!';
break;
}
g_debug("%3d: %7s(%c): %s", rp->srcline, type, startchr, line);
fprintf(mesgstream,"%c %s\n", startchr, line);
} else if (g_str_equal(filter->name, "index out")) {
g_fprintf(indexstream, "%s\n", line+1); // remove initial '.'
} else if (g_str_equal(filter->name, "index err")) {
g_debug("index err: %s", line);
g_fprintf(mesgstream, "? %s\n", line);
} else {
g_debug("unknown: %s", line);
}
len = p - line + 1;
filter->first += len;
filter->size -= len;
line = p + 1;
}
if (nread <= 0) {
g_free(filter->name);
g_free(filter->buffer);
g_free(filter);
}
}
static void
ambsdtar_backup(
application_argument_t *argument)
{
int dumpin;
char *qdisk;
char *timestamps;
int indexf = 4;
int outf;
int data_out;
int index_out;
int index_err;
char *errmsg = NULL;
amwait_t wait_status;
GPtrArray *argv_ptr;
pid_t tarpid;
pid_t indexpid = 0;
time_t tt;
char *file_exclude;
char *file_include;
char new_timestamps[64];
char *option;
char *bsdtar_realpath;
messagelist_t mlist = NULL;
messagelist_t mesglist = NULL;
message_t *message;
if (!bsdtar_path) {
fprintf(mesgstream, "sendbackup: error [BSDTAR-PATH not defined]\n");
exit(1);
}
if ((message = check_exec_for_suid_message("BSDTAR_PATH", bsdtar_path, &bsdtar_realpath))) {
fprintf(mesgstream, "sendbackup: error [%s]", get_message(message));
exit(1);
}
if ((option = validate_command_options(argument))) {
fprintf(mesgstream, "sendbackup: error [Invalid '%s' COMMAND-OPTIONS]\n", option);
exit(1);
}
if (state_dir && strlen(state_dir) == 0)
state_dir = NULL;
if (state_dir) {
message = check_dir_message(state_dir, R_OK|W_OK);
if (message) {
if (message_get_severity(message) > MSG_INFO) {
ambsdtar_print_message(message);
}
delete_message(message);
}
} else {
fprintf(mesgstream, "sendbackup: error [STATE-DIR not defined]\n");
exit(1);
}
if (!argument->level) {
fprintf(mesgstream, "sendbackup: error [No level argument]\n");
exit(1);
}
if (!argument->dle.disk) {
fprintf(mesgstream, "sendbackup: error [No disk argument]\n");
exit(1);
}
if (!argument->dle.device) {
fprintf(mesgstream, "sendbackup: error [No device argument]\n");
exit(1);
}
qdisk = quote_string(argument->dle.disk);
tt = time(NULL);
ctime_r(&tt, new_timestamps);
new_timestamps[strlen(new_timestamps)-1] = '\0';
timestamps = ambsdtar_get_timestamps(argument,
GPOINTER_TO_INT(argument->level->data),
mesgstream, CMD_BACKUP);
argv_ptr = ambsdtar_build_argv(bsdtar_realpath,
argument, timestamps, &file_exclude,
&file_include, CMD_BACKUP, &mlist);
for (mesglist = mlist; mesglist != NULL; mesglist = mesglist->next){
message_t *message = mesglist->data;
if (message_get_severity(message) <= MSG_INFO) {
fprintf(mesgstream, "| %s\n", get_message(message));
} else {
fprintf(mesgstream, "? %s\n", get_message(message));
}
delete_message(message);
}
g_slist_free(mlist);
mlist = NULL;
if (argument->dle.create_index) {
tarpid = pipespawnv(bsdtar_path, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 1,
&dumpin, &data_out, &outf, (char **)argv_ptr->pdata);
g_ptr_array_free_full(argv_ptr);
argv_ptr = g_ptr_array_new();
g_ptr_array_add(argv_ptr, g_strdup(bsdtar_path));
g_ptr_array_add(argv_ptr, g_strdup("tf"));
g_ptr_array_add(argv_ptr, g_strdup("-"));
g_ptr_array_add(argv_ptr, NULL);
indexpid = pipespawnv(bsdtar_path, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 1,
&index_in, &index_out, &index_err,
(char **)argv_ptr->pdata);
g_ptr_array_free_full(argv_ptr);
aclose(dumpin);
indexstream = fdopen(indexf, "w");
if (!indexstream) {
error(_("error indexstream(%d): %s\n"), indexf, strerror(errno));
}
read_fd(data_out , "data out" , &read_data_out);
read_fd(outf , "data err" , &read_text);
read_fd(index_out, "index out", &read_text);
read_fd(index_err, "index err", &read_text);
} else {
tarpid = pipespawnv(bsdtar_path, STDIN_PIPE|STDERR_PIPE, 1,
&dumpin, &dataf, &outf, (char **)argv_ptr->pdata);
g_ptr_array_free_full(argv_ptr);
aclose(dumpin);
aclose(dataf);
read_fd(outf, "data err", &read_text);
}
event_loop(0);
waitpid(tarpid, &wait_status, 0);
if (WIFSIGNALED(wait_status)) {
errmsg = g_strdup_printf(_("%s terminated with signal %d: see %s"),
bsdtar_path, WTERMSIG(wait_status), dbfn());
ambsdtar_exit_value = 1;
} else if (WIFEXITED(wait_status)) {
if (exit_value[WEXITSTATUS(wait_status)] == 1) {
errmsg = g_strdup_printf(_("%s exited with status %d: see %s"),
bsdtar_path, WEXITSTATUS(wait_status), dbfn());
ambsdtar_exit_value = 1;
} else {
/* Normal exit */
}
} else {
errmsg = g_strdup_printf(_("%s got bad exit: see %s"),
bsdtar_path, dbfn());
ambsdtar_exit_value = 1;
}
if (argument->dle.create_index) {
waitpid(indexpid, &wait_status, 0);
if (WIFSIGNALED(wait_status)) {
errmsg = g_strdup_printf(_("'%s index' terminated with signal %d: see %s"),
bsdtar_path, WTERMSIG(wait_status), dbfn());
ambsdtar_exit_value = 1;
} else if (WIFEXITED(wait_status)) {
if (exit_value[WEXITSTATUS(wait_status)] == 1) {
errmsg = g_strdup_printf(_("'%s index' exited with status %d: see %s"),
bsdtar_path, WEXITSTATUS(wait_status), dbfn());
ambsdtar_exit_value = 1;
} else {
/* Normal exit */
}
} else {
errmsg = g_strdup_printf(_("'%s index' got bad exit: see %s"),
bsdtar_path, dbfn());
ambsdtar_exit_value = 1;
}
}
g_debug(_("after %s %s wait"), bsdtar_path, qdisk);
g_debug(_("ambsdtar: %s: pid %ld"), bsdtar_path, (long)tarpid);
if (errmsg) {
g_debug("%s", errmsg);
g_fprintf(mesgstream, "sendbackup: error [%s]\n", errmsg);
ambsdtar_exit_value = 1;
} else if (argument->dle.record) {
ambsdtar_set_timestamps(argument,
GPOINTER_TO_INT(argument->level->data),
new_timestamps,
mesgstream);
}
g_debug("sendbackup: size %lld", (long long)dump_size);
fprintf(mesgstream, "sendbackup: size %lld\n", (long long)dump_size);
if (argument->dle.create_index)
fclose(indexstream);
fclose(mesgstream);
if (argument->verbose == 0) {
if (file_exclude)
unlink(file_exclude);
if (file_include)
unlink(file_include);
}
amfree(file_exclude);
amfree(file_include);
amfree(timestamps);
amfree(qdisk);
amfree(bsdtar_path);
amfree(errmsg);
}
static ssize_t
read_filter_buffer(
filter_t *filter)
{
ssize_t nread;
if (filter->buffer == NULL) {
/* allocate initial buffer */
filter->buffer = g_malloc(2048);
filter->first = 0;
filter->size = 0;
filter->allocated_size = 2048;
} else if (filter->first > 0) {
if (filter->allocated_size - filter->size - filter->first < 1024) {
memmove(filter->buffer, filter->buffer + filter->first,
filter->size);
filter->first = 0;
}
} else if (filter->allocated_size - filter->size < 1024) {
/* double the size of the buffer */
filter->allocated_size *= 2;
filter->buffer = g_realloc(filter->buffer, filter->allocated_size);
}
nread = read(filter->fd, filter->buffer + filter->first + filter->size,
filter->allocated_size - filter->first - filter->size - 2);
if (nread <= 0) {
event_release(filter->event);
aclose(filter->fd);
if (filter->size > 0 && filter->buffer[filter->first + filter->size - 1] != '\n') {
/* Add a '\n' at end of buffer */
filter->buffer[filter->first + filter->size] = '\n';
filter->size++;
}
} else {
filter->size += nread;
}
return nread;
}
static void
handle_restore_stdin(
void *cookie)
{
filter_t *filter = cookie;
ssize_t nread;
if (filter->buffer == NULL) {
/* allocate initial buffer */
filter->buffer = g_malloc(65536);
filter->first = 0;
filter->size = 0;
filter->allocated_size = 65536;
}
nread = read(filter->fd, filter->buffer, filter->allocated_size);
if (nread > 0) {
/* process the complete buffer */
int nwrite = full_write(filter->out , filter->buffer, nread);
if (nwrite < nread) {
ambsdtar_exit_value = 1;
g_debug("wrote only %d bytes", nwrite);
event_release(filter->event);
filter->event = NULL;
aclose(filter->fd);
g_free(filter->buffer);
filter->buffer = NULL;
aclose(filter->out);
}
} else {
event_release(filter->event);
filter->event = NULL;
aclose(filter->fd);
g_free(filter->buffer);
filter->buffer = NULL;
aclose(filter->out);
}
}
static void
handle_restore_stdout(
void *cookie)
{
filter_t *filter = cookie;
ssize_t nread;
char *b, *p;
gint64 len;
nread = read_filter_buffer(filter);
/* process all complete lines */
b = filter->buffer + filter->first;
b[filter->size] = '\0';
while (b < filter->buffer + filter->first + filter->size &&
(p = strchr(b, '\n')) != NULL) {
*p = '\0';
g_fprintf(stdout, "%s\n", b);
len = p - b + 1;
filter->first += len;
filter->size -= len;
b = p + 1;
}
if (nread <= 0) {
g_free(filter->buffer);
}
}
static void
handle_restore_stderr(
void *cookie)
{
filter_t *filter = cookie;
ssize_t nread;
char *b, *p;
gint64 len;
nread = read_filter_buffer(filter);
/* process all complete lines */
b = filter->buffer + filter->first;
b[filter->size] = '\0';
while (b < filter->buffer + filter->first + filter->size &&
(p = strchr(b, '\n')) != NULL) {
*p = '\0';
if (*b == 'x' && *(b+1) == ' ') {
g_fprintf(stdout, "%s\n", b+2);
} else {
gboolean line_is_error = TRUE;
if (restore_ok) {
if ((strncmp(b, "bsdtar: copyfile unpack (./.) failed: Operation not permitted", 61) == 0) ||
(strncmp(b, "bsdtar: Error exit delayed from previous errors.", 48) == 0)) {
line_is_error = FALSE;
} else {
restore_ok = FALSE;
}
}
if (line_is_error) {
g_fprintf(stderr, "%s\n", b);
}
}
len = p - b + 1;
filter->first += len;
filter->size -= len;
b = p + 1;
}
if (nread <= 0) {
g_free(filter->buffer);
}
}
static void
ambsdtar_restore(
application_argument_t *argument)
{
GPtrArray *argv_ptr = g_ptr_array_new();
int j;
char *include_filename = NULL;
char *exclude_filename = NULL;
int tarpid;
filter_t in_buf;
filter_t out_buf;
filter_t err_buf;
int tarin, tarout, tarerr;
char *errmsg = NULL;
amwait_t wait_status;
char *bsdtar_realpath;
message_t *message;
restore_ok = TRUE;
if (!bsdtar_path) {
error(_("BSDTAR-PATH not defined"));
}
if ((message = check_exec_for_suid_message("BSDTAR_PATH", bsdtar_path, &bsdtar_realpath))) {
fprintf(stderr, "%s\n", get_message(message));
delete_message(message);
exit(1);
}
if (!security_allow_to_restore()) {
error("The user is not allowed to restore files");
}
g_ptr_array_add(argv_ptr, g_strdup(bsdtar_realpath));
g_ptr_array_add(argv_ptr, g_strdup("--numeric-owner"));
/* ignore trailing zero blocks on input (this was the default until tar-1.21) */
if (argument->tar_blocksize) {
g_ptr_array_add(argv_ptr, g_strdup("--block-size"));
g_ptr_array_add(argv_ptr, g_strdup(argument->tar_blocksize));
}
g_ptr_array_add(argv_ptr, g_strdup("-xvf"));
g_ptr_array_add(argv_ptr, g_strdup("-"));
if (bsdtar_sparse) {
g_ptr_array_add(argv_ptr, g_strdup("-S"));
}
if (bsdtar_target) {
struct stat stat_buf;
if(stat(bsdtar_target, &stat_buf) != 0) {
fprintf(stderr,"can not stat directory %s: %s\n", bsdtar_target, strerror(errno));
exit(1);
}
if (!S_ISDIR(stat_buf.st_mode)) {
fprintf(stderr,"%s is not a directory\n", bsdtar_target);
exit(1);
}
if (access(bsdtar_target, W_OK) != 0) {
fprintf(stderr, "Can't write to %s: %s\n", bsdtar_target, strerror(errno));
exit(1);
}
g_ptr_array_add(argv_ptr, g_strdup("--directory"));
g_ptr_array_add(argv_ptr, g_strdup(bsdtar_target));
}
if (argument->dle.exclude_list &&
argument->dle.exclude_list->nb_element == 1) {
FILE *exclude;
char *sdisk;
int in_argv;
int entry_in_exclude = 0;
char line[2*PATH_MAX];
FILE *exclude_list;
if (argument->dle.disk) {
sdisk = sanitise_filename(argument->dle.disk);
} else {
sdisk = g_strdup_printf("no_dle-%d", (int)getpid());
}
exclude_filename= g_strjoin(NULL, AMANDA_TMPDIR, "/", "exclude-", sdisk, NULL);
exclude_list = fopen(argument->dle.exclude_list->first->name, "r");
if (!exclude_list) {
fprintf(stderr, "Cannot open exclude file '%s': %s\n",
argument->dle.exclude_list->first->name, strerror(errno));
error("Cannot open exclude file '%s': %s\n",
argument->dle.exclude_list->first->name, strerror(errno));
/*NOTREACHED*/
}
exclude = fopen(exclude_filename, "w");
if (!exclude) {
fprintf(stderr, "Cannot open exclude file '%s': %s\n",
exclude_filename, strerror(errno));
fclose(exclude_list);
error("Cannot open exclude file '%s': %s\n",
exclude_filename, strerror(errno));
/*NOTREACHED*/
}
while (fgets(line, 2*PATH_MAX, exclude_list)) {
char *escaped;
line[strlen(line)-1] = '\0'; /* remove '\n' */
escaped = escape_tar_glob(line, &in_argv);
if (in_argv) {
g_ptr_array_add(argv_ptr, "--exclude");
g_ptr_array_add(argv_ptr, escaped);
} else {
fprintf(exclude,"%s\n", escaped);
entry_in_exclude++;
amfree(escaped);
}
}
fclose(exclude_list);
fclose(exclude);
g_ptr_array_add(argv_ptr, g_strdup("--exclude-from"));
g_ptr_array_add(argv_ptr, exclude_filename);
}
{
GPtrArray *argv_include = g_ptr_array_new();
FILE *include;
char *sdisk;
int in_argv;
guint i;
int entry_in_include = 0;
if (argument->dle.disk) {
sdisk = sanitise_filename(argument->dle.disk);
} else {
sdisk = g_strdup_printf("no_dle-%d", (int)getpid());
}
include_filename = g_strjoin(NULL, AMANDA_TMPDIR, "/", "include-", sdisk, NULL);
include = fopen(include_filename, "w");
if (!include) {
fprintf(stderr, "Cannot open include file '%s': %s\n",
include_filename, strerror(errno));
error("Cannot open include file '%s': %s\n",
include_filename, strerror(errno));
/*NOTREACHED*/
}
if (argument->dle.include_list &&
argument->dle.include_list->nb_element == 1) {
char line[2*PATH_MAX];
FILE *include_list = fopen(argument->dle.include_list->first->name, "r");
if (!include_list) {
fclose(include);
fprintf(stderr, "Cannot open include file '%s': %s\n",
argument->dle.include_list->first->name,
strerror(errno));
error("Cannot open include file '%s': %s\n",
argument->dle.include_list->first->name,
strerror(errno));
/*NOTREACHED*/
}
while (fgets(line, 2*PATH_MAX, include_list)) {
char *escaped;
line[strlen(line)-1] = '\0'; /* remove '\n' */
if (!g_str_equal(line, ".")) {
escaped = escape_tar_glob(line, &in_argv);
if (in_argv) {
g_ptr_array_add(argv_include, escaped);
} else {
fprintf(include,"%s\n", escaped);
entry_in_include++;
amfree(escaped);
}
}
}
fclose(include_list);
}
for (j=1; j< argument->argc; j++) {
if (!g_str_equal(argument->argv[j], ".")) {
char *escaped = escape_tar_glob(argument->argv[j], &in_argv);
if (in_argv) {
g_ptr_array_add(argv_include, escaped);
} else {
fprintf(include,"%s\n", escaped);
entry_in_include++;
amfree(escaped);
}
}
}
fclose(include);
if (entry_in_include) {
g_ptr_array_add(argv_ptr, g_strdup("--files-from"));
g_ptr_array_add(argv_ptr, include_filename);
}
for (i = 0; i < argv_include->len; i++) {
g_ptr_array_add(argv_ptr, (char *)g_ptr_array_index(argv_include,i));
}
amfree(sdisk);
}
g_ptr_array_add(argv_ptr, NULL);
debug_executing(argv_ptr);
tarpid = pipespawnv(bsdtar_path, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 1,
&tarin, &tarout, &tarerr, (char **)argv_ptr->pdata);
in_buf.fd = 0;
in_buf.out = tarin;
in_buf.name = "stdin";
in_buf.buffer = NULL;
in_buf.first = 0;
in_buf.size = 0;
in_buf.allocated_size = 0;
out_buf.fd = tarout;
out_buf.name = "stdout";
out_buf.buffer = NULL;
out_buf.first = 0;
out_buf.size = 0;
out_buf.allocated_size = 0;
err_buf.fd = tarerr;
err_buf.name = "stderr";
err_buf.buffer = NULL;
err_buf.first = 0;
err_buf.size = 0;
err_buf.allocated_size = 0;
in_buf.event = event_create((event_id_t)0, EV_READFD,
handle_restore_stdin, &in_buf);
out_buf.event = event_create((event_id_t)tarout, EV_READFD,
handle_restore_stdout, &out_buf);
err_buf.event = event_create((event_id_t)tarerr, EV_READFD,
handle_restore_stderr, &err_buf);
event_activate(in_buf.event);
event_activate(out_buf.event);
event_activate(err_buf.event);
event_loop(0);
waitpid(tarpid, &wait_status, 0);
if (WIFSIGNALED(wait_status)) {
errmsg = g_strdup_printf(_("%s terminated with signal %d: see %s"),
bsdtar_path, WTERMSIG(wait_status), dbfn());
ambsdtar_exit_value = 1;
} else if (WIFEXITED(wait_status) && WEXITSTATUS(wait_status) > 0) {
if (!restore_ok || WEXITSTATUS(wait_status) != 1) {
errmsg = g_strdup_printf(_("%s exited with status %d: see %s"),
bsdtar_path, WEXITSTATUS(wait_status), dbfn());
ambsdtar_exit_value = 1;
}
}
g_debug(_("ambsdtar: %s: pid %ld"), bsdtar_path, (long)tarpid);
if (errmsg) {
g_debug("%s", errmsg);
fprintf(stderr, "error [%s]\n", errmsg);
}
if (argument->verbose == 0) {
if (exclude_filename)
unlink(exclude_filename);
unlink(include_filename);
}
amfree(bsdtar_path);
amfree(include_filename);
amfree(exclude_filename);
g_free(errmsg);
}
static void
ambsdtar_validate(
application_argument_t *argument G_GNUC_UNUSED)
{
char *cmd = NULL;
GPtrArray *argv_ptr = g_ptr_array_new();
char **env;
char *e;
char buf[32768];
if (!bsdtar_path) {
g_debug("BSDTAR-PATH not set; Piping to /dev/null");
fprintf(stderr,"BSDTAR-PATH not set; Piping to /dev/null\n");
goto pipe_to_null;
}
cmd = g_strdup(bsdtar_path);
g_ptr_array_add(argv_ptr, g_strdup(bsdtar_path));
/* ignore trailing zero blocks on input (this was the default until tar-1.21) */
g_ptr_array_add(argv_ptr, g_strdup("-tf"));
g_ptr_array_add(argv_ptr, g_strdup("-"));
g_ptr_array_add(argv_ptr, NULL);
debug_executing(argv_ptr);
env = safe_env();
execve(cmd, (char **)argv_ptr->pdata, env);
e = strerror(errno);
g_debug("failed to execute %s: %s; Piping to /dev/null", cmd, e);
fprintf(stderr,"failed to execute %s: %s; Piping to /dev/null\n", cmd, e);
free_env(env);
pipe_to_null:
while (read(0, buf, 32768) > 0) {
}
amfree(cmd);
}
static void
ambsdtar_index(
application_argument_t *argument G_GNUC_UNUSED)
{
char *cmd = NULL;
GPtrArray *argv_ptr = g_ptr_array_new();
int datain = 0;
int indexf;
int errf = 2;
pid_t tarpid;
FILE *indexstream;
char line[32768];
amwait_t wait_status;
char *errmsg = NULL;
if (!bsdtar_path) {
g_debug("BSDTAR-PATH not set");
fprintf(stderr,"BSDTAR-PATH not set");
while (read(0, line, 32768) > 0) {
}
exit(1);
}
cmd = g_strdup(bsdtar_path);
g_ptr_array_add(argv_ptr, g_strdup(bsdtar_path));
/* ignore trailing zero blocks on input (this was the default until tar-1.21) */
g_ptr_array_add(argv_ptr, g_strdup("-tf"));
g_ptr_array_add(argv_ptr, g_strdup("-"));
g_ptr_array_add(argv_ptr, NULL);
tarpid = pipespawnv(cmd, STDOUT_PIPE, 0,
&datain, &indexf, &errf, (char **)argv_ptr->pdata);
aclose(datain);
indexstream = fdopen(indexf, "r");
if (!indexstream) {
error(_("error indexstream(%d): %s\n"), indexf, strerror(errno));
}
fprintf(stdout, "/\n");
while (fgets(line, sizeof(line), indexstream) != NULL) {
if (strlen(line) > 0 && line[strlen(line)-1] == '\n') {
/* remove trailling \n */
line[strlen(line)-1] = '\0';
}
if (*line == '.' && *(line+1) == '/') { /* filename */
fprintf(stdout, "%s\n", &line[1]); /* remove . */
}
}
fclose(indexstream);
waitpid(tarpid, &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 (exit_value[WEXITSTATUS(wait_status)] == 1) {
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());
}
g_debug(_("ambsdtar: %s: pid %ld"), cmd, (long)tarpid);
if (errmsg) {
g_debug("%s", errmsg);
fprintf(stderr, "error [%s]\n", errmsg);
}
amfree(cmd);
}
static void
ambsdtar_build_exinclude(
dle_t *dle,
int *nb_exclude,
char **file_exclude,
int *nb_include,
char **file_include,
char *dirname,
messagelist_t *mlist)
{
int n_exclude = 0;
int n_include = 0;
char *exclude = NULL;
char *include = NULL;
if (dle->exclude_file) n_exclude += dle->exclude_file->nb_element;
if (dle->exclude_list) n_exclude += dle->exclude_list->nb_element;
if (dle->include_file) n_include += dle->include_file->nb_element;
if (dle->include_list) n_include += dle->include_list->nb_element;
if (n_exclude > 0) exclude = build_exclude(dle, mlist);
if (n_include > 0) include = build_include(dle, dirname, mlist);
if (nb_exclude)
*nb_exclude = n_exclude;
if (file_exclude)
*file_exclude = exclude;
else
amfree(exclude);
if (nb_include)
*nb_include = n_include;
if (file_include)
*file_include = include;
else
amfree(include);
}
static char *
ambsdtar_get_timestamps(
application_argument_t *argument,
int level,
FILE *mesgstream,
int command)
{
char *basename = NULL;
char *filename = NULL;
int infd;
char *inputname = NULL;
char *errmsg = NULL;
if (state_dir) {
char number[NUM_STR_SIZE];
int baselevel;
char *sdisk = sanitise_filename(argument->dle.disk);
FILE *tt;
char line[1024];
basename = g_strjoin(NULL, state_dir,
"/",
argument->host,
sdisk,
NULL);
amfree(sdisk);
snprintf(number, sizeof(number), "%d", level);
filename = g_strjoin(NULL, basename, "_", number, NULL);
/*
* Open the timestamps 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;
inputname = g_strdup("AA");
while (infd == -1 && inputname != NULL) {
amfree(inputname);
if (--baselevel >= 0) {
snprintf(number, sizeof(number), "%d", baselevel);
inputname = g_strconcat(basename, "_", number, NULL);
} else {
inputname = NULL;
amfree(basename);
amfree(filename);
g_debug("Using NULL timestamps");
return NULL;
}
if ((infd = open(inputname, O_RDONLY)) == -1) {
errmsg = g_strdup_printf(_("ambsdtar: error opening %s: %s"),
inputname, strerror(errno));
g_debug("%s", errmsg);
if (baselevel == 0) {
if (command == CMD_ESTIMATE) {
fprintf(mesgstream, "ERROR %s\n", errmsg);
} else {
fprintf(mesgstream, "sendbackup: error [%s]\n", errmsg);
}
exit(1);
}
amfree(errmsg);
}
}
tt = fdopen(infd, "r");
if (!tt) {
g_debug("Failed to fdopen '%s' to '%s'", inputname, strerror(errno));
close(infd);
infd = -1;
} else if (!fgets(line, 1024, tt)) {
errmsg = g_strdup_printf(_("ambsdtar: error reading '%s': %s"),
inputname, strerror(errno));
if (command == CMD_ESTIMATE) {
fprintf(mesgstream, "ERROR %s\n", errmsg);
} else {
fprintf(mesgstream, "sendbackup: error [%s]\n", errmsg);
}
fclose(tt);
exit(1);
} else {
fclose(tt);
tt = NULL;
infd = -1;
g_debug("Read timestamps '%s' to '%s'", line, filename);
}
amfree(basename);
amfree(filename);
amfree(inputname);
return g_strdup(line);
} else {
errmsg = _("STATE-DIR is not defined");
g_debug("%s", errmsg);
if (command == CMD_ESTIMATE) {
fprintf(mesgstream, "ERROR %s\n", errmsg);
} else {
fprintf(mesgstream, "sendbackup: error [%s]\n", errmsg);
}
exit(1);
}
return NULL;
}
static void
ambsdtar_set_timestamps(
application_argument_t *argument,
int level,
char *timestamps,
FILE *mesgstream)
{
char *basename = NULL;
char *filename = NULL;
char *errmsg = NULL;
if (state_dir) {
char number[NUM_STR_SIZE];
char *sdisk = sanitise_filename(argument->dle.disk);
FILE *tt;
basename = g_strjoin(NULL, state_dir,
"/",
argument->host,
sdisk,
NULL);
amfree(sdisk);
snprintf(number, sizeof(number), "%d", level);
filename = g_strjoin(NULL, basename, "_", number, NULL);
tt = fopen(filename, "w");
if (!tt) {
errmsg = g_strdup_printf("Failed to open '%s': %s",
filename, strerror(errno));
g_debug("%s", errmsg);
fprintf(mesgstream, "sendbackup: error [%s]\n", errmsg);
} else {
fprintf(tt, "%s", timestamps);
fclose(tt);
g_debug("Wrote timestamps '%s' to '%s'", timestamps, filename);
}
amfree(basename);
amfree(filename);
} else {
errmsg = _("STATE-DIR is not defined");
g_debug("%s", errmsg);
fprintf(mesgstream, "sendabckup: error [%s]\n", errmsg);
exit(1);
}
}
static GPtrArray *
ambsdtar_build_argv(
char *bsdtar_realpath,
application_argument_t *argument,
char *timestamps,
char **file_exclude,
char **file_include,
int command,
messagelist_t *mlist)
{
int nb_exclude = 0;
int nb_include = 0;
char *dirname;
char tmppath[PATH_MAX];
GPtrArray *argv_ptr = g_ptr_array_new();
GSList *copt;
if (bsdtar_target) {
dirname = bsdtar_target;
} else {
dirname = argument->dle.device;
}
ambsdtar_build_exinclude(&argument->dle,
&nb_exclude, file_exclude,
&nb_include, file_include, dirname, mlist);
g_ptr_array_add(argv_ptr, g_strdup(bsdtar_realpath));
g_ptr_array_add(argv_ptr, g_strdup("--create"));
g_ptr_array_add(argv_ptr, g_strdup("--file"));
if (command == CMD_ESTIMATE) {
g_ptr_array_add(argv_ptr, g_strdup("/dev/null"));
} else {
g_ptr_array_add(argv_ptr, g_strdup("-"));
}
g_ptr_array_add(argv_ptr, g_strdup("--directory"));
canonicalize_pathname(dirname, tmppath);
g_ptr_array_add(argv_ptr, g_strdup(tmppath));
if (timestamps) {
g_ptr_array_add(argv_ptr, g_strdup("--newer"));
g_ptr_array_add(argv_ptr, g_strdup(timestamps));
}
if (bsdtar_onefilesystem)
g_ptr_array_add(argv_ptr, g_strdup("--one-file-system"));
if (argument->tar_blocksize) {
g_ptr_array_add(argv_ptr, g_strdup("--block-size"));
g_ptr_array_add(argv_ptr, g_strdup(argument->tar_blocksize));
}
g_ptr_array_add(argv_ptr, g_strdup("--totals"));
for (copt = argument->command_options; copt != NULL; copt = copt->next) {
g_ptr_array_add(argv_ptr, g_strdup((char *)copt->data));
}
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);
return(argv_ptr);
}