/*
* 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: client_util.c,v 1.34 2006/05/25 01:47:11 johnfranks Exp $
*
*/
#include "amanda.h"
#include "conffile.h"
#include "client_util.h"
#include "getfsent.h"
#include "amutil.h"
#include "glib-util.h"
#include "timestamp.h"
#include "pipespawn.h"
#include "amxml.h"
#include "glob.h"
#include "clock.h"
#include "ammessage.h"
#include "amandates.h"
#include "security-file.h"
#define MAXMAXDUMPS 16
static int add_exclude(FILE *file_exclude, char *aexc, gboolean optional,
messagelist_t *mlist);
static int add_include(const char *disk, const char *device, FILE *file_include,
char *ainc, gboolean optional, messagelist_t *mlist);
static char *build_name(char *disk, char *exin, messagelist_t *mlist);
static char *get_name(char *diskname, char *exin, time_t t, int n);
char *
fixup_relative(
char * name,
char * device)
{
char *newname;
if(*name != '/') {
char *dirname = amname_to_dirname(device);
newname = g_strjoin(NULL, dirname, "/", name , NULL);
amfree(dirname);
}
else {
newname = g_strdup(name);
}
return newname;
}
/* GDestroyFunc for a hash table whose values are GSLists contianing malloc'd
* strings */
static void
destroy_slist_free_full(gpointer list) {
slist_free_full((GSList *)list, g_free);
}
static char *
get_name(
char * diskname,
char * exin,
time_t t,
int n)
{
char number[NUM_STR_SIZE];
char *filename;
char *ts;
ts = get_timestamp_from_time(t);
if(n == 0)
number[0] = '\0';
else
g_snprintf(number, sizeof(number), "%03d", n - 1);
filename = g_strjoin(NULL, get_pname(), ".", diskname, ".", ts, number, ".",
exin, NULL);
amfree(ts);
return filename;
}
static char *
build_name(
char *disk,
char *exin,
messagelist_t *mlist)
{
int n;
int fd;
char *filename = NULL;
char *afilename = NULL;
char *diskname;
time_t curtime;
char *dbgdir;
char *e = NULL;
DIR *d;
struct dirent *entry;
char *test_name;
size_t match_len, d_name_len;
time(&curtime);
diskname = sanitise_filename(disk);
dbgdir = g_strconcat(AMANDA_TMPDIR, "/", NULL);
if((d = opendir(AMANDA_TMPDIR)) == NULL) {
error(_("open debug directory \"%s\": %s"),
AMANDA_TMPDIR, strerror(errno));
/*NOTREACHED*/
}
test_name = get_name(diskname, exin,
curtime - (getconf_int(CNF_DEBUG_DAYS) * 24 * 60 * 60), 0);
match_len = strlen(get_pname()) + strlen(diskname) + 2;
while((entry = readdir(d)) != NULL) {
if(is_dot_or_dotdot(entry->d_name)) {
continue;
}
d_name_len = strlen(entry->d_name);
if(strncmp(test_name, entry->d_name, match_len) != 0
|| d_name_len < match_len + 14 + 8
|| !g_str_equal(entry->d_name + d_name_len - 7, exin)) {
continue; /* not one of our files */
}
if(strcmp(entry->d_name, test_name) < 0) {
g_free(e);
e = g_strconcat(dbgdir, entry->d_name, NULL);
(void) unlink(e); /* get rid of old file */
}
}
amfree(test_name);
amfree(e);
closedir(d);
n=0;
do {
filename = get_name(diskname, exin, curtime, n);
g_free(afilename);
afilename = g_strconcat(dbgdir, filename, NULL);
if((fd=open(afilename, O_WRONLY|O_CREAT|O_APPEND, 0600)) < 0){
amfree(afilename);
n++;
}
else {
close(fd);
}
amfree(filename);
} while(!afilename && n < 1000);
if(afilename == NULL) {
filename = get_name(diskname, exin, curtime, 0);
g_free(afilename);
afilename = g_strconcat(dbgdir, filename, NULL);
*mlist = g_slist_append(*mlist, build_message(
__FILE__, __LINE__, 4600004, MSG_ERROR, 2,
"filename", afilename,
errno , errno));
amfree(afilename);
amfree(filename);
}
amfree(dbgdir);
amfree(diskname);
return afilename;
}
static int
add_exclude(
FILE *file_exclude,
char *aexc,
gboolean optional,
messagelist_t *mlist)
{
size_t l;
char *quoted, *file;
(void)optional; /* Quiet unused parameter warning */
(void)mlist; /* Quiet unused parameter warning */
l = strlen(aexc);
if(aexc[l-1] == '\n') {
aexc[l-1] = '\0';
l--;
}
file = quoted = quote_string(aexc);
if (*file == '"') {
file[strlen(file) - 1] = '\0';
file++;
}
g_fprintf(file_exclude, "%s\n", file);
amfree(quoted);
return 1;
}
static int
add_include(
const char *disk,
const char *device,
FILE *file_include,
char *ainc,
int optional,
messagelist_t *mlist)
{
size_t l;
int nb_exp=0;
char *quoted, *file;
(void)disk; /* Quiet unused parameter warning */
(void)device; /* Quiet unused parameter warning */
l = strlen(ainc);
if(ainc[l-1] == '\n') {
ainc[l-1] = '\0';
l--;
}
if (strncmp(ainc, "./", 2) != 0) {
*mlist = g_slist_append(*mlist, build_message(
__FILE__, __LINE__, 4600005,
optional ? MSG_INFO : MSG_ERROR, 1,
"include", ainc));
}
else {
char *incname = ainc+2;
int set_root;
set_root = set_root_privs(1);
/* Take as is if not root && many '/' */
if(!set_root && strchr(incname, '/')) {
file = quoted = quote_string(ainc);
if (*file == '"') {
file[strlen(file) - 1] = '\0';
file++;
}
g_fprintf(file_include, "%s\n", file);
amfree(quoted);
nb_exp++;
}
else {
int nb;
glob_t globbuf;
char *cwd;
globbuf.gl_offs = 0;
cwd = g_get_current_dir();
if (chdir(device) != 0) {
error(_("Failed to chdir(%s): %s\n"), device, strerror(errno));
}
glob(incname, 0, NULL, &globbuf);
if (chdir(cwd) != 0) {
error(_("Failed to chdir(%s): %s\n"), cwd, strerror(errno));
}
if (set_root)
set_root_privs(0);
nb_exp = globbuf.gl_pathc;
for (nb=0; nb < nb_exp; nb++) {
file = g_strconcat("./", globbuf.gl_pathv[nb], NULL);
quoted = quote_string(file);
if (*file == '"') {
file[strlen(file) - 1] = '\0';
file++;
}
g_fprintf(file_include, "%s\n", file);
amfree(quoted);
amfree(file);
}
}
}
return nb_exp;
}
char *
build_exclude(
dle_t *dle,
messagelist_t *mlist)
{
char *filename;
FILE *file_exclude;
FILE *exclude;
char *aexc;
sle_t *excl;
int nb_exclude = 0;
if (dle->exclude_file) nb_exclude += dle->exclude_file->nb_element;
if (dle->exclude_list) nb_exclude += dle->exclude_list->nb_element;
if (nb_exclude == 0) return NULL;
if ((filename = build_name(dle->disk, "exclude", mlist)) != NULL) {
if ((file_exclude = fopen(filename,"w")) != NULL) {
if (dle->exclude_file) {
for(excl = dle->exclude_file->first; excl != NULL;
excl = excl->next) {
add_exclude(file_exclude, excl->name,
dle->exclude_optional, mlist);
}
}
if (dle->exclude_list) {
for(excl = dle->exclude_list->first; excl != NULL;
excl = excl->next) {
char *exclname = fixup_relative(excl->name, dle->device);
if((exclude = fopen(exclname, "r")) != NULL) {
while ((aexc = pgets(exclude)) != NULL) {
if (aexc[0] == '\0') {
amfree(aexc);
continue;
}
add_exclude(file_exclude, aexc,
dle->exclude_optional, mlist);
amfree(aexc);
}
fclose(exclude);
}
else {
*mlist = g_slist_append(*mlist, build_message(
__FILE__, __LINE__, 4600002,
dle->exclude_optional && errno == ENOENT ? MSG_INFO : MSG_ERROR,
2,
"exclude", exclname,
"errno" , errno));
}
amfree(exclname);
}
}
fclose(file_exclude);
} else {
*mlist = g_slist_append(*mlist, build_message(
__FILE__, __LINE__, 4600003, MSG_ERROR, 2,
"exclude", filename,
"errno" , errno));
}
}
return filename;
}
char *
build_include(
dle_t *dle,
char const *dirname,
messagelist_t *mlist)
{
char *filename;
FILE *file_include;
FILE *include;
char *ainc = NULL;
sle_t *incl;
int nb_include = 0;
int nb_exp = 0;
if (dle->include_file) nb_include += dle->include_file->nb_element;
if (dle->include_list) nb_include += dle->include_list->nb_element;
if (nb_include == 0) return NULL;
if ((filename = build_name(dle->disk, "include", mlist)) != NULL) {
if ((file_include = fopen(filename,"w")) != NULL) {
if (dle->include_file) {
for (incl = dle->include_file->first; incl != NULL;
incl = incl->next) {
nb_exp += add_include(dle->disk, dirname, file_include,
incl->name, dle->include_optional, mlist);
}
}
if (dle->include_list) {
for (incl = dle->include_list->first; incl != NULL;
incl = incl->next) {
char *inclname = fixup_relative(incl->name, dle->device);
if ((include = fopen(inclname, "r")) != NULL) {
while ((ainc = pgets(include)) != NULL) {
if (ainc[0] == '\0') {
amfree(ainc);
continue;
}
nb_exp += add_include(dle->disk, dirname,
file_include, ainc,
dle->include_optional, mlist);
amfree(ainc);
}
fclose(include);
}
else {
*mlist = g_slist_append(*mlist, build_message(
__FILE__, __LINE__, 4600006,
dle->include_optional && errno == ENOENT ? MSG_INFO : MSG_ERROR,
2,
"include", inclname,
"errno" , errno));
}
amfree(inclname);
}
}
fclose(file_include);
} else {
*mlist = g_slist_append(*mlist, build_message(
__FILE__, __LINE__, 4600007, MSG_ERROR, 2,
"include", filename,
"errno" , errno));
}
}
if (nb_exp == 0) {
*mlist = g_slist_append(*mlist, build_message(
__FILE__, __LINE__, 4600008, MSG_ERROR, 1,
"disk", dle->disk));
}
return filename;
}
void
parse_options(
char *str,
dle_t *dle,
am_feature_t *fs,
int verbose)
{
char *exc;
char *inc;
char *p, *tok;
char *quoted;
p = g_strdup(str);
tok = strtok(p,";");
while (tok != NULL) {
if(am_has_feature(fs, fe_options_auth)
&& BSTRNCMP(tok,"auth=") == 0) {
if (dle->auth != NULL) {
quoted = quote_string(tok + 5);
dbprintf(_("multiple auth option %s\n"), quoted);
if(verbose) {
g_printf(_("ERROR [multiple auth option %s]\n"), quoted);
}
amfree(quoted);
amfree(dle->auth);
}
dle->auth = g_strdup(&tok[5]);
}
else if(am_has_feature(fs, fe_options_bsd_auth)
&& BSTRNCMP(tok, "bsd-auth") == 0) {
if (dle->auth != NULL) {
dbprintf(_("multiple auth option\n"));
if (verbose) {
g_printf(_("ERROR [multiple auth option]\n"));
}
amfree(dle->auth);
}
dle->auth = g_strdup("bsd");
}
else if (BSTRNCMP(tok, "compress-fast") == 0) {
if (dle->compress != COMP_NONE) {
dbprintf(_("multiple compress option\n"));
if (verbose) {
g_printf(_("ERROR [multiple compress option]\n"));
}
}
dle->compress = COMP_FAST;
}
else if (BSTRNCMP(tok, "compress-best") == 0) {
if (dle->compress != COMP_NONE) {
dbprintf(_("multiple compress option\n"));
if (verbose) {
g_printf(_("ERROR [multiple compress option]\n"));
}
}
dle->compress = COMP_BEST;
}
else if (BSTRNCMP(tok, "srvcomp-fast") == 0) {
if (dle->compress != COMP_NONE) {
dbprintf(_("multiple compress option\n"));
if (verbose) {
g_printf(_("ERROR [multiple compress option]\n"));
}
}
dle->compress = COMP_SERVER_FAST;
}
else if (BSTRNCMP(tok, "srvcomp-best") == 0) {
if (dle->compress != COMP_NONE) {
dbprintf(_("multiple compress option\n"));
if (verbose) {
g_printf(_("ERROR [multiple compress option]\n"));
}
}
dle->compress = COMP_SERVER_BEST;
}
else if (BSTRNCMP(tok, "srvcomp-cust=") == 0) {
if (dle->compress != COMP_NONE) {
dbprintf(_("multiple compress option\n"));
if (verbose) {
g_printf(_("ERROR [multiple compress option]\n"));
}
}
amfree(dle->compprog);
dle->compprog = g_strdup(tok + sizeof("srvcomp-cust=") -1);
dle->compress = COMP_SERVER_CUST;
}
else if (BSTRNCMP(tok, "comp-cust=") == 0) {
if (dle->compress != COMP_NONE) {
dbprintf(_("multiple compress option\n"));
if (verbose) {
g_printf(_("ERROR [multiple compress option]\n"));
}
}
amfree(dle->compprog);
dle->compprog = g_strdup(tok + sizeof("comp-cust=") -1);
dle->compress = COMP_CUST;
/* parse encryption options */
}
else if (BSTRNCMP(tok, "encrypt-serv-cust=") == 0) {
if (dle->encrypt != ENCRYPT_NONE) {
dbprintf(_("multiple encrypt option\n"));
if (verbose) {
g_printf(_("ERROR [multiple encrypt option]\n"));
}
}
amfree(dle->srv_encrypt);
dle->srv_encrypt = g_strdup(tok + sizeof("encrypt-serv-cust=") -1);
dle->encrypt = ENCRYPT_SERV_CUST;
}
else if (BSTRNCMP(tok, "encrypt-cust=") == 0) {
if (dle->encrypt != ENCRYPT_NONE) {
dbprintf(_("multiple encrypt option\n"));
if (verbose) {
g_printf(_("ERROR [multiple encrypt option]\n"));
}
}
amfree(dle->clnt_encrypt);
dle->clnt_encrypt= g_strdup(tok + sizeof("encrypt-cust=") -1);
dle->encrypt = ENCRYPT_CUST;
}
else if (BSTRNCMP(tok, "server-decrypt-option=") == 0) {
amfree(dle->srv_decrypt_opt);
dle->srv_decrypt_opt = g_strdup(tok + sizeof("server-decrypt-option=") -1);
}
else if (BSTRNCMP(tok, "client-decrypt-option=") == 0) {
amfree(dle->clnt_decrypt_opt);
dle->clnt_decrypt_opt = g_strdup(tok + sizeof("client-decrypt-option=") -1);
}
else if (BSTRNCMP(tok, "no-record") == 0) {
if (dle->record != 1) {
dbprintf(_("multiple no-record option\n"));
if (verbose) {
g_printf(_("ERROR [multiple no-record option]\n"));
}
}
dle->record = 0;
}
else if (BSTRNCMP(tok, "index") == 0) {
if (dle->create_index != 0) {
dbprintf(_("multiple index option\n"));
if (verbose) {
g_printf(_("ERROR [multiple index option]\n"));
}
}
dle->create_index = 1;
}
else if (BSTRNCMP(tok, "exclude-optional") == 0) {
if (dle->exclude_optional != 0) {
dbprintf(_("multiple exclude-optional option\n"));
if (verbose) {
g_printf(_("ERROR [multiple exclude-optional option]\n"));
}
}
dle->exclude_optional = 1;
}
else if (g_str_equal(tok, "include-optional")) {
if (dle->include_optional != 0) {
dbprintf(_("multiple include-optional option\n"));
if (verbose) {
g_printf(_("ERROR [multiple include-optional option]\n"));
}
}
dle->include_optional = 1;
}
else if (BSTRNCMP(tok,"exclude-file=") == 0) {
exc = unquote_string(&tok[13]);
dle->exclude_file = append_sl(dle->exclude_file, exc);
amfree(exc);
}
else if (BSTRNCMP(tok,"exclude-list=") == 0) {
exc = unquote_string(&tok[13]);
dle->exclude_list = append_sl(dle->exclude_list, exc);
amfree(exc);
}
else if (BSTRNCMP(tok,"include-file=") == 0) {
inc = unquote_string(&tok[13]);
dle->include_file = append_sl(dle->include_file, inc);
amfree(inc);
}
else if (BSTRNCMP(tok,"include-list=") == 0) {
inc = unquote_string(&tok[13]);
dle->include_list = append_sl(dle->include_list, inc);
amfree(inc);
}
else if (BSTRNCMP(tok,"kencrypt") == 0) {
dle->kencrypt = 1;
}
else if (!g_str_equal(tok, "|")) {
quoted = quote_string(tok);
dbprintf(_("unknown option %s\n"), quoted);
if (verbose) {
g_printf(_("ERROR [unknown option: %s]\n"), quoted);
}
amfree(quoted);
}
tok = strtok(NULL, ";");
}
amfree(p);
}
void
application_property_add_to_argv(
GPtrArray *argv_ptr,
dle_t *dle,
backup_support_option_t *bsu,
am_feature_t *amfeatures)
{
sle_t *incl, *excl;
if (bsu) {
if (bsu->include_file && dle->include_file) {
for (incl = dle->include_file->first; incl != NULL;
incl = incl->next) {
g_ptr_array_add(argv_ptr, g_strdup("--include-file"));
g_ptr_array_add(argv_ptr, g_strdup(incl->name));
}
}
if (bsu->include_list && dle->include_list) {
for (incl = dle->include_list->first; incl != NULL;
incl = incl->next) {
g_ptr_array_add(argv_ptr, g_strdup("--include-list"));
g_ptr_array_add(argv_ptr, g_strdup(incl->name));
}
}
if (bsu->include_optional && dle->include_optional) {
g_ptr_array_add(argv_ptr, g_strdup("--include-optional"));
g_ptr_array_add(argv_ptr, g_strdup("yes"));
}
if (bsu->exclude_file && dle->exclude_file) {
for (excl = dle->exclude_file->first; excl != NULL;
excl = excl->next) {
g_ptr_array_add(argv_ptr, g_strdup("--exclude-file"));
g_ptr_array_add(argv_ptr, g_strdup(excl->name));
}
}
if (bsu->exclude_list && dle->exclude_list) {
for (excl = dle->exclude_list->first; excl != NULL;
excl = excl->next) {
g_ptr_array_add(argv_ptr, g_strdup("--exclude-list"));
g_ptr_array_add(argv_ptr, g_strdup(excl->name));
}
}
if (bsu->exclude_optional && dle->exclude_optional) {
g_ptr_array_add(argv_ptr, g_strdup("--exclude-optional"));
g_ptr_array_add(argv_ptr, g_strdup("yes"));
}
if (bsu->features && amfeatures) {
char *feature_string = am_feature_to_string(amfeatures);
g_ptr_array_add(argv_ptr, g_strdup("--amfeatures"));
g_ptr_array_add(argv_ptr, feature_string);
}
if (dle->data_path == DATA_PATH_DIRECTTCP &&
bsu->data_path_set & DATA_PATH_DIRECTTCP) {
GSList *directtcp;
g_ptr_array_add(argv_ptr, g_strdup("--data-path"));
g_ptr_array_add(argv_ptr, g_strdup("directtcp"));
for (directtcp = dle->directtcp_list; directtcp != NULL;
directtcp = directtcp->next) {
g_ptr_array_add(argv_ptr, g_strdup("--direct-tcp"));
g_ptr_array_add(argv_ptr, g_strdup(directtcp->data));
}
}
}
property_add_to_argv(argv_ptr, dle->application_property);
return;
}
typedef struct {
dle_t *dle;
char *name;
proplist_t dle_proplist;
int verbose;
int good;
} merge_property_t;
static void
merge_property(
gpointer key_p,
gpointer value_p,
gpointer user_data_p)
{
char *property_s = key_p;
property_t *conf_property = value_p;
merge_property_t *merge_p = user_data_p;
property_t *dle_property = g_hash_table_lookup(merge_p->dle_proplist,
property_s);
GSList *value;
char *qdisk = quote_string(merge_p->dle->disk);
if (dle_property) {
if (dle_property->priority && conf_property->priority) {
if (merge_p->verbose) {
g_fprintf(stdout,
_("ERROR %s (%s) Both server client have priority for property '%s'.\n"),
qdisk, merge_p->name, property_s);
}
g_debug("ERROR %s (%s) Both server client have priority for property '%s'.", qdisk, merge_p->name, property_s);
merge_p->good = 0;
/* Use client property */
g_hash_table_remove(merge_p->dle_proplist, key_p);
g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
} else if (dle_property->priority) {
if (merge_p->verbose) {
g_fprintf(stdout,
_("ERROR %s (%s) Server set priority for property '%s' but client set the property.\n"),
qdisk, merge_p->name, property_s);
}
g_debug("%s (%s) Server set priority for property '%s' but client set the property.", qdisk, merge_p->name, property_s);
/* use server property */
} else if (conf_property->priority) {
if (merge_p->verbose) {
g_fprintf(stdout,
_("ERROR %s (%s) Client set priority for property '%s' but server set the property.\n"),
qdisk, merge_p->name, property_s);
}
g_debug("%s (%s) Client set priority for property '%s' but server set the property.", qdisk, merge_p->name, property_s);
/* Use client property */
g_hash_table_remove(merge_p->dle_proplist, key_p);
g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
} else if (!conf_property->append) {
if (merge_p->verbose) {
g_fprintf(stdout,
_("ERROR %s (%s) Both server and client set property '%s', using client value.\n"),
qdisk, merge_p->name, property_s);
}
g_debug("%s (%s) Both server and client set property '%s', using client value.", qdisk, merge_p->name, property_s);
/* Use client property */
g_hash_table_remove(merge_p->dle_proplist, key_p);
g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
} else { /* merge */
for (value = conf_property->values; value != NULL;
value = value->next) {
dle_property->values = g_slist_append(dle_property->values,
value->data);
}
}
} else { /* take value from conf */
g_hash_table_insert(merge_p->dle_proplist, key_p, conf_property);
}
amfree(qdisk);
}
int
merge_properties(
dle_t *dle,
char *name,
proplist_t dle_proplist,
proplist_t conf_proplist,
int verbose)
{
merge_property_t merge_p = {dle, name, dle_proplist, verbose, 1};
if (conf_proplist != NULL) {
g_hash_table_foreach(conf_proplist,
&merge_property,
&merge_p);
}
return merge_p.good;
}
int
merge_dles_properties(
dle_t *dles,
int verbose)
{
dle_t *dle;
application_t *app;
GSList *scriptlist;
pp_script_t *pp_script;
int good = 1;
for (dle=dles; dle != NULL; dle=dle->next) {
if (dle->program_is_application_api) {
app = NULL;
if (dle->application_client_name &&
strlen(dle->application_client_name) > 0) {
app = lookup_application(dle->application_client_name);
if (!app) {
char *qamname = quote_string(dle->disk);
char *errmsg = g_strdup_printf("Application '%s' not found on client",
dle->application_client_name);
char *qerrmsg = quote_string(errmsg);
good = 0;
if (verbose) {
g_fprintf(stdout, _("ERROR %s %s\n"), qamname, qerrmsg);
}
g_debug("%s: %s", qamname, qerrmsg);
amfree(qamname);
amfree(errmsg);
amfree(qerrmsg);
}
} else {
app = lookup_application(dle->program);
}
if (app) {
merge_properties(dle, dle->program,
dle->application_property,
application_get_property(app),
verbose);
}
}
for (scriptlist = dle->scriptlist; scriptlist != NULL;
scriptlist = scriptlist->next) {
script_t *script = scriptlist->data;
pp_script = NULL;
if (script->client_name && strlen(script->client_name) > 0) {
pp_script = lookup_pp_script(script->client_name);
if (!pp_script) {
char *qamname = quote_string(dle->disk);
char *errmsg = g_strdup_printf("Script '%s' not found on client",
script->client_name);
char *qerrmsg = quote_string(errmsg);
good = 0;
if (verbose) {
g_fprintf(stderr, _("ERROR %s %s\n"), qamname, qerrmsg);
}
g_debug("%s: %s", qamname, qerrmsg);
amfree(qamname);
amfree(errmsg);
amfree(qerrmsg);
}
} else {
pp_script = lookup_pp_script(script->plugin);
}
if (pp_script) {
merge_properties(dle, script->plugin,
script->property,
pp_script_get_property(pp_script),
verbose);
}
}
}
return good;
}
void
run_client_script(
script_t *script,
execute_on_t execute_on,
g_option_t *g_options,
dle_t *dle)
{
pid_t scriptpid;
int scriptin, scriptout, scripterr;
char *cmd;
GPtrArray *argv_ptr = g_ptr_array_new();
FILE *streamout;
FILE *streamerr;
char *line;
amwait_t wait_status;
char *command = NULL;
backup_support_option_t *bsu;
GPtrArray *errarray = NULL;
if ((script->execute_on & execute_on) == 0)
return;
if (script->execute_where != ES_CLIENT)
return;
switch (execute_on) {
case EXECUTE_ON_PRE_DLE_AMCHECK:
command = "PRE-DLE-AMCHECK";
break;
case EXECUTE_ON_PRE_HOST_AMCHECK:
command = "PRE-HOST-AMCHECK";
break;
case EXECUTE_ON_POST_DLE_AMCHECK:
command = "POST-DLE-AMCHECK";
break;
case EXECUTE_ON_POST_HOST_AMCHECK:
command = "POST-HOST-AMCHECK";
break;
case EXECUTE_ON_PRE_DLE_ESTIMATE:
command = "PRE-DLE-ESTIMATE";
break;
case EXECUTE_ON_PRE_HOST_ESTIMATE:
command = "PRE-HOST-ESTIMATE";
break;
case EXECUTE_ON_POST_DLE_ESTIMATE:
command = "POST-DLE-ESTIMATE";
break;
case EXECUTE_ON_POST_HOST_ESTIMATE:
command = "POST-HOST-ESTIMATE";
break;
case EXECUTE_ON_PRE_DLE_BACKUP:
command = "PRE-DLE-BACKUP";
break;
case EXECUTE_ON_PRE_HOST_BACKUP:
command = "PRE-HOST-BACKUP";
break;
case EXECUTE_ON_POST_DLE_BACKUP:
command = "POST-DLE-BACKUP";
break;
case EXECUTE_ON_POST_HOST_BACKUP:
command = "POST-HOST-BACKUP";
break;
case EXECUTE_ON_PRE_RECOVER:
command = "PRE-RECOVER";
break;
case EXECUTE_ON_POST_RECOVER:
command = "POST-RECOVER";
break;
case EXECUTE_ON_PRE_LEVEL_RECOVER:
command = "PRE-LEVEL-RECOVER";
break;
case EXECUTE_ON_POST_LEVEL_RECOVER:
command = "POST-LEVEL-RECOVER";
break;
case EXECUTE_ON_INTER_LEVEL_RECOVER:
command = "INTER-LEVEL-RECOVER";
break;
default:
{
char *msg = g_strdup_printf("ERROR %s: Bad EXECUTE-ON property",
script->plugin);
g_ptr_array_add(script->result->output, msg);
return;
break;
}
}
script->result = g_new0(client_script_result_t, 1);
script->result->proplist =
g_hash_table_new_full(g_str_hash, g_str_equal,
&g_free, &destroy_slist_free_full);
script->result->output = g_ptr_array_new();
script->result->err = g_ptr_array_new();
bsu = backup_support_option(script->plugin, &errarray);
if (!bsu) {
guint i;
for (i=0; i < errarray->len; i++) {
char *line = g_ptr_array_index(errarray, i);
char *errmsg = g_strdup_printf(_("Script '%s' command 'support': %s"), script->plugin, line);
g_ptr_array_add(script->result->err, errmsg);
}
if (i == 0) { /* nothing in errarray */
char *errmsg;
errmsg = g_strdup_printf(
_("Script '%s': cannot execute support command"),
script->plugin);
g_ptr_array_add(script->result->err, errmsg);
}
script->result->exit_status = 1;
g_ptr_array_free_full(errarray);
return;
}
cmd = g_strjoin(NULL, APPLICATION_DIR, "/", script->plugin, NULL);
g_ptr_array_add(argv_ptr, g_strdup(script->plugin));
g_ptr_array_add(argv_ptr, g_strdup(command));
if (bsu->execute_where) {
g_ptr_array_add(argv_ptr, g_strdup("--execute-where"));
g_ptr_array_add(argv_ptr, g_strdup("client"));
}
if (g_options->config && bsu->config) {
g_ptr_array_add(argv_ptr, g_strdup("--config"));
g_ptr_array_add(argv_ptr, g_strdup(g_options->config));
}
if (g_options->hostname && bsu->host) {
g_ptr_array_add(argv_ptr, g_strdup("--host"));
g_ptr_array_add(argv_ptr, g_strdup(g_options->hostname));
}
if (dle->disk && bsu->disk) {
g_ptr_array_add(argv_ptr, g_strdup("--disk"));
g_ptr_array_add(argv_ptr, g_strdup(dle->disk));
}
if (dle->device) {
g_ptr_array_add(argv_ptr, g_strdup("--device"));
g_ptr_array_add(argv_ptr, g_strdup(dle->device));
}
if (g_options->timestamp && bsu->timestamp) {
g_ptr_array_add(argv_ptr, g_strdup("--timestamp"));
g_ptr_array_add(argv_ptr, g_strdup(g_options->timestamp));
}
if (dle->levellist) {
levellist_t levellist;
char number[NUM_STR_SIZE];
for (levellist=dle->levellist; levellist; levellist=levellist->next) {
am_level_t *alevel = (am_level_t *)levellist->data;
g_ptr_array_add(argv_ptr, g_strdup("--level"));
g_snprintf(number, sizeof(number), "%d", alevel->level);
g_ptr_array_add(argv_ptr, g_strdup(number));
}
}
property_add_to_argv(argv_ptr, script->property);
g_ptr_array_add(argv_ptr, NULL);
scriptpid = pipespawnv(cmd, STDIN_PIPE|STDOUT_PIPE|STDERR_PIPE, 0,
&scriptin, &scriptout, &scripterr,
(char **)argv_ptr->pdata);
close(scriptin);
streamout = fdopen(scriptout, "r");
if (streamout) {
while((line = pgets(streamout)) != NULL) {
dbprintf("script: %s\n", line);
if (BSTRNCMP(line, "PROPERTY ") == 0) {
char *property_name, *property_value;
property_name = line + 9;
property_value = strchr(property_name,' ');
if (property_value == NULL) {
char *msg = g_strdup_printf(
"ERROR %s: Bad output property: %s",
script->plugin, line);
g_ptr_array_add(script->result->output, msg);
} else {
property_t *property;
*property_value++ = '\0';
property_value = g_strdup(property_value);
property = g_hash_table_lookup(script->result->proplist,
property_name);
if (!property) {
property_name = g_strdup(property_name);
property = g_new0(property_t, 1);
g_hash_table_insert(script->result->proplist,
property_name, property);
}
property->values = g_slist_append(property->values,
property_value);
}
amfree(line);
} else {
g_ptr_array_add(script->result->output, line);
}
}
fclose(streamout);
}
streamerr = fdopen(scripterr, "r");
if (streamerr) {
while((line = pgets(streamerr)) != NULL) {
g_ptr_array_add(script->result->err,
g_strdup_printf(_("Script '%s' command '%s': %s"),
script->plugin, command, line));
amfree(line);
}
fclose(streamerr);
}
waitpid(scriptpid, &wait_status, 0);
if (WIFSIGNALED(wait_status)) {
g_ptr_array_add(script->result->err,
g_strdup_printf(_("Script '%s' command '%s' terminated with signal %d: see %s"),
script->plugin, command,
WTERMSIG(wait_status),
dbfn()));
script->result->exit_status = 1;
} else if (WIFEXITED(wait_status)) {
if (WEXITSTATUS(wait_status) != 0) {
g_ptr_array_add(script->result->err,
g_strdup_printf(_("Script '%s' command '%s' exited with status %d: see %s"),
script->plugin, command,
WEXITSTATUS(wait_status),
dbfn()));
script->result->exit_status = WEXITSTATUS(wait_status);
} else {
/* Normal exit */
}
}
amfree(cmd);
g_ptr_array_free_full(argv_ptr);
g_free(bsu);
}
void run_client_script_output(gpointer data, gpointer user_data);
void run_client_script_output_estimate(gpointer data, gpointer user_data);
void run_client_script_output_host_estimate(gpointer data, gpointer user_data);
void run_client_script_output_backup(gpointer data, gpointer user_data);
void run_client_script_err_amcheck(gpointer data, gpointer user_data);
void run_client_script_err_host_estimate(gpointer data, gpointer user_data);
void run_client_script_err_estimate(gpointer data, gpointer user_data);
void run_client_script_err_backup(gpointer data, gpointer user_data);
void run_client_script_err_recover(gpointer data, gpointer user_data);
typedef struct script_output_s {
FILE *stream;
message_t *(*fprint_message)(FILE *out, message_t *message);
dle_t *dle;
int exit_status;
} script_output_t;
void
run_client_script_output(
gpointer data,
gpointer user_data)
{
char *line = data;
script_output_t *so = user_data;
if (line && so->stream) {
if (so->fprint_message) {
delete_message(so->fprint_message(so->stream, build_message(
__FILE__, __LINE__, 4600000, MSG_ERROR, 1,
"errmsg", line)));
} else {
g_fprintf(so->stream, "%s\n", line);
}
}
}
void
run_client_script_output_backup(
gpointer data,
gpointer user_data)
{
char *line = data;
script_output_t *so = user_data;
if (line && so->stream) {
g_fprintf(so->stream, "| %s\n", line);
}
}
void
run_client_script_err_amcheck(
gpointer data,
gpointer user_data)
{
char *line = data;
script_output_t *so = user_data;
if (line && so->stream) {
if (so->fprint_message) {
delete_message(so->fprint_message(so->stream, build_message(
__FILE__, __LINE__, 4600001, MSG_ERROR, 1,
"errmsg", line)));
} else {
g_fprintf(so->stream, "ERROR %s\n", line);
}
}
}
void
run_client_script_output_estimate(
gpointer data,
gpointer user_data)
{
char *line = data;
script_output_t *so = user_data;
if (line && so->stream) {
char *qdisk = quote_string(so->dle->disk);
g_fprintf(so->stream, "%s 0 WARNING \"%s\"\n", qdisk, line);
amfree(qdisk);
}
}
void
run_client_script_output_host_estimate(
gpointer data,
gpointer user_data)
{
char *line = data;
script_output_t *so = user_data;
if (line && so->stream) {
char *qdisk = quote_string(so->dle->disk);
g_fprintf(so->stream, "WARNING \"%s\"\n", line);
amfree(qdisk);
}
}
void
run_client_script_err_estimate(
gpointer data,
gpointer user_data)
{
char *line = data;
script_output_t *so = user_data;
if (line && so->stream) {
char *qdisk = quote_string(so->dle->disk);
g_fprintf(so->stream, "%s 0 ERROR \"%s\"\n", qdisk, line);
amfree(qdisk);
}
}
void
run_client_script_err_host_estimate(
gpointer data,
gpointer user_data)
{
char *line = data;
script_output_t *so = user_data;
if (line && so->stream) {
char *qdisk = quote_string(so->dle->disk);
g_fprintf(so->stream, "ERROR \"%s\"\n", line);
amfree(qdisk);
}
}
void
run_client_script_err_backup(
gpointer data,
gpointer user_data)
{
char *line = data;
script_output_t *so = user_data;
if (line && so->stream) {
if (so->exit_status == 0) {
g_fprintf(so->stream, "? %s\n", line);
} else {
g_fprintf(so->stream, "sendbackup: error [%s]\n", line);
}
}
}
void
run_client_script_err_recover(
gpointer data,
gpointer user_data)
{
char *line = data;
script_output_t *so = user_data;
if (line && so->stream) {
g_fprintf(so->stream, "%s\n", line);
}
}
int
run_client_scripts(
execute_on_t execute_on,
g_option_t *g_options,
dle_t *dle,
FILE *streamout,
message_t *(*fprint_message)(FILE *out, message_t *message))
{
GSList *scriptlist;
script_t *script;
GFunc client_script_err = NULL;
GFunc client_script_out = NULL;
script_output_t so = { streamout, fprint_message, dle, 0 };
int exit_status = 0;
for (scriptlist = dle->scriptlist; scriptlist != NULL;
scriptlist = scriptlist->next) {
script = (script_t *)scriptlist->data;
run_client_script(script, execute_on, g_options, dle);
if (script->result) {
switch (execute_on) {
case EXECUTE_ON_PRE_DLE_AMCHECK:
case EXECUTE_ON_PRE_HOST_AMCHECK:
case EXECUTE_ON_POST_DLE_AMCHECK:
case EXECUTE_ON_POST_HOST_AMCHECK:
client_script_out = run_client_script_output;
client_script_err = run_client_script_err_amcheck;
break;
case EXECUTE_ON_PRE_DLE_ESTIMATE:
case EXECUTE_ON_POST_DLE_ESTIMATE:
client_script_out = run_client_script_output_estimate;
if (am_has_feature(g_options->features,
fe_sendsize_rep_warning)) {
client_script_err = run_client_script_err_estimate;
}
break;
case EXECUTE_ON_PRE_HOST_ESTIMATE:
case EXECUTE_ON_POST_HOST_ESTIMATE:
client_script_out = run_client_script_output_host_estimate;
if (am_has_feature(g_options->features,
fe_sendsize_rep_warning)) {
client_script_err = run_client_script_err_host_estimate;
}
break;
case EXECUTE_ON_PRE_DLE_BACKUP:
case EXECUTE_ON_PRE_HOST_BACKUP:
case EXECUTE_ON_POST_DLE_BACKUP:
case EXECUTE_ON_POST_HOST_BACKUP:
client_script_out = run_client_script_output_backup;
client_script_err = run_client_script_err_backup;
break;
case EXECUTE_ON_PRE_RECOVER:
case EXECUTE_ON_POST_RECOVER:
case EXECUTE_ON_PRE_LEVEL_RECOVER:
case EXECUTE_ON_POST_LEVEL_RECOVER:
case EXECUTE_ON_INTER_LEVEL_RECOVER:
client_script_out = run_client_script_output;
client_script_err = run_client_script_err_recover;
}
so.exit_status = script->result->exit_status;
exit_status |= script->result->exit_status;
if (script->result->output) {
if (client_script_out) {
g_ptr_array_foreach(script->result->output,
client_script_out,
&so);
}
g_ptr_array_free(script->result->output, TRUE);
script->result->output = NULL;
}
if (script->result->err) {
if (client_script_err != NULL) {
g_ptr_array_foreach(script->result->err,
client_script_err,
&so);
}
g_ptr_array_free(script->result->err, TRUE);
script->result->err = NULL;
}
}
}
return exit_status;
}
void
run_calcsize(
char *config,
char *program,
char *disk,
char *dirname,
GSList *levels,
char *file_exclude,
char *file_include)
{
char *cmd, *cmdline;
char *command;
GPtrArray *argv_ptr = g_ptr_array_new();
char tmppath[PATH_MAX];
char number[NUM_STR_SIZE];
GSList *alevel;
guint level;
guint i;
char *match_expr;
int pipefd = -1, nullfd = -1;
pid_t calcpid;
times_t start_time;
FILE *dumpout = NULL;
int dumpsince;
char *errmsg = NULL;
char *line = NULL;
amwait_t wait_status;
int len;
char *qdisk;
amandates_t *amdp;
char *amandates_file;
gchar **args;
qdisk = quote_string(disk);
amandates_file = getconf_str(CNF_AMANDATES);
if(!start_amandates(amandates_file, 0)) {
char *errstr = strerror(errno);
char *errmsg = g_strdup_printf(_("could not open %s: %s"), amandates_file, errstr);
char *qerrmsg = quote_string(errmsg);
g_printf(_("ERROR %s\n"), qerrmsg);
amfree(qdisk);
amfree(errmsg);
amfree(qerrmsg);
return;
}
startclock();
cmd = g_strjoin(NULL, amlibexecdir, "/", "calcsize", NULL);
g_ptr_array_add(argv_ptr, g_strdup("calcsize"));
if (config)
g_ptr_array_add(argv_ptr, g_strdup(config));
else
g_ptr_array_add(argv_ptr, g_strdup("NOCONFIG"));
g_ptr_array_add(argv_ptr, g_strdup(program));
canonicalize_pathname(disk, tmppath);
g_ptr_array_add(argv_ptr, g_strdup(tmppath));
canonicalize_pathname(dirname, tmppath);
g_ptr_array_add(argv_ptr, g_strdup(tmppath));
if (file_exclude) {
g_ptr_array_add(argv_ptr, g_strdup("-X"));
g_ptr_array_add(argv_ptr, g_strdup(file_exclude));
}
if (file_include) {
g_ptr_array_add(argv_ptr, g_strdup("-I"));
g_ptr_array_add(argv_ptr, g_strdup(file_include));
}
for (alevel = levels; alevel != NULL; alevel = alevel->next) {
amdp = amandates_lookup(disk);
level = GPOINTER_TO_INT(alevel->data);
dbprintf("level: %d\n", level);
dumpsince = 0;
for (i=0; i < level; i++) {
if (dumpsince < amdp->dates[i])
dumpsince = amdp->dates[i];
}
g_snprintf(number, sizeof(number), "%d", level);
g_ptr_array_add(argv_ptr, g_strdup(number));
g_snprintf(number, sizeof(number), "%d", dumpsince);
g_ptr_array_add(argv_ptr, g_strdup(number));
}
g_ptr_array_add(argv_ptr, NULL);
args = (gchar **) g_ptr_array_free(argv_ptr, FALSE);
command = args[0];
cmdline = g_strjoinv(" ", args);
dbprintf(_("running: \"%s\"\n"), cmdline);
amfree(cmdline);
start_time = curclock();
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);
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(qdisk);
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 qdisk because it can have a '%'. */
if (g_str_has_prefix(line, qdisk) &&
sscanf(line+len, match_expr, &level, &size_) == 2) {
g_printf("%d %lld %d\n", level, size_, 1); /* write to sendsize */
dbprintf(_("estimate size for %s level %d: %lld KB\n"),
qdisk, level, size_);
}
}
fclose(dumpout);
amfree(match_expr);
dbprintf(_("waiting for %s %s child (pid=%d)\n"),
command, qdisk, (int)calcpid);
waitpid(calcpid, &wait_status, 0);
close(nullfd);
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, qdisk,
(int)calcpid, WEXITSTATUS(wait_status));
dbprintf(_(".....\n"));
dbprintf(_("estimate time for %s: %s\n"),
qdisk,
walltime_str(timessub(curclock(), start_time)));
common_exit:
if (errmsg && errmsg[0] != '\0') {
char *qerrmsg = quote_string(errmsg);
dbprintf(_("errmsg is %s\n"), errmsg);
g_printf("ERROR %s\n", qerrmsg);
amfree(qerrmsg);
}
amfree(qdisk);
amfree(errmsg);
g_strfreev(args);
amfree(cmd);
}
gboolean
check_access(
char * filename,
int mode)
{
char *noun, *adjective;
char *quoted = quote_string(filename);
gboolean result;
if(mode == F_OK)
noun = "find", adjective = "exists";
else if((mode & X_OK) == X_OK)
noun = "execute", adjective = "executable";
else if((mode & (W_OK|R_OK)) == (W_OK|R_OK))
noun = "read/write", adjective = "read/writable";
else
noun = "access", adjective = "accessible";
if(EUIDACCESS(filename, mode) == -1) {
g_printf(_("ERROR can not %s %s: %s (ruid:%d euid:%d)\n"), noun, quoted, strerror(errno),
(int)getuid(), (int)geteuid());
result = FALSE;
} else {
g_printf(_("OK %s %s (ruid:%d euid:%d)\n"), quoted, adjective,
(int)getuid(), (int)geteuid());
result = TRUE;
}
amfree(quoted);
return result;
}
gboolean
check_file(
char * filename,
int mode)
{
struct stat stat_buf;
char *quoted;
if(!stat(filename, &stat_buf)) {
if(!S_ISREG(stat_buf.st_mode)) {
quoted = quote_string(filename);
g_printf(_("ERROR [%s is not a file]\n"), quoted);
amfree(quoted);
return FALSE;
}
} else {
int save_errno = errno;
quoted = quote_string(filename);
g_printf(_("ERROR [can not stat %s: %s]\n"), quoted,
strerror(save_errno));
amfree(quoted);
return FALSE;
}
return check_access(filename, mode);
}
gboolean
check_dir(
char * dirname,
int mode)
{
struct stat stat_buf;
char *quoted;
char *dir;
gboolean result;
if(!stat(dirname, &stat_buf)) {
if(!S_ISDIR(stat_buf.st_mode)) {
quoted = quote_string(dirname);
g_printf(_("ERROR [%s is not a directory]\n"), quoted);
amfree(quoted);
return FALSE;
}
} else {
int save_errno = errno;
quoted = quote_string(dirname);
g_printf(_("ERROR [can not stat %s: %s]\n"), quoted,
strerror(save_errno));
amfree(quoted);
return FALSE;
}
dir = g_strconcat(dirname, "/.", NULL);
result = check_access(dir, mode);
amfree(dir);
return result;
}
gboolean
check_suid(
char * filename)
{
#ifndef SINGLE_USERID
struct stat stat_buf;
char *quoted = quote_string(filename);
if(!stat(filename, &stat_buf)) {
if(stat_buf.st_uid != 0 ) {
g_printf(_("ERROR [%s is not owned by root]\n"), quoted);
amfree(quoted);
return FALSE;
}
if((stat_buf.st_mode & S_ISUID) != S_ISUID) {
g_printf(_("ERROR [%s is not SUID root]\n"), quoted);
amfree(quoted);
return FALSE;
}
}
else {
g_printf(_("ERROR [can not stat %s: %s]\n"), quoted, strerror(errno));
amfree(quoted);
return FALSE;
}
amfree(quoted);
#else
(void)filename; /* Quiet unused parameter warning */
#endif
return TRUE;
}
#ifndef SINGLE_USERID
static message_t *check_exec_for_suid_message_recursive(char *filename);
#endif
message_t *
check_exec_for_suid_message(
char *type,
char *filename,
char **my_realpath)
{
#ifndef SINGLE_USERID
message_t *message;
char tmp_realpath[PATH_MAX];
*my_realpath = realpath(filename, tmp_realpath);
if (!*my_realpath) {
return build_message(
AMANDA_FILE, __LINE__, 3600091, MSG_ERROR, 2,
"filename", filename,
"errno", errno);
}
*my_realpath = g_strdup(tmp_realpath);
if ((message = security_allow_program_as_root(type, *my_realpath))) {
return message;
}
return check_exec_for_suid_message_recursive(filename);
#else
(void)type;
*my_realpath = g_strdup(filename);
return NULL;
#endif
}
#ifndef SINGLE_USERID
static message_t *
check_exec_for_suid_message_recursive(
char *filename)
{
struct stat stat_buf;
if (!stat(filename, &stat_buf)) {
char *copy_filename;
char *s;
if (stat_buf.st_uid != 0 ) {
return build_message(
AMANDA_FILE, __LINE__, 3600088, MSG_ERROR, 1,
"filename", filename);
}
if (stat_buf.st_mode & S_IWOTH) {
return build_message(
AMANDA_FILE, __LINE__, 3600089, MSG_ERROR, 1,
"filename", filename);
}
if (stat_buf.st_mode & S_IWGRP) {
return build_message(
AMANDA_FILE, __LINE__, 3600090, MSG_ERROR, 1,
"filename", filename);
}
copy_filename = g_strdup(filename);
if ((s = strchr(copy_filename, '/'))) {
*s = '\0';
if (*copy_filename && !check_exec_for_suid_message_recursive(copy_filename)) {
amfree(copy_filename);
return FALSE;
}
}
amfree(copy_filename);
}
else {
return build_message(
AMANDA_FILE, __LINE__, 3600067, MSG_ERROR, 2,
"errno", errno,
"filename", filename);
}
return NULL;
}
#endif
#ifndef SINGLE_USERID
gboolean check_exec_for_suid_recursive(char *filename, FILE *verbose);
#endif
gboolean
check_exec_for_suid(
char *type,
char *filename,
FILE *verbose,
char **my_realpath)
{
#ifndef SINGLE_USERID
message_t *message;
char tmp_realpath[PATH_MAX];
*my_realpath = realpath(filename, tmp_realpath);
if (!*my_realpath) {
int saved_errno = errno;
char *quoted = quote_string(filename);
if (verbose)
g_fprintf(verbose, "ERROR [Can't find realpath for '%s': %s\n", quoted, strerror(saved_errno));
g_debug("ERROR [Can't find realpath for '%s': %s", quoted, strerror(saved_errno));
amfree(quoted);
return FALSE;
}
*my_realpath = g_strdup(tmp_realpath);
if ((message = security_allow_program_as_root(type, *my_realpath))) {
if (verbose)
g_fprintf(verbose, "%s\n", get_message(message));
return FALSE;
}
return check_exec_for_suid_recursive(*my_realpath, verbose);
#else
(void)type;
*my_realpath = g_strdup(filename);
(void)verbose;
return TRUE;
#endif
}
#ifndef SINGLE_USERID
gboolean
check_exec_for_suid_recursive(
char *filename,
FILE *verbose)
{
struct stat stat_buf;
char *quoted = quote_string(filename);
if (lstat(filename, &stat_buf) == 0) {
char *copy_filename;
char *s;
if (stat_buf.st_uid != 0 ) {
if (verbose)
g_fprintf(verbose, "ERROR [%s is not owned by root]\n", quoted);
g_debug("Error: %s is not owned by root", quoted);
amfree(quoted);
return FALSE;
}
if (stat_buf.st_mode & S_IWOTH) {
if (verbose)
g_fprintf(verbose, "ERROR [%s is writable by everyone]\n", quoted);
g_debug("Error: %s is writable by everyone", quoted);
amfree(quoted);
return FALSE;
}
if (stat_buf.st_mode & S_IWGRP) {
if (verbose)
g_fprintf(verbose, "ERROR [%s is writable by the group]\n", quoted);
g_debug("Error: %s is writable by the group", quoted);
amfree(quoted);
return FALSE;
}
copy_filename = g_strdup(filename);
if ((s = strchr(copy_filename, '/'))) {
*s = '\0';
if (*copy_filename && !check_exec_for_suid_recursive(copy_filename, verbose)) {
amfree(quoted);
amfree(copy_filename);
return FALSE;
}
}
amfree(copy_filename);
}
else {
if (verbose)
g_fprintf(verbose, "ERROR [can not stat %s: %s]\n", quoted, strerror(errno));
g_debug("Error: can not stat %s: %s", quoted, strerror(errno));
amfree(quoted);
return FALSE;
}
amfree(quoted);
return TRUE;
}
#endif
message_t *
check_access_message(
char * filename,
int mode)
{
char *noun, *adjective;
char ruid_str[NUM_STR_SIZE];
char euid_str[NUM_STR_SIZE];
g_snprintf(ruid_str, sizeof(ruid_str), "%d", (int)getuid());
g_snprintf(euid_str, sizeof(euid_str), "%d", (int)geteuid());
if(mode == F_OK)
noun = "find", adjective = "exists";
else if((mode & X_OK) == X_OK)
noun = "execute", adjective = "executable";
else if((mode & (W_OK|R_OK)) == (W_OK|R_OK))
noun = "read/write", adjective = "read/writable";
else
noun = "access", adjective = "accessible";
if(EUIDACCESS(filename, mode) == -1) {
return build_message(
AMANDA_FILE, __LINE__, 3600063, MSG_ERROR, 5,
"errno", errno,
"noun", noun,
"filename", filename,
"ruid", ruid_str,
"euid", euid_str);
} else {
return build_message(
AMANDA_FILE, __LINE__, 3600064, MSG_INFO, 5,
"noun", noun,
"adjective", adjective,
"filename", filename,
"ruid", ruid_str,
"euid", euid_str);
}
}
message_t *
check_file_message(
char * filename,
int mode)
{
struct stat stat_buf;
if(!stat(filename, &stat_buf)) {
if(!S_ISREG(stat_buf.st_mode)) {
return build_message(
AMANDA_FILE, __LINE__, 3600059, MSG_ERROR, 1,
"filename", filename);
}
} else {
return build_message(
AMANDA_FILE, __LINE__, 3600060, MSG_ERROR, 2,
"errno", errno,
"filename", filename);
}
return check_access_message(filename, mode);
}
message_t *
check_dir_message(
char * dirname,
int mode)
{
struct stat stat_buf;
char *dir;
message_t *result;
if(!stat(dirname, &stat_buf)) {
if(!S_ISDIR(stat_buf.st_mode)) {
return build_message(
AMANDA_FILE, __LINE__, 3600061, MSG_ERROR, 1,
"dirname", dirname);
return FALSE;
}
} else {
return build_message(
AMANDA_FILE, __LINE__, 3600062, MSG_ERROR, 2,
"errno", errno,
"dirname", dirname);
return FALSE;
}
dir = g_strconcat(dirname, "/.", NULL);
result = check_access_message(dir, mode);
amfree(dir);
return result;
}
message_t *
check_suid_message(
char * filename)
{
#ifndef SINGLE_USERID
struct stat stat_buf;
if(!stat(filename, &stat_buf)) {
if(stat_buf.st_uid != 0 ) {
return build_message(
AMANDA_FILE, __LINE__, 3600065, MSG_ERROR, 1,
"filename", filename);
}
if((stat_buf.st_mode & S_ISUID) != S_ISUID) {
return build_message(
AMANDA_FILE, __LINE__, 3600066, MSG_ERROR, 1,
"filename", filename);
}
}
else {
return build_message(
AMANDA_FILE, __LINE__, 3600067, MSG_ERROR, 2,
"errno", errno,
"filename", filename);
}
#else
(void)filename; /* Quiet unused parameter warning */
#endif
return NULL;
}
/*
* Returns the value of the first integer in a string.
*/
double
the_num(
char * str,
int pos)
{
char *num;
int ch;
double d;
do {
ch = *str++;
while(ch && !isdigit(ch)) ch = *str++;
if (pos == 1) break;
pos--;
while(ch && (isdigit(ch) || ch == '.')) ch = *str++;
} while (ch);
num = str - 1;
while(isdigit(ch) || ch == '.') ch = *str++;
str[-1] = '\0';
d = atof(num);
str[-1] = (char)ch;
return d;
}
char *
config_errors_to_error_string(
GSList *errlist)
{
char *errmsg;
gboolean multiple_errors = FALSE;
if (errlist) {
errmsg = (char *)errlist->data;
if (errlist->next)
multiple_errors = TRUE;
} else {
errmsg = _("(no error message)");
}
return g_strdup_printf("ERROR %s%s", errmsg,
multiple_errors? _(" (additional errors not displayed)"):"");
}
void
add_type_table(
dmpline_t typ,
amregex_t **re_table,
amregex_t *orig_re_table,
GSList *normal_message,
GSList *ignore_message,
GSList *strange_message)
{
amregex_t *rp;
for(rp = orig_re_table; rp->regex != NULL; rp++) {
if (rp->typ == typ) {
int found = 0;
GSList *mes;
for (mes = normal_message; mes != NULL; mes = mes->next) {
if (g_str_equal(rp->regex, (char *)mes->data))
found = 1;
}
for (mes = ignore_message; mes != NULL; mes = mes->next) {
if (g_str_equal(rp->regex, (char *)mes->data))
found = 1;
}
for (mes = strange_message; mes != NULL; mes = mes->next) {
if (g_str_equal(rp->regex, (char *)mes->data))
found = 1;
}
if (found == 0) {
(*re_table)->regex = rp->regex;
(*re_table)->srcline = rp->srcline;
(*re_table)->scale = rp->scale;
(*re_table)->field = rp->field;
(*re_table)->typ = rp->typ;
(*re_table)++;
}
}
}
}
void
add_list_table(
dmpline_t typ,
amregex_t **re_table,
GSList *message)
{
GSList *mes;
for (mes = message; mes != NULL; mes = mes->next) {
(*re_table)->regex = (char *)mes->data;
(*re_table)->srcline = 0;
(*re_table)->scale = 0;
(*re_table)->field = 0;
(*re_table)->typ = typ;
(*re_table)++;
}
}
amregex_t *
build_re_table(
amregex_t *orig_re_table,
GSList *normal_message,
GSList *ignore_message,
GSList *strange_message)
{
int nb = 0;
amregex_t *rp;
amregex_t *re_table, *new_re_table;
for(rp = orig_re_table; rp->regex != NULL; rp++) {
nb++;
}
nb += g_slist_length(normal_message);
nb += g_slist_length(ignore_message);
nb += g_slist_length(strange_message);
nb ++;
re_table = new_re_table = malloc(nb * sizeof(amregex_t));
/* add SIZE from orig_re_table */
add_type_table(DMP_SIZE, &re_table, orig_re_table,
normal_message, ignore_message, strange_message);
/* add ignore_message */
add_list_table(DMP_IGNORE, &re_table, ignore_message);
/* add IGNORE from orig_re_table */
add_type_table(DMP_IGNORE, &re_table, orig_re_table,
normal_message, ignore_message, strange_message);
/* add normal_message */
add_list_table(DMP_NORMAL, &re_table, normal_message);
/* add NORMAL from orig_re_table */
add_type_table(DMP_NORMAL, &re_table, orig_re_table,
normal_message, ignore_message, strange_message);
/* add strange_message */
add_list_table(DMP_STRANGE, &re_table, strange_message);
/* add STRANGE from orig_re_table */
add_type_table(DMP_STRANGE, &re_table, orig_re_table,
normal_message, ignore_message, strange_message);
/* add ERROR from orig_re_table */
add_type_table(DMP_ERROR, &re_table, orig_re_table,
normal_message, ignore_message, strange_message);
/* Add DMP_STRANGE with NULL regex, */
/* it is not copied by previous statement */
re_table->regex = NULL;
re_table->srcline = 0;
re_table->scale = 0;
re_table->field = 0;
re_table->typ = DMP_STRANGE;
return new_re_table;
}