#include "amfeatures.h"
#include "packet.h"
#include "version.h"
-#include "queue.h"
#include "security.h"
#include "stream.h"
#include "util.h"
SERVICE_SENDBACKUP,
SERVICE_SELFCHECK,
SERVICE_AMINDEXD,
- SERVICE_AMIDXTAPED
+ SERVICE_AMIDXTAPED,
+ SERVICE_AMDUMPD
} service_t;
static struct services {
{ "sendbackup", 1, SERVICE_SENDBACKUP },
{ "selfcheck", 1, SERVICE_SELFCHECK },
{ "amindexd", 0, SERVICE_AMINDEXD },
- { "amidxtaped", 0, SERVICE_AMIDXTAPED }
+ { "amidxtaped", 0, SERVICE_AMIDXTAPED },
+ { "amdumpd", 0, SERVICE_AMDUMPD }
};
#define NSERVICES (int)(sizeof(services) / sizeof(services[0]))
struct active_service *as; /* pointer back to our enclosure */
} data[DATA_FD_COUNT];
char databuf[NETWORK_BLOCK_BYTES]; /* buffer to relay netfd data in */
- TAILQ_ENTRY(active_service) tq; /* queue handle */
};
/*
* Queue of outstanding requests that we are running.
*/
-static struct {
- TAILQ_HEAD(, active_service) tailq;
- int qlength;
-} serviceq = {
- TAILQ_HEAD_INITIALIZER(serviceq.tailq), 0
-};
+GSList *serviceq = NULL;
static int wait_30s = 1;
static int exit_on_qlength = 1;
static action_t s_ackwait(struct active_service *, action_t, pkt_t *);
static void repfd_recv(void *);
+static void process_errfd(void *cookie);
static void errfd_recv(void *);
static void timeout_repfd(void *);
static void protocol_recv(void *, pkt_t *, security_status_t);
safe_fd(-1, 0);
safe_cd();
+ /*
+ * Nexenta needs the SUN_PERSONALITY env variable to be unset, otherwise
+ * the Sun version of tar in /usr/sun/sbin/tar is called instead.
+ *
+ * On other operating systems this will have no effect.
+ */
+
+#ifdef HAVE_UNSETENV
+ unsetenv("SUN_PERSONALITY");
+#endif
+
/*
* When called via inetd, it is not uncommon to forget to put the
* argv[0] value on the config line. On some systems (e.g. Solaris)
/*NOTREACHED*/
}
+ if (argc > 1 && argv && argv[1] && g_str_equal(argv[1], "--version")) {
+ printf("amandad-%s\n", VERSION);
+ return (0);
+ }
+
/* Don't die when child closes pipe */
signal(SIGPIPE, SIG_IGN);
check_running_as(RUNNING_AS_CLIENT_LOGIN);
}
- erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
+ add_amanda_log_handler(amanda_log_stderr);
+ add_amanda_log_handler(amanda_log_syslog);
/*
* ad-hoc argument parsing
in = 0; out = 1; /* default to stdin/stdout */
have_services = 0;
for (i = 1; i < argc; i++) {
- /*
- * accept -krb4 as an alias for -auth=krb4 (for compatibility)
- */
- if (strcmp(argv[i], "-krb4") == 0) {
- argv[i] = "-auth=krb4";
- /* FALLTHROUGH */
- auth = "krb4";
- }
-
/*
* Get a driver for a security type specified after -auth=
*/
- else if (strncmp(argv[i], "-auth=", strlen("-auth=")) == 0) {
+ if (strncmp(argv[i], "-auth=", strlen("-auth=")) == 0) {
argv[i] += strlen("-auth=");
secdrv = security_getdriver(argv[i]);
auth = argv[i];
error(_("no driver for security type '%s'\n"), argv[i]);
/*NOTREACHED*/
}
+ if (strcmp(auth, "local") == 0 ||
+ strcmp(auth, "rsh") == 0 ||
+ strcmp(auth, "ssh") == 0) {
+ int i;
+ for (i=0; i < NSERVICES; i++) {
+ services[i].active = 1;
+ }
+ }
continue;
}
}
/*
- * If no security type specified, use BSD
+ * If no security type specified, use BSDTCP
*/
if (secdrv == NULL) {
- secdrv = security_getdriver("BSD");
- auth = "bsd";
+ secdrv = security_getdriver("BSDTCP");
+ auth = "bsdtcp";
if (secdrv == NULL) {
- error(_("no driver for default security type 'BSD'\n"));
+ error(_("no driver for default security type 'BSDTCP'\n"));
/*NOTREACHED*/
}
}
exit_on_qlength = 1;
}
- if (getuid() == 0) {
+#ifndef SINGLE_USERID
+ if (geteuid() == 0) {
if (strcasecmp(auth, "krb5") != 0) {
- error(_("Amanda must be run as user '%s' when using '%s' authentication"),
- CLIENT_LOGIN, auth);
+ struct passwd *pwd;
+ /* lookup our local user name */
+ if ((pwd = getpwnam(CLIENT_LOGIN)) == NULL) {
+ error(_("getpwnam(%s) failed."), CLIENT_LOGIN);
+ }
+
+ if (pwd->pw_uid != 0) {
+ error(_("'amandad' must be run as user '%s' when using '%s' authentication"),
+ CLIENT_LOGIN, auth);
+ }
}
} else {
if (strcasecmp(auth, "krb5") == 0) {
- error(_("Amanda must be run as user 'root' when using 'krb5' authentication"));
+ error(_("'amandad' must be run as user 'root' when using 'krb5' authentication"));
}
}
-
+#endif
/* initialize */
startclock();
- dbprintf(_("version %s\n"), version());
+ dbprintf(_("version %s\n"), VERSION);
for (i = 0; version_info[i] != NULL; i++) {
dbprintf(" %s", version_info[i]);
}
/*
* If things are still running, then don't exit.
*/
- if (serviceq.qlength > 0)
+ if (g_slist_length(serviceq) > 0)
return;
/*
pkt_t * pkt)
{
pkt_t pkt_out;
+ GSList *iter;
struct active_service *as;
char *pktbody, *tok, *service, *arguments;
char *service_path = NULL;
}
/*
- * If we have errors (not warnings) from the config file, let the server
+ * If we have errors (not warnings) from the config file, let the remote system
* know immediately. Unfortunately, we only get one ERROR line, so if there
* are multiple errors, we just show the first.
*/
return;
}
+ g_debug("authenticated peer name is '%s'", security_get_authenticated_peer_name(handle));
+
/*
* If pkt is NULL, then there was a problem with the new connection.
*/
goto send_pkt_out;
}
- service_path = vstralloc(amlibexecdir, "/", service, versionsuffix(), NULL);
+ service_path = vstralloc(amlibexecdir, "/", service, NULL);
if (access(service_path, X_OK) < 0) {
dbprintf(_("can't execute %s: %s\n"), service_path, strerror(errno));
pkt_init(&pkt_out, P_NAK,
}
/* see if its already running */
- for (as = TAILQ_FIRST(&serviceq.tailq); as != NULL;
- as = TAILQ_NEXT(as, tq)) {
+ for (iter = serviceq; iter != NULL; iter = g_slist_next(iter)) {
+ as = (struct active_service *)iter->data;
if (strcmp(as->cmd, service_path) == 0 &&
strcmp(as->arguments, arguments) == 0) {
dbprintf(_("%s %s: already running, acking req\n"),
}
}
- /* Process errfd before sending the REP packet */
- if (as->ev_errfd) {
- SELECT_ARG_TYPE readset;
- struct timeval tv;
- int nfound;
-
- memset(&tv, 0, SIZEOF(tv));
- FD_ZERO(&readset);
- FD_SET(as->errfd, &readset);
- nfound = select(as->errfd+1, &readset, NULL, NULL, &tv);
- if (nfound && FD_ISSET(as->errfd, &readset)) {
- errfd_recv(as);
- }
- }
+ process_errfd(as);
if (pid == 0)
pid = waitpid(as->pid, &retstat, WNOHANG);
for (dh = &as->data[0]; dh < &as->data[DATA_FD_COUNT]; dh++) {
if (dh->netfd == NULL)
continue;
+ dbprintf("opening security stream for fd %d\n", (int)(dh - as->data) + DATA_FD_OFFSET);
if (security_stream_accept(dh->netfd) < 0) {
dbprintf(_("stream %td accept failed: %s\n"),
dh - &as->data[0], security_geterror(as->security_handle));
state_machine(as, A_RECVREP, NULL);
}
+static void
+process_errfd(
+ void *cookie)
+{
+ struct active_service *as = cookie;
+
+ /* Process errfd before sending the REP packet */
+ if (as->ev_errfd) {
+ SELECT_ARG_TYPE readset;
+ struct timeval tv;
+ int nfound;
+
+ memset(&tv, 0, SIZEOF(tv));
+ FD_ZERO(&readset);
+ FD_SET(as->errfd, &readset);
+ nfound = select(as->errfd+1, &readset, NULL, NULL, &tv);
+ if (nfound && FD_ISSET(as->errfd, &readset)) {
+ errfd_recv(as);
+ }
+ }
+}
+
/*
* Called when a errfd has received data
*/
/* fill info_end_buf from the tail end of combined_buf */
memcpy(as->info_end_buf, combined_buf + n, INFO_END_LEN);
+ amfree(combined_buf);
/* if we did see info_end_str, start reading the data fd (fd 0) */
if (as->seen_info_end) {
{
struct datafd_handle *dh;
+ /* note that handle is in the range DATA_FD_OFFSET to DATA_FD_COUNT, but
+ * it is NOT a file descriptor! */
+
/* if the handle is -1, then we don't bother */
if (handle < 0)
return (-1);
struct active_service *as;
pid_t pid;
int newfd;
+ char *peer_name;
+ char *amanda_remote_host_env[2];
assert(security_handle != NULL);
assert(cmd != NULL);
/* add it to the service queue */
/* increment the active service count */
- TAILQ_INSERT_TAIL(&serviceq.tailq, as, tq);
- serviceq.qlength++;
+ serviceq = g_slist_append(serviceq, (gpointer)as);
return (as);
case 0:
* and start up.
*/
+ /* set up the AMANDA_AUTHENTICATED_PEER env var so child services
+ * can use it to authenticate */
+ peer_name = security_get_authenticated_peer_name(security_handle);
+ amanda_remote_host_env[0] = NULL;
+ amanda_remote_host_env[1] = NULL;
+ if (*peer_name) {
+ amanda_remote_host_env[0] =
+ g_strdup_printf("AMANDA_AUTHENTICATED_PEER=%s", peer_name);
+ }
+
/*
* The data stream is stdin in the new process
*/
data_write[i + 1][0] = newfd;
}
}
+ while(data_write[4][0] >= DATA_FD_OFFSET &&
+ data_write[4][0] <= DATA_FD_OFFSET + DATA_FD_COUNT*2 - 1) {
+ newfd = dup(data_write[4][0]);
+ if (newfd == -1)
+ error(_("Can't dup out off DATA_FD range"));
+ data_write[4][0] = newfd;
+ }
+ while(data_write[4][1] >= DATA_FD_OFFSET &&
+ data_write[4][1] <= DATA_FD_OFFSET + DATA_FD_COUNT*2 - 1) {
+ newfd = dup(data_write[4][1]);
+ if (newfd == -1)
+ error(_("Can't dup out off DATA_FD range"));
+ data_write[4][1] = newfd;
+ }
+
for (i = 0; i < DATA_FD_COUNT*2; i++)
close(DATA_FD_OFFSET + i);
aclose(data_write[STDERR_PIPE][1]);
safe_fd(DATA_FD_OFFSET, DATA_FD_COUNT*2);
- execle(cmd, cmd, "amandad", auth, (char *)NULL, safe_env());
+ execle(cmd, cmd, "amandad", auth, (char *)NULL, safe_env_full(amanda_remote_host_env));
error(_("could not exec service %s: %s\n"), cmd, strerror(errno));
/*NOTREACHED*/
}
struct active_service * as)
{
int i;
+ int count;
+ pid_t pid;
struct datafd_handle *dh;
amandad_debug(1, _("closing service: %s\n"),
aclose(as->reqfd);
if (as->repfd != -1)
aclose(as->repfd);
+ if (as->errfd != -1) {
+ process_errfd(as);
+ aclose(as->errfd);
+ }
if (as->ev_repfd != NULL)
event_release(as->ev_repfd);
if (as->ev_reptimeout != NULL)
event_release(as->ev_reptimeout);
+ if (as->ev_errfd != NULL)
+ event_release(as->ev_errfd);
for (i = 0; i < DATA_FD_COUNT; i++) {
dh = &as->data[i];
if (as->security_handle != NULL)
security_close(as->security_handle);
+ /* try to kill the process; if this fails, then it's already dead and
+ * likely some of the other zombie cleanup ate its brains, so we don't
+ * bother to waitpid for it */
assert(as->pid > 0);
- kill(as->pid, SIGTERM);
- waitpid(as->pid, NULL, WNOHANG);
+ pid = waitpid(as->pid, NULL, WNOHANG);
+ if (pid != as->pid && kill(as->pid, SIGTERM) == 0) {
+ pid = waitpid(as->pid, NULL, WNOHANG);
+ count = 5;
+ while (pid != as->pid && count > 0) {
+ count--;
+ sleep(1);
+ pid = waitpid(as->pid, NULL, WNOHANG);
+ }
+ if (pid != as->pid) {
+ g_debug("Process %d failed to exit", (int)as->pid);
+ }
+ }
- TAILQ_REMOVE(&serviceq.tailq, as, tq);
- assert(serviceq.qlength > 0);
- serviceq.qlength--;
+ serviceq = g_slist_remove(serviceq, (gpointer)as);
amfree(as->cmd);
amfree(as->arguments);
amfree(as->rep_pkt.body);
amfree(as);
- if(exit_on_qlength == 0 && serviceq.qlength == 0) {
+ if(exit_on_qlength == 0 && g_slist_length(serviceq) == 0) {
dbclose();
exit(0);
}