/*
* Amanda, The Advanced Maryland Automatic Network Disk Archiver
* Copyright (c) 1991-1999 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.
*/
#include "amanda.h"
#include "amandad.h"
#include "match.h"
#include "clock.h"
#include "amfeatures.h"
#include "getfsent.h"
#include "conffile.h"
#include "amandates.h"
#include "stream.h"
#include "amxml.h"
#include "client_util.h"
#include "ammessage.h"
#define senddiscover_debug(i, ...) do { \
if ((i) <= debug_sendbackup) { \
g_debug(__VA_LIST__); \
} \
} while (0)
#define TIMEOUT 30
pid_t application_api_pid = (pid_t)-1;
g_option_t *g_options = NULL;
dle_t *gdle = NULL;
static am_feature_t *our_features = NULL;
static char *our_feature_string = NULL;
static char *amandad_auth = NULL;
/* local functions */
int main(int argc, char **argv);
char *childstr(pid_t pid);
int check_status(pid_t pid, amwait_t w);
int check_result(void);
static void senddiscover_print_array_message(gpointer data, gpointer user_data);static void
senddiscover_print_array_message(
gpointer data,
gpointer user_data)
{
FILE *stream = (FILE *)user_data;
message_t *message = data;
fprint_message(stream, message);
delete_message(message);
//delete_message(fprint_message(stream, message));
}
int
main(
int argc,
char ** argv)
{
dle_t *dle = NULL;
char *line = NULL;
GSList *errlist;
char **env;
if (argc > 1 && argv[1] && g_str_equal(argv[1], "--version")) {
printf("senddiscover-%s\n", VERSION);
return (0);
}
glib_init();
/* initialize */
/*
* Configure program for internationalization:
* 1) Only set the message locale for now.
* 2) Set textdomain for all amanda related programs to "amanda"
* We don't want to be forced to support dozens of message catalogs.
*/
setlocale(LC_MESSAGES, "C");
textdomain("amanda");
safe_fd(DATA_FD_OFFSET, DATA_FD_COUNT*2);
openbsd_fd_inform();
safe_cd();
set_pname("senddiscover");
/* Don't die when child closes pipe */
signal(SIGPIPE, SIG_IGN);
/* Don't die when interrupt received */
signal(SIGINT, SIG_IGN);
add_amanda_log_handler(amanda_log_stderr);
add_amanda_log_handler(amanda_log_syslog);
dbopen(DBG_SUBDIR_CLIENT);
startclock();
g_debug("Version %s", VERSION);
if(argc > 2 && g_str_equal(argv[1], "amandad")) {
amandad_auth = g_strdup(argv[2]);
}
our_features = am_init_feature_set();
our_feature_string = am_feature_to_string(our_features);
config_init(CONFIG_INIT_CLIENT|CONFIG_INIT_GLOBAL, NULL);
/* (check for config errors comes later) */
check_running_as(RUNNING_AS_CLIENT_LOGIN);
for(; (line = agets(stdin)) != NULL; free(line)) {
if (line[0] == '\0')
continue;
if(strncmp_const(line, "OPTIONS ") == 0) {
g_options = parse_g_options(line+8, 1);
if(!g_options->hostname) {
g_options->hostname = g_malloc(MAX_HOSTNAME_LENGTH+1);
gethostname(g_options->hostname, MAX_HOSTNAME_LENGTH);
g_options->hostname[MAX_HOSTNAME_LENGTH] = '\0';
}
if (g_options->config) {
/* overlay this configuration on the existing (nameless) configuration */
config_init(CONFIG_INIT_CLIENT | CONFIG_INIT_EXPLICIT_NAME | CONFIG_INIT_OVERLAY,
g_options->config);
dbrename(get_config_name(), DBG_SUBDIR_CLIENT);
}
/* check for any config errors now */
if (config_errors(&errlist) >= CFGERR_ERRORS) {
g_printf("OPTIONS \n");
g_printf("[\n");
config_print_errors_as_message();
goto err;
}
if (am_has_feature(g_options->features, fe_req_xml)) {
break;
}
continue;
}
}
amfree(line);
if (g_options == NULL) {
g_printf("OPTIONS \n");
g_printf("[\n");
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900004, MSG_ERROR, 1,
"service", "senddiscover")));
goto err;
}
g_printf("OPTIONS ");
if(am_has_feature(g_options->features, fe_rep_options_features)) {
g_printf("features=%s;", our_feature_string);
}
if(am_has_feature(g_options->features, fe_rep_options_hostname)) {
g_printf("hostname=%s;", g_options->hostname);
}
if (!am_has_feature(g_options->features, fe_rep_options_features) &&
!am_has_feature(g_options->features, fe_rep_options_hostname)) {
g_printf(";");
}
g_printf("\n");
g_printf("[\n");
fflush(stdout);
if (am_has_feature(g_options->features, fe_req_xml)) {
char *errmsg = NULL;
dle = amxml_parse_node_FILE(stdin, &errmsg);
if (errmsg) {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900013, MSG_ERROR, 2,
"service", "senddiscover",
"errmsg", errmsg)));
goto err;
}
if (!dle) {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900014, MSG_ERROR, 1,
"service", "senddiscover")));
goto err;
} else if (dle->next) {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900015, MSG_ERROR, 1,
"service", "senddiscover")));
goto err;
}
} else {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900007, MSG_ERROR, 1,
"service", "senddiscover")));
goto err;
}
gdle = dle;
if (dle->program == NULL) {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900008, MSG_ERROR, 1,
"service", "senddiscover")));
goto err;
}
g_debug(" Parsed request as: program '%s'", dle->program);
if (dle->program_is_application_api == 0) {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900003, MSG_ERROR, 1,
"service", "senddiscover")));
goto err;
}
if (dle->auth && amandad_auth) {
if (strcasecmp(dle->auth, amandad_auth) != 0) {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900009, MSG_ERROR, 3,
"service", "senddiscover",
"amandad_auth", amandad_auth,
"dle_auth", dle->auth)));
goto err;
}
}
if (merge_dles_properties(dle, 0) == 0) {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900003, MSG_ERROR, 1,
"service", "senddiscover")));
goto err;
}
if (dle->program_is_application_api == 1) {
guint j;
char *cmd=NULL;
GPtrArray *argv_ptr;
backup_support_option_t *bsu;
int result;
GPtrArray *errarray;
int outfd[2];
int errfd[2];
FILE *dumperr;
bsu = backup_support_option(dle->program, &errarray);
if (!bsu) {
char *errmsg;
guint i;
for (i=0; i < errarray->len; i++) {
errmsg = g_ptr_array_index(errarray, i);
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900000, MSG_ERROR, 3,
"service", "senddiscover",
"application", dle->program,
"errmsg", errmsg)));
}
if (i == 0) { /* no errarray */
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900000, MSG_ERROR, 2,
"service", "senddiscover",
"application", dle->program)));
}
g_ptr_array_free_full(errarray);
goto err;
}
if (!bsu->discover) {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900002, MSG_ERROR, 3,
"service", "senddiscover",
"application", dle->program,
"method", "discover")));
goto err;
}
if (pipe(outfd) < 0) {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900005, MSG_ERROR, 3,
"service", "senddiscover",
"application", dle->program,
"method", "discover")));
goto err;
}
if (pipe(errfd) < 0) {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900005, MSG_ERROR, 3,
"service", "senddiscover",
"application", dle->program,
"method", "discover")));
goto err;
}
switch(application_api_pid=fork()) {
case 0: /* child */
argv_ptr = g_ptr_array_new();
cmd = g_strjoin(NULL, APPLICATION_DIR, "/", dle->program, NULL);
g_ptr_array_add(argv_ptr, g_strdup(dle->program));
g_ptr_array_add(argv_ptr, g_strdup("discover"));
if (bsu->message_line == 1) {
g_ptr_array_add(argv_ptr, g_strdup("--message"));
g_ptr_array_add(argv_ptr, g_strdup("json"));
}
if (g_options->config && bsu->config == 1) {
g_ptr_array_add(argv_ptr, g_strdup("--config"));
g_ptr_array_add(argv_ptr, g_strdup(g_options->config));
}
if (dle->device) {
g_ptr_array_add(argv_ptr, g_strdup("--device"));
g_ptr_array_add(argv_ptr, g_strdup(dle->device));
}
if (g_options->hostname && bsu->host == 1) {
g_ptr_array_add(argv_ptr, g_strdup("--host"));
g_ptr_array_add(argv_ptr, g_strdup(g_options->hostname));
}
application_property_add_to_argv(argv_ptr, dle, bsu,
g_options->features);
g_ptr_array_add(argv_ptr, NULL);
g_debug("%s: running \"%s", get_pname(), cmd);
for (j = 1; j < argv_ptr->len - 1; j++)
g_debug(" %s", (char *)g_ptr_array_index(argv_ptr,j));
g_debug("\"");
if (dup2(outfd[1], 1) == -1) {
int save_errno = errno;
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900006, MSG_ERROR, 4,
"service", "senddiscover",
"application", dle->program,
"method", "discover",
"errno", save_errno)));
goto err;
}
if (dup2(errfd[1], 2) == -1) {
int save_errno = errno;
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900006, MSG_ERROR, 4,
"service", "senddiscover",
"application", dle->program,
"method", "discover",
"errno", save_errno)));
goto err;
}
safe_fd(3, 1);
env = safe_env();
execve(cmd, (char **)argv_ptr->pdata, env);
free_env(env);
goto err;
break;
default:
break;
case -1: {
int save_errno = errno;
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900010, MSG_ERROR, 4,
"service", "senddiscover",
"application", dle->program,
"method", "discover",
"errno", save_errno)));
goto err;
}
}
close(errfd[1]);
close(outfd[1]);
{
GString *json_buffer = g_string_new("");
char *buf = g_malloc(32769);
size_t size;
while ((size = read_fully(outfd[0], buf, 1000, NULL)) > 0) {
buf[size] = '\0';
g_string_append(json_buffer, buf);
}
g_free(buf);
close(outfd[0]);
if (json_buffer && json_buffer->str) {
GPtrArray *message_array;
g_debug("json_buffer: %s", json_buffer->str);
message_array = parse_json_message(json_buffer->str);
/* print and delete the messages */
g_ptr_array_foreach(message_array, senddiscover_print_array_message, stdout);
g_string_free(json_buffer, TRUE);
g_ptr_array_free(message_array, TRUE);
}
}
dumperr = fdopen(errfd[0],"r");
if (!dumperr) {
int save_errno = errno;
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900011, MSG_ERROR, 3,
"service", "senddiscover",
"application", dle->program,
"errno", save_errno)));
goto err;
}
result = 0;
while ((line = pgets(dumperr)) != NULL) {
if (strlen(line) > 0) {
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900012, MSG_ERROR, 3,
"service", "senddiscover",
"application", dle->program,
"errmsg", line)));
result = 1;
}
amfree(line);
}
result |= check_result();
amfree(bsu);
}
amfree(our_feature_string);
am_release_feature_set(our_features);
our_features = NULL;
free_g_options(g_options);
err:
g_printf("\n]\n");
dbclose();
return 0;
}
/*
* Returns a string for a child process. Checks the saved dump and
* compress pids to see which it is.
*/
char *
childstr(
pid_t pid)
{
if (pid == application_api_pid) {
if (!gdle) {
g_debug("gdle == NULL");
return "gdle == NULL";
}
return gdle->program;
}
return "unknown";
}
/*
* Determine if the child return status really indicates an error.
* If so, add the error message to the error string; more than one
* child can have an error.
*/
int
check_status(
pid_t pid,
amwait_t w)
{
char *str;
int ret, sig, rc;
str = childstr(pid);
if (WIFSIGNALED(w)) {
ret = 0;
rc = sig = WTERMSIG(w);
} else {
sig = 0;
rc = ret = WEXITSTATUS(w);
}
if (pid == application_api_pid)
application_api_pid = -1;
if (rc == 0) {
return 0; /* normal exit */
}
if (ret == 0) {
char *str_pid = g_strdup_printf("%d", (int)pid);
char *str_signal = g_strdup_printf("%d", (int)sig);
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900016, MSG_ERROR, 4,
"service", "senddiscover",
"application", str,
"pid", str_pid,
"signal", str_signal)));
g_free(str_pid);
g_free(str_signal);
} else {
char *str_pid = g_strdup_printf("%d", (int)pid);
char *str_code = g_strdup_printf("%d", (int)ret);
delete_message(fprint_message(stdout, build_message(
AMANDA_FILE, __LINE__, 2900017, MSG_ERROR, 4,
"service", "senddiscover",
"application", str,
"pid", str_pid,
"code", str_code)));
g_free(str_pid);
g_free(str_code);
}
return 1;
}
int
check_result(void)
{
int goterror;
pid_t wpid;
amwait_t retstat;
goterror = 0;
while ((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
if (check_status(wpid, retstat))
goterror = 1;
}
if (application_api_pid != -1) {
sleep(5);
while ((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
if (check_status(wpid, retstat))
goterror = 1;
}
}
if (application_api_pid != -1) {
g_debug("Sending SIGHUP to application process %d",
(int)application_api_pid);
if (application_api_pid != -1) {
if (kill(application_api_pid, SIGHUP) == -1) {
g_debug("Can't send SIGHUP to %d: %s",
(int)application_api_pid,
strerror(errno));
}
}
sleep(5);
while ((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
if (check_status(wpid, retstat))
goterror = 1;
}
}
if (application_api_pid != -1) {
g_debug("Sending SIGKILL to application process %d",
(int)application_api_pid);
if (application_api_pid != -1) {
if (kill(application_api_pid, SIGKILL) == -1) {
g_debug("Can't send SIGKILL to %d: %s",
(int)application_api_pid,
strerror(errno));
}
}
sleep(5);
while ((wpid = waitpid((pid_t)-1, &retstat, WNOHANG)) > 0) {
if (check_status(wpid, retstat))
goterror = 1;
}
}
return goterror;
}