*/
/*
- * $Id: amandad.c,v 1.18.2.5 2007/01/10 16:26:57 martinea Exp $
+ * $Id: amandad.c,v 1.18 2006/08/21 20:17:09 martinea Exp $
*
* handle client-host side of Amanda network communications, including
* security checks, execution of the proper service, and acking the
* master side
*/
-/*#define AMANDAD_DEBUG*/
-
#include "amanda.h"
#include "amandad.h"
#include "clock.h"
#include "security.h"
#include "stream.h"
#include "util.h"
+#include "conffile.h"
#define REP_TIMEOUT (6*60*60) /* secs for service to reply */
#define ACK_TIMEOUT 10 /* XXX should be configurable */
-#define MAX_REP_RETRIES 5
+#define STDERR_PIPE (DATA_FD_COUNT + 1)
+
+#define amandad_debug(i, ...) do { \
+ if ((i) <= debug_amandad) { \
+ dbprintf(__VA_ARGS__); \
+ } \
+} while (0)
/*
* These are the actions for entering the state machine
struct active_service;
typedef action_t (*state_t)(struct active_service *, action_t, pkt_t *);
+/* string that we scan for in sendbackup's MESG stream */
+static const char info_end_str[] = "sendbackup: info end\n";
+#define INFO_END_LEN (sizeof(info_end_str)-1)
+
+/*
+ * Here are the services that we allow.
+ * Must be in the same order as services[].
+ */
+typedef enum {
+ SERVICE_NOOP,
+ SERVICE_SENDSIZE,
+ SERVICE_SENDBACKUP,
+ SERVICE_SELFCHECK,
+ SERVICE_AMINDEXD,
+ SERVICE_AMIDXTAPED
+} service_t;
+
+static struct services {
+ char *name;
+ int active;
+ service_t service;
+} services[] = {
+ { "noop", 1, SERVICE_NOOP },
+ { "sendsize", 1, SERVICE_SENDSIZE },
+ { "sendbackup", 1, SERVICE_SENDBACKUP },
+ { "selfcheck", 1, SERVICE_SELFCHECK },
+ { "amindexd", 0, SERVICE_AMINDEXD },
+ { "amidxtaped", 0, SERVICE_AMIDXTAPED }
+};
+#define NSERVICES (int)(sizeof(services) / sizeof(services[0]))
+
/*
* This structure describes an active running service.
*
* for communications with the amanda server.
*/
struct active_service {
+ service_t service; /* service name */
char *cmd; /* name of command we ran */
char *arguments; /* arguments we sent it */
security_handle_t *security_handle; /* remote server */
int send_partial_reply; /* send PREP packet */
int reqfd; /* pipe to write requests */
int repfd; /* pipe to read replies */
+ int errfd; /* pipe to read stderr */
event_handle_t *ev_repfd; /* read event handle for repfd */
event_handle_t *ev_reptimeout; /* timeout for rep data */
+ event_handle_t *ev_errfd; /* read event handle for errfd */
pkt_t rep_pkt; /* rep packet we're sending out */
+ char *errbuf; /* buffer to read the err into */
char *repbuf; /* buffer to read the rep into */
size_t bufsize; /* length of repbuf */
size_t repbufsize; /* length of repbuf */
int repretry; /* times we'll retry sending the rep */
+ int seen_info_end; /* have we seen "sendbackup info end\n"? */
+ char info_end_buf[INFO_END_LEN]; /* last few bytes read, used for scanning for info end */
+
/*
* General user streams to the process, and their equivalent
* network streams.
TAILQ_ENTRY(active_service) tq; /* queue handle */
};
-/*
- * Here are the services that we allow.
- */
-static struct services {
- char *name;
- int active;
-} services[] = {
- { "noop", 1 },
- { "sendsize", 1 },
- { "sendbackup", 1 },
- { "selfcheck", 1 },
- { "amindexd", 0 },
- { "amidxtaped", 0 }
-};
-#define NSERVICES (int)(sizeof(services) / sizeof(services[0]))
-
/*
* Queue of outstanding requests that we are running.
*/
TAILQ_HEAD_INITIALIZER(serviceq.tailq), 0
};
-/*
- * Data for dbmalloc to check for memory leaks
- */
-#ifdef USE_DBMALLOC
-static struct {
- struct {
- unsigned long size, hist;
- } start, end;
-} dbmalloc_info;
-#endif
-
static int wait_30s = 1;
static int exit_on_qlength = 1;
static char *auth = NULL;
+static kencrypt_type amandad_kencrypt = KENCRYPT_NONE;
int main(int argc, char **argv);
static action_t s_ackwait(struct active_service *, action_t, pkt_t *);
static void repfd_recv(void *);
+static void errfd_recv(void *);
static void timeout_repfd(void *);
static void protocol_recv(void *, pkt_t *, security_status_t);
static void process_readnetfd(void *);
static void process_writenetfd(void *, void *, ssize_t);
static struct active_service *service_new(security_handle_t *,
- const char *, const char *);
+ const char *, service_t, const char *);
static void service_delete(struct active_service *);
static int writebuf(struct active_service *, const void *, size_t);
static ssize_t do_sendpkt(security_handle_t *handle, pkt_t *pkt);
+static char *amandad_get_security_conf (char *, void *);
-static void child_signal(int signal);
-
-#ifdef AMANDAD_DEBUG
static const char *state2str(state_t);
static const char *action2str(action_t);
-#endif
-
-/*
- * Harvests defunct processes...
- */
-
-static void
-child_signal(
- int signal)
-{
- pid_t rp;
-
- (void)signal; /* Quite compiler warning */
- /*
- * Reap and child status and promptly ignore since we don't care...
- */
- do {
- rp = waitpid(-1, NULL, WNOHANG);
- } while (rp > 0);
-}
int
main(
int in, out;
const security_driver_t *secdrv;
int no_exit = 0;
- struct sigaction act, oact;
char *pgm = "amandad"; /* in case argv[0] is not set */
-#if defined(AMANDAD_DEBUG) && defined(USE_REUSEADDR)
+#if defined(USE_REUSEADDR)
const int on = 1;
int r;
#endif
+ /*
+ * Configure program for internationalization:
+ * 1) Only set the message locale for now.
+ * 2) Set textdomain for all amanda related programs to "amanda"
+ * We don't want to be forced to support dozens of message catalogs.
+ */
+ setlocale(LC_MESSAGES, "C");
+ textdomain("amanda");
+
safe_fd(-1, 0);
safe_cd();
dbopen(DBG_SUBDIR_AMANDAD);
if(argv == NULL) {
- error("argv == NULL\n");
+ error(_("argv == NULL\n"));
/*NOTREACHED*/
}
/* Don't die when child closes pipe */
signal(SIGPIPE, SIG_IGN);
- /* Tell me when a child exits or dies... */
- act.sa_handler = child_signal;
- sigemptyset(&act.sa_mask);
- act.sa_flags = 0;
- if(sigaction(SIGCHLD, &act, &oact) != 0) {
- error("error setting SIGCHLD handler: %s", strerror(errno));
- /*NOTREACHED*/
- }
-
-#ifdef USE_DBMALLOC
- dbmalloc_info.start.size = malloc_inuse(&dbmalloc_info.start.hist);
-#endif
-
- erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
+ /* Parse the configuration; we'll handle errors later */
+ config_init(CONFIG_INIT_CLIENT, NULL);
-#ifdef FORCE_USERID
- /* we'd rather not run as root */
if (geteuid() == 0) {
- if(client_uid == (uid_t) -1) {
- error("error [cannot find user %s in passwd file]\n", CLIENT_LOGIN);
- /*NOTREACHED*/
- }
- initgroups(CLIENT_LOGIN, client_gid);
- setgid(client_gid);
- setegid(client_gid);
- seteuid(client_uid);
+ check_running_as(RUNNING_AS_ROOT);
+ initgroups(CLIENT_LOGIN, get_client_gid());
+ setgid(get_client_gid());
+ setegid(get_client_gid());
+ seteuid(get_client_uid());
+ } else {
+ check_running_as(RUNNING_AS_CLIENT_LOGIN);
}
-#endif /* FORCE_USERID */
+
+ erroutput_type = (ERR_INTERACTIVE|ERR_SYSLOG);
/*
* ad-hoc argument parsing
*
* We accept -auth=[authentication type]
* -no-exit
-#ifdef AMANDAD_DEBUG
* -tcp=[port]
* -udp=[port]
-#endif
* We also add a list of services that amandad can launch
*/
secdrv = NULL;
secdrv = security_getdriver(argv[i]);
auth = argv[i];
if (secdrv == NULL) {
- error("no driver for security type '%s'\n", argv[i]);
+ error(_("no driver for security type '%s'\n"), argv[i]);
/*NOTREACHED*/
}
continue;
continue;
}
-#ifdef AMANDAD_DEBUG
/*
* Allow us to directly bind to a udp port for debugging.
* This may only apply to some security types.
*/
else if (strncmp(argv[i], "-udp=", strlen("-udp=")) == 0) {
+#ifdef WORKING_IPV6
+ struct sockaddr_in6 sin;
+#else
struct sockaddr_in sin;
+#endif
argv[i] += strlen("-udp=");
+#ifdef WORKING_IPV6
+ in = out = socket(AF_INET6, SOCK_DGRAM, 0);
+#else
in = out = socket(AF_INET, SOCK_DGRAM, 0);
+#endif
if (in < 0) {
- error("can't create dgram socket: %s\n", strerror(errno));
+ error(_("can't create dgram socket: %s\n"), strerror(errno));
/*NOTREACHED*/
}
#ifdef USE_REUSEADDR
r = setsockopt(in, SOL_SOCKET, SO_REUSEADDR,
- (void *)&on, (socklen_t)sizeof(on));
+ (void *)&on, (socklen_t_equiv)sizeof(on));
if (r < 0) {
- dbprintf(("%s: amandad: setsockopt(SO_REUSEADDR) failed: %s\n",
- debug_prefix(NULL),
- strerror(errno)));
+ dbprintf(_("amandad: setsockopt(SO_REUSEADDR) failed: %s\n"),
+ strerror(errno));
}
#endif
+#ifdef WORKING_IPV6
+ sin.sin6_family = (sa_family_t)AF_INET6;
+ sin.sin6_addr = in6addr_any;
+ sin.sin6_port = (in_port_t)htons((in_port_t)atoi(argv[i]));
+#else
sin.sin_family = (sa_family_t)AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = (in_port_t)htons((in_port_t)atoi(argv[i]));
- if (bind(in, (struct sockaddr *)&sin, (socklen_t)sizeof(sin)) < 0) {
- error("can't bind to port %d: %s\n", atoi(argv[i]),
+#endif
+ if (bind(in, (struct sockaddr *)&sin, (socklen_t_equiv)sizeof(sin)) < 0) {
+ error(_("can't bind to port %d: %s\n"), atoi(argv[i]),
strerror(errno));
/*NOTREACHED*/
}
* Ditto for tcp ports.
*/
else if (strncmp(argv[i], "-tcp=", strlen("-tcp=")) == 0) {
+#ifdef WORKING_IPV6
+ struct sockaddr_in6 sin;
+#else
struct sockaddr_in sin;
+#endif
int sock;
- socklen_t n;
+ socklen_t_equiv n;
argv[i] += strlen("-tcp=");
+#ifdef WORKING_IPV6
+ sock = socket(AF_INET6, SOCK_STREAM, 0);
+#else
sock = socket(AF_INET, SOCK_STREAM, 0);
+#endif
if (sock < 0) {
- error("can't create tcp socket: %s\n", strerror(errno));
+ error(_("can't create tcp socket: %s\n"), strerror(errno));
/*NOTREACHED*/
}
- n = 1;
#ifdef USE_REUSEADDR
r = setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
- (void *)&on, (socklen_t)sizeof(on));
+ (void *)&on, (socklen_t_equiv)sizeof(on));
if (r < 0) {
- dbprintf(("%s: amandad: setsockopt(SO_REUSEADDR) failed: %s\n",
- debug_prefix(NULL),
- strerror(errno)));
+ dbprintf(_("amandad: setsockopt(SO_REUSEADDR) failed: %s\n"),
+ strerror(errno));
}
#endif
- setsockopt(sock, SOL_SOCKET, SO_REUSEADDR,
- (void *)&n, (socklen_t)sizeof(n));
+#ifdef WORKING_IPV6
+ sin.sin6_family = (sa_family_t)AF_INET6;
+ sin.sin6_addr = in6addr_any;
+ sin.sin6_port = (in_port_t)htons((in_port_t)atoi(argv[i]));
+#else
sin.sin_family = (sa_family_t)AF_INET;
sin.sin_addr.s_addr = INADDR_ANY;
sin.sin_port = (in_port_t)htons((in_port_t)atoi(argv[i]));
- if (bind(sock, (struct sockaddr *)&sin, (socklen_t)sizeof(sin)) < 0) {
- error("can't bind to port %d: %s\n", atoi(argv[i]),
+#endif
+ if (bind(sock, (struct sockaddr *)&sin, (socklen_t_equiv)sizeof(sin)) < 0) {
+ error(_("can't bind to port %d: %s\n"), atoi(argv[i]),
strerror(errno));
/*NOTREACHED*/
}
listen(sock, 10);
- n = (socklen_t)sizeof(sin);
+ n = (socklen_t_equiv)sizeof(sin);
in = out = accept(sock, (struct sockaddr *)&sin, &n);
}
-#endif
/*
* It must be a service name
*/
if (strcmp(services[j].name, argv[i]) == 0)
break;
if (j == (int)NSERVICES) {
- dbprintf(("%s: %s: invalid service\n",
- debug_prefix_time(NULL), argv[i]));
+ dbprintf(_("%s: invalid service\n"), argv[i]);
exit(1);
}
services[j].active = 1;
secdrv = security_getdriver("BSD");
auth = "bsd";
if (secdrv == NULL) {
- error("no driver for default security type 'BSD'\n");
+ error(_("no driver for default security type 'BSD'\n"));
/*NOTREACHED*/
}
}
if(strcasecmp(auth, "rsh") == 0 ||
strcasecmp(auth, "ssh") == 0 ||
+ strcasecmp(auth, "local") == 0 ||
strcasecmp(auth, "bsdtcp") == 0) {
wait_30s = 0;
exit_on_qlength = 1;
}
+ if (getuid() == 0) {
+ if (strcasecmp(auth, "krb5") != 0) {
+ 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(_("'amandad' must be run as user 'root' when using 'krb5' authentication"));
+ }
+ }
+
+
/* initialize */
startclock();
- dbprintf(("%s: version %s\n", get_pname(), version()));
+ dbprintf(_("version %s\n"), version());
for (i = 0; version_info[i] != NULL; i++) {
- dbprintf(("%s: %s", debug_prefix(NULL), version_info[i]));
+ dbprintf(" %s", version_info[i]);
}
if (! (argc >= 1 && argv != NULL && argv[0] != NULL)) {
- dbprintf(("%s: WARNING: argv[0] not defined: check inetd.conf\n",
- debug_prefix(NULL)));
+ dbprintf(_("WARNING: argv[0] not defined: check inetd.conf\n"));
+ }
+
+ /* krb5 require the euid to be 0 */
+ if (strcasecmp(auth, "krb5") == 0) {
+ seteuid((uid_t)0);
}
/*
* Schedule to call protocol_accept() when new security handles
* are created on stdin.
*/
- security_accept(secdrv, in, out, protocol_accept);
+ security_accept(secdrv, amandad_get_security_conf, in, out, protocol_accept, NULL);
/*
* Schedule an event that will try to exit every 30 seconds if there
if (no_exit)
return;
-#ifdef USE_DBMALLOC
- dbmalloc_info.end.size = malloc_inuse(&dbmalloc_info.end.hist);
-
- if (dbmalloc_info.start.size != dbmalloc_info.end.size) {
- malloc_list(dbfd(), dbmalloc_info.start.hist,
- dbmalloc_info.end.hist);
- }
-#endif
-
dbclose();
exit(0);
}
struct active_service *as;
char *pktbody, *tok, *service, *arguments;
char *service_path = NULL;
+ GSList *errlist = NULL;
int i;
pkt_out.body = NULL;
return;
}
+ /*
+ * If we have errors (not warnings) from the config file, let the server
+ * know immediately. Unfortunately, we only get one ERROR line, so if there
+ * are multiple errors, we just show the first.
+ */
+ if (config_errors(&errlist) >= CFGERR_ERRORS) {
+ GSList *iter = errlist;
+ char *errmsg;
+ gboolean multiple_errors = FALSE;
+
+ if (iter) {
+ errmsg = (char *)iter->data;
+ if (iter->next)
+ multiple_errors = TRUE;
+ } else {
+ errmsg = "(no error message)";
+ }
+
+ pkt_init(&pkt_out, P_NAK, "ERROR %s%s", errmsg,
+ multiple_errors? _(" (additional errors not displayed)"):"");
+ do_sendpkt(handle, &pkt_out);
+ amfree(pkt_out.body);
+ security_close(handle);
+ return;
+ }
+
/*
* If pkt is NULL, then there was a problem with the new connection.
*/
if (pkt == NULL) {
- dbprintf(("%s: accept error: %s\n",
- debug_prefix_time(NULL), security_geterror(handle)));
+ dbprintf(_("accept error: %s\n"), security_geterror(handle));
pkt_init(&pkt_out, P_NAK, "ERROR %s\n", security_geterror(handle));
do_sendpkt(handle, &pkt_out);
amfree(pkt_out.body);
return;
}
- dbprintf(("%s: accept recv %s pkt:\n<<<<<\n%s>>>>>\n",
- debug_prefix_time(NULL), pkt_type2str(pkt->type), pkt->body));
+ dbprintf(_("accept recv %s pkt:\n<<<<<\n%s>>>>>\n"),
+ pkt_type2str(pkt->type), pkt->body);
/*
* If this is not a REQ packet, just forget about it.
*/
if (pkt->type != P_REQ) {
- dbprintf(("%s: received unexpected %s packet:\n<<<<<\n%s>>>>>\n\n",
- debug_prefix_time(NULL), pkt_type2str(pkt->type), pkt->body));
+ dbprintf(_("received unexpected %s packet:\n<<<<<\n%s>>>>>\n\n"),
+ pkt_type2str(pkt->type), pkt->body);
security_close(handle);
return;
}
if (services[i].active == 1 && strcmp(services[i].name, service) == 0)
break;
if (i == (int)NSERVICES) {
- dbprintf(("%s: %s: invalid service\n",
- debug_prefix_time(NULL), service));
- pkt_init(&pkt_out, P_NAK, "ERROR %s: invalid service\n", service);
+ dbprintf(_("%s: invalid service\n"), service);
+ pkt_init(&pkt_out, P_NAK, _("ERROR %s: invalid service, add '%s' as argument to amandad\n"), service, service);
goto send_pkt_out;
}
- service_path = vstralloc(libexecdir, "/", service, versionsuffix(), NULL);
+ service_path = vstralloc(amlibexecdir, "/", service, versionsuffix(), NULL);
if (access(service_path, X_OK) < 0) {
- dbprintf(("%s: can't execute %s: %s\n",
- debug_prefix_time(NULL), service_path, strerror(errno)));
+ dbprintf(_("can't execute %s: %s\n"), service_path, strerror(errno));
pkt_init(&pkt_out, P_NAK,
- "ERROR execute access to \"%s\" denied\n",
+ _("ERROR execute access to \"%s\" denied\n"),
service_path);
goto send_pkt_out;
}
as = TAILQ_NEXT(as, tq)) {
if (strcmp(as->cmd, service_path) == 0 &&
strcmp(as->arguments, arguments) == 0) {
- dbprintf(("%s: %s %s: already running, acking req\n",
- debug_prefix_time(NULL), service, arguments));
+ dbprintf(_("%s %s: already running, acking req\n"),
+ service, arguments);
pkt_init_empty(&pkt_out, P_ACK);
goto send_pkt_out_no_delete;
}
* create a new service instance, and send the arguments down
* the request pipe.
*/
- dbprintf(("%s: creating new service: %s\n%s\n",
- debug_prefix_time(NULL), service, arguments));
- as = service_new(handle, service_path, arguments);
+ dbprintf(_("creating new service: %s\n%s\n"), service, arguments);
+ as = service_new(handle, service_path, services[i].service, arguments);
if (writebuf(as, arguments, strlen(arguments)) < 0) {
const char *errmsg = strerror(errno);
- dbprintf(("%s: error sending arguments to %s: %s\n",
- debug_prefix_time(NULL), service, errmsg));
- pkt_init(&pkt_out, P_NAK, "ERROR error writing arguments to %s: %s\n",
+ dbprintf(_("error sending arguments to %s: %s\n"), service, errmsg);
+ pkt_init(&pkt_out, P_NAK, _("ERROR error writing arguments to %s: %s\n"),
service, errmsg);
goto send_pkt_out;
}
return;
badreq:
- pkt_init(&pkt_out, P_NAK, "ERROR invalid REQ\n");
- dbprintf(("%s: received invalid %s packet:\n<<<<<\n%s>>>>>\n\n",
- debug_prefix_time(NULL), pkt_type2str(pkt->type), pkt->body));
+ pkt_init(&pkt_out, P_NAK, _("ERROR invalid REQ\n"));
+ dbprintf(_("received invalid %s packet:\n<<<<<\n%s>>>>>\n\n"),
+ pkt_type2str(pkt->type), pkt->body);
send_pkt_out:
if(as)
state_t curstate;
pkt_t nak;
-#ifdef AMANDAD_DEBUG
- dbprintf(("%s: state_machine: %p entering\n",
- debug_prefix_time(NULL), as));
-#endif
+ amandad_debug(1, _("state_machine: %p entering\n"), as);
for (;;) {
curstate = as->state;
-#ifdef AMANDAD_DEBUG
- dbprintf(("%s: state_machine: %p curstate=%s action=%s\n",
- debug_prefix_time(NULL), as,
- state2str(curstate), action2str(action)));
-#endif
+ amandad_debug(1, _("state_machine: %p curstate=%s action=%s\n"), as,
+ state2str(curstate), action2str(action));
retaction = (*curstate)(as, action, pkt);
-#ifdef AMANDAD_DEBUG
- dbprintf(("%s: state_machine: %p curstate=%s returned %s (nextstate=%s)\n",
- debug_prefix_time(NULL),
- as, state2str(curstate), action2str(retaction),
- state2str(as->state)));
-#endif
+ amandad_debug(1, _("state_machine: %p curstate=%s returned %s (nextstate=%s)\n"),
+ as, state2str(curstate), action2str(retaction),
+ state2str(as->state));
switch (retaction) {
/*
* State has queued up and is now blocking on input.
*/
case A_PENDING:
-#ifdef AMANDAD_DEBUG
- dbprintf(("%s: state_machine: %p leaving (A_PENDING)\n",
- debug_prefix_time(NULL), as));
-#endif
+ amandad_debug(1, _("state_machine: %p leaving (A_PENDING)\n"), as);
return;
/*
* Send a nak, and return.
*/
case A_SENDNAK:
- dbprintf(("%s: received unexpected %s packet\n",
- debug_prefix_time(NULL), pkt_type2str(pkt->type)));
- dbprintf(("<<<<<\n%s----\n\n", pkt->body));
- pkt_init(&nak, P_NAK, "ERROR unexpected packet type %s\n",
+ dbprintf(_("received unexpected %s packet\n"),
+ pkt_type2str(pkt->type));
+ dbprintf(_("<<<<<\n%s----\n\n"), pkt->body);
+ pkt_init(&nak, P_NAK, _("ERROR unexpected packet type %s\n"),
pkt_type2str(pkt->type));
do_sendpkt(as->security_handle, &nak);
amfree(nak.body);
security_recvpkt(as->security_handle, protocol_recv, as, -1);
-#ifdef AMANDAD_DEBUG
- dbprintf(("%s: state_machine: %p leaving (A_SENDNAK)\n",
- debug_prefix_time(NULL), as));
-#endif
+ amandad_debug(1, _("state_machine: %p leaving (A_SENDNAK)\n"), as);
return;
/*
* Service is done. Remove it and finish.
*/
case A_FINISH:
+ amandad_debug(1, _("state_machine: %p leaving (A_FINISH)\n"), as);
service_delete(as);
-#ifdef AMANDAD_DEBUG
- dbprintf(("%s: state_machine: %p leaving (A_FINISH)\n",
- debug_prefix_time(NULL), as));
-#endif
return;
default:
pkt_init_empty(&ack, P_ACK);
if (do_sendpkt(as->security_handle, &ack) < 0) {
- dbprintf(("%s: error sending ACK: %s\n",
- debug_prefix_time(NULL), security_geterror(as->security_handle)));
+ dbprintf(_("error sending ACK: %s\n"),
+ security_geterror(as->security_handle));
amfree(ack.body);
return (A_FINISH);
}
as->ev_repfd = event_register((event_id_t)as->repfd, EV_READFD, repfd_recv, as);
as->ev_reptimeout = event_register(REP_TIMEOUT, EV_TIME,
timeout_repfd, as);
+ as->errbuf = NULL;
+ as->ev_errfd = event_register((event_id_t)as->errfd, EV_READFD, errfd_recv, as);
security_recvpkt(as->security_handle, protocol_recv, as, -1);
return (A_PENDING);
}
action_t action,
pkt_t * pkt)
{
- ssize_t n;
- char *repbuf_temp;
+ ssize_t n;
+ char *repbuf_temp;
+ char *what;
+ char *msg;
+ int code = 0;
+ int pid;
+ amwait_t retstat;
/*
* We normally shouldn't receive any packets while waiting
* and go back and wait for more data.
*/
if (pkt->type == P_REQ) {
- dbprintf(("%s: received dup P_REQ packet, ACKing it\n",
- debug_prefix_time(NULL)));
+ dbprintf(_("received dup P_REQ packet, ACKing it\n"));
amfree(as->rep_pkt.body);
pkt_init_empty(&as->rep_pkt, P_ACK);
do_sendpkt(as->security_handle, &as->rep_pkt);
if (action == A_TIMEOUT) {
amfree(as->rep_pkt.body);
- pkt_init(&as->rep_pkt, P_NAK, "ERROR timeout on reply pipe\n");
- dbprintf(("%s: %s timed out waiting for REP data\n",
- debug_prefix_time(NULL), as->cmd));
+ pkt_init(&as->rep_pkt, P_NAK, _("ERROR timeout on reply pipe\n"));
+ dbprintf(_("%s timed out waiting for REP data\n"), as->cmd);
do_sendpkt(as->security_handle, &as->rep_pkt);
return (A_FINISH);
}
} while ((n < 0) && ((errno == EINTR) || (errno == EAGAIN)));
if (n < 0) {
const char *errstr = strerror(errno);
- dbprintf(("%s: read error on reply pipe: %s\n",
- debug_prefix_time(NULL), errstr));
+ dbprintf(_("read error on reply pipe: %s\n"), errstr);
amfree(as->rep_pkt.body);
- pkt_init(&as->rep_pkt, P_NAK, "ERROR read error on reply pipe: %s\n",
+ pkt_init(&as->rep_pkt, P_NAK, _("ERROR read error on reply pipe: %s\n"),
errstr);
do_sendpkt(as->security_handle, &as->rep_pkt);
return (A_FINISH);
}
+
+ /* If end of service, wait for process status */
+ if (n == 0) {
+ pid = waitpid(as->pid, &retstat, WNOHANG);
+ if (as->service == SERVICE_NOOP ||
+ as->service == SERVICE_SENDSIZE ||
+ as->service == SERVICE_SELFCHECK) {
+ int t = 0;
+ while (t<5 && pid == 0) {
+ sleep(1);
+ t++;
+ pid = waitpid(as->pid, &retstat, WNOHANG);
+ }
+ }
+
+ /* 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);
+ }
+ }
+
+ if (pid == 0)
+ pid = waitpid(as->pid, &retstat, WNOHANG);
+
+ if (pid > 0) {
+ what = NULL;
+ if (! WIFEXITED(retstat)) {
+ what = _("signal");
+ code = WTERMSIG(retstat);
+ } else if (WEXITSTATUS(retstat) != 0) {
+ what = _("code");
+ code = WEXITSTATUS(retstat);
+ }
+ if (what) {
+ dbprintf(_("service %s failed: pid %u exited with %s %d\n"),
+ (as->cmd)?as->cmd:_("??UNKONWN??"),
+ (unsigned)as->pid,
+ what, code);
+ msg = vstrallocf(
+ _("ERROR service %s failed: pid %u exited with %s %d\n"),
+ (as->cmd)?as->cmd:_("??UNKONWN??"), (unsigned)as->pid,
+ what, code);
+ if (as->repbufsize + strlen(msg) >= (as->bufsize - 1)) {
+ as->bufsize *= 2;
+ repbuf_temp = alloc(as->bufsize);
+ memcpy(repbuf_temp, as->repbuf, as->repbufsize + 1);
+ amfree(as->repbuf);
+ as->repbuf = repbuf_temp;
+ }
+ strcpy(as->repbuf + as->repbufsize, msg);
+ as->repbufsize += strlen(msg);
+ amfree(msg);
+ }
+ }
+ }
+
/*
* If we got some data, go back and wait for more, or EOF. Nul terminate
* the buffer first.
* We need to map these to security streams and pass them back
* to the amanda server. If the handle is -1, then we don't map.
*/
- repbuf = stralloc(as->repbuf);
+ if (strncmp_const(as->repbuf,"KENCRYPT\n") == 0) {
+ amandad_kencrypt = KENCRYPT_WILL_DO;
+ repbuf = stralloc(as->repbuf + 9);
+ } else {
+ repbuf = stralloc(as->repbuf);
+ }
amfree(as->rep_pkt.body);
pkt_init_empty(&as->rep_pkt, P_REP);
tok = strtok(repbuf, " ");
* state.
*/
as->state = s_sendrep;
- as->repretry = MAX_REP_RETRIES;
+ as->repretry = getconf_int(CNF_REP_TRIES);
amfree(repbuf);
return (A_CONTINUE);
}
as->state = s_sendrep;
return (A_CONTINUE);
}
- dbprintf(("%s: timeout waiting for ACK for our REP\n",
- debug_prefix_time(NULL)));
+ dbprintf(_("timeout waiting for ACK for our REP\n"));
return (A_FINISH);
}
-#ifdef AMANDAD_DEBUG
- dbprintf(("%s: received ACK, now opening streams\n",
- debug_prefix_time(NULL)));
-#endif
+ amandad_debug(1, _("received ACK, now opening streams\n"));
assert(action == A_RECVPKT);
if (pkt->type == P_REQ) {
- dbprintf(("%s: received dup P_REQ packet, resending REP\n",
- debug_prefix_time(NULL)));
+ dbprintf(_("received dup P_REQ packet, resending REP\n"));
as->state = s_sendrep;
return (A_CONTINUE);
}
if (pkt->type != P_ACK)
return (A_SENDNAK);
+ if (amandad_kencrypt == KENCRYPT_WILL_DO) {
+ amandad_kencrypt = KENCRYPT_YES;
+ }
+
/*
* Got the ack, now open the pipes
*/
if (dh->netfd == NULL)
continue;
if (security_stream_accept(dh->netfd) < 0) {
- dbprintf(("%s: stream %ld accept failed: %s\n",
- debug_prefix_time(NULL),
- dh - &as->data[0], security_geterror(as->security_handle)));
+ dbprintf(_("stream %td accept failed: %s\n"),
+ dh - &as->data[0], security_geterror(as->security_handle));
security_stream_close(dh->netfd);
dh->netfd = NULL;
+ continue;
+ }
+
+ /* setup an event for reads from it. As a special case, don't start
+ * listening on as->data[0] until we read some data on another fd, if
+ * the service is sendbackup. This ensures that we send a MESG or
+ * INDEX token before any DATA tokens, as dumper assumes. This is a
+ * hack, if that wasn't already obvious! */
+ if (dh != &as->data[0] || as->service != SERVICE_SENDBACKUP) {
+ dh->ev_read = event_register((event_id_t)dh->fd_read, EV_READFD,
+ process_readnetfd, dh);
+ } else {
+ amandad_debug(1, "Skipping registration of sendbackup's data FD\n");
}
- /* setup an event for reads from it */
- dh->ev_read = event_register((event_id_t)dh->fd_read, EV_READFD,
- process_readnetfd, dh);
security_stream_read(dh->netfd, process_writenetfd, dh);
* If no pipes are open, then we're done. Otherwise, just start running.
* The event handlers on all of the pipes will take it from here.
*/
-#ifdef AMANDAD_DEBUG
- dbprintf(("%s: at end of s_ackwait, npipes is %d\n",
- debug_prefix_time(NULL), npipes));
-#endif
+ amandad_debug(1, _("at end of s_ackwait, npipes is %d\n"), npipes);
if (npipes == 0)
return (A_FINISH);
else {
state_machine(as, A_RECVREP, NULL);
}
+/*
+ * Called when a errfd has received data
+ */
+static void
+errfd_recv(
+ void * cookie)
+{
+ struct active_service *as = cookie;
+ char buf[32769];
+ int n;
+ char *r;
+
+ assert(as != NULL);
+ assert(as->ev_errfd != NULL);
+
+ n = read(as->errfd, &buf, 32768);
+ /* merge buffer */
+ if (n > 0) {
+ /* Terminate it with '\0' */
+ buf[n+1] = '\0';
+
+ if (as->errbuf) {
+ as->errbuf = vstrextend(&as->errbuf, buf, NULL);
+ } else {
+ as->errbuf = stralloc(buf);
+ }
+ } else if (n == 0) {
+ event_release(as->ev_errfd);
+ as->ev_errfd = NULL;
+ } else { /* n < 0 */
+ event_release(as->ev_errfd);
+ as->ev_errfd = NULL;
+ g_snprintf(buf, 32768,
+ "error reading stderr or service: %s\n", strerror(errno));
+ }
+
+ /* for each line terminate by '\n' */
+ while (as->errbuf != NULL && (r = strchr(as->errbuf, '\n')) != NULL) {
+ char *s;
+
+ *r = '\0';
+ s = vstrallocf("ERROR service %s: %s\n",
+ services[as->service].name, as->errbuf);
+
+ /* Add to repbuf, error message will be in the REP packet if it
+ * is not already sent
+ */
+ n = strlen(s);
+ if (as->bufsize == 0) {
+ as->bufsize = NETWORK_BLOCK_BYTES;
+ as->repbuf = alloc(as->bufsize);
+ }
+ while (as->bufsize < as->repbufsize + n) {
+ char *repbuf_temp;
+ as->bufsize *= 2;
+ repbuf_temp = alloc(as->bufsize);
+ memcpy(repbuf_temp, as->repbuf, as->repbufsize + 1);
+ amfree(as->repbuf);
+ as->repbuf = repbuf_temp;
+ }
+ memcpy(as->repbuf + as->repbufsize, s, n);
+ as->repbufsize += n;
+
+ dbprintf("%s", s);
+
+ /* remove first line from buffer */
+ r++;
+ s = stralloc(r);
+ amfree(as->errbuf);
+ as->errbuf = s;
+ }
+}
+
/*
* Called when a repfd has timed out
*/
switch (status) {
case S_OK:
- dbprintf(("%s: received %s pkt:\n<<<<<\n%s>>>>>\n",
- debug_prefix_time(NULL), pkt_type2str(pkt->type), pkt->body));
+ dbprintf(_("received %s pkt:\n<<<<<\n%s>>>>>\n"),
+ pkt_type2str(pkt->type), pkt->body);
state_machine(as, A_RECVPKT, pkt);
break;
case S_TIMEOUT:
- dbprintf(("%s: timeout\n", debug_prefix_time(NULL)));
+ dbprintf(_("timeout\n"));
state_machine(as, A_TIMEOUT, NULL);
break;
case S_ERROR:
- dbprintf(("%s: receive error: %s\n",
- debug_prefix_time(NULL), security_geterror(as->security_handle)));
+ dbprintf(_("receive error: %s\n"),
+ security_geterror(as->security_handle));
break;
}
}
* Process has died.
*/
if (n < 0) {
- pkt_init(&nak, P_NAK, "A ERROR data descriptor %d broken: %s\n",
+ pkt_init(&nak, P_NAK, _("A ERROR data descriptor %d broken: %s\n"),
dh->fd_read, strerror(errno));
goto sendnak;
}
service_delete(as);
return;
}
+
+ /* Handle the special case of recognizing "sendbackup info end"
+ * from sendbackup's MESG fd */
+ if (as->service == SERVICE_SENDBACKUP && !as->seen_info_end && dh == &as->data[1]) {
+ /* make a buffer containing the combined data from info_end_buf
+ * and what we've read this time, and search it for info_end_strj
+ * This includes a NULL byte for strstr's sanity. */
+ char *combined_buf = malloc(INFO_END_LEN + n + 1);
+ memcpy(combined_buf, as->info_end_buf, INFO_END_LEN);
+ memcpy(combined_buf+INFO_END_LEN, as->databuf, n);
+ combined_buf[INFO_END_LEN+n] = '\0';
+
+ as->seen_info_end = (strstr(combined_buf, info_end_str) != NULL);
+
+ /* fill info_end_buf from the tail end of combined_buf */
+ memcpy(as->info_end_buf, combined_buf + n, INFO_END_LEN);
+
+ /* if we did see info_end_str, start reading the data fd (fd 0) */
+ if (as->seen_info_end) {
+ struct datafd_handle *dh = &as->data[0];
+ amandad_debug(1, "Opening datafd to sendbackup (delayed until sendbackup sent header info)\n");
+ dh->ev_read = event_register((event_id_t)dh->fd_read, EV_READFD,
+ process_readnetfd, dh);
+ } else {
+ amandad_debug(1, "sendbackup header info still not complete\n");
+ }
+ }
+
if (security_stream_write(dh->netfd, as->databuf, (size_t)n) < 0) {
/* stream has croaked */
- pkt_init(&nak, P_NAK, "ERROR write error on stream %d: %s\n",
+ pkt_init(&nak, P_NAK, _("ERROR write error on stream %d: %s\n"),
security_stream_id(dh->netfd),
security_stream_geterror(dh->netfd));
goto sendnak;
dh = cookie;
if (dh->fd_write <= 0) {
- dbprintf(("%s: process_writenetfd: dh->fd_write <= 0\n",
- debug_prefix_time(NULL)));
+ dbprintf(_("process_writenetfd: dh->fd_write <= 0\n"));
} else if (size > 0) {
- fullwrite(dh->fd_write, buf, (size_t)size);
+ full_write(dh->fd_write, buf, (size_t)size);
security_stream_read(dh->netfd, process_writenetfd, dh);
}
else {
/* allocate a stream from the security layer and return */
dh->netfd = security_stream_server(as->security_handle);
if (dh->netfd == NULL) {
- dbprintf(("%s: couldn't open stream to server: %s\n",
- debug_prefix_time(NULL), security_geterror(as->security_handle)));
+ dbprintf(_("couldn't open stream to server: %s\n"),
+ security_geterror(as->security_handle));
return (-1);
}
service_new(
security_handle_t * security_handle,
const char * cmd,
+ service_t service,
const char * arguments)
{
int i;
- int data_read[DATA_FD_COUNT + 1][2];
- int data_write[DATA_FD_COUNT + 1][2];
+ int data_read[DATA_FD_COUNT + 2][2];
+ int data_write[DATA_FD_COUNT + 2][2];
struct active_service *as;
pid_t pid;
int newfd;
assert(arguments != NULL);
/* a plethora of pipes */
+ /* data_read[0] : stdin
+ * data_write[0] : stdout
+ * data_read[1], data_write[1] : first stream
+ * data_read[2], data_write[2] : second stream
+ * data_read[3], data_write[3] : third stream
+ * data_write[4] : stderr
+ */
for (i = 0; i < DATA_FD_COUNT + 1; i++) {
if (pipe(data_read[i]) < 0) {
- error("pipe: %s\n", strerror(errno));
+ error(_("pipe: %s\n"), strerror(errno));
/*NOTREACHED*/
}
if (pipe(data_write[i]) < 0) {
- error("pipe: %s\n", strerror(errno));
+ error(_("pipe: %s\n"), strerror(errno));
/*NOTREACHED*/
}
}
+ if (pipe(data_write[STDERR_PIPE]) < 0) {
+ error(_("pipe: %s\n"), strerror(errno));
+ /*NOTREACHED*/
+ }
switch(pid = fork()) {
case -1:
- error("could not fork service %s: %s\n", cmd, strerror(errno));
+ error(_("could not fork service %s: %s\n"), cmd, strerror(errno));
/*NOTREACHED*/
default:
/*
* The parent. Close the far ends of our pipes and return.
*/
- as = alloc(SIZEOF(*as));
+ as = g_new0(struct active_service, 1);
as->cmd = stralloc(cmd);
as->arguments = stralloc(arguments);
as->security_handle = security_handle;
as->state = NULL;
+ as->service = service;
as->pid = pid;
as->send_partial_reply = 0;
- if(strcmp(cmd+(strlen(cmd)-8), "sendsize") == 0) {
+ as->seen_info_end = FALSE;
+ /* fill in info_end_buf with non-null characters */
+ memset(as->info_end_buf, '-', sizeof(as->info_end_buf));
+ if(service == SERVICE_SENDSIZE) {
g_option_t *g_options;
char *option_str, *p;
as->repretry = 0;
as->rep_pkt.body = NULL;
+ /*
+ * read from the stderr pipe
+ */
+ as->errfd = data_write[STDERR_PIPE][0];
+ aclose(data_write[STDERR_PIPE][1]);
+ as->ev_errfd = NULL;
+ as->errbuf = NULL;
+
/*
* read from the rest of the general-use pipes
* (netfds are opened as the client requests them)
* The child. Put our pipes in their advertised locations
* and start up.
*/
-#ifdef FORCE_USERID
- seteuid((uid_t)0);
- setuid(client_uid);
-#endif
/*
* The data stream is stdin in the new process
*/
if (dup2(data_read[0][0], 0) < 0) {
- error("dup %d to %d failed: %s\n", data_read[0][0], 0,
+ error(_("dup %d to %d failed: %s\n"), data_read[0][0], 0,
strerror(errno));
/*NOTREACHED*/
}
* The reply stream is stdout
*/
if (dup2(data_write[0][1], 1) < 0) {
- error("dup %d to %d failed: %s\n", data_write[0][1], 1,
+ error(_("dup %d to %d failed: %s\n"), data_write[0][1], 1,
strerror(errno));
}
aclose(data_write[0][0]);
data_read[i + 1][1] <= DATA_FD_OFFSET + DATA_FD_COUNT*2 - 1) {
newfd = dup(data_read[i + 1][1]);
if(newfd == -1)
- error("Can't dup out off DATA_FD range");
+ error(_("Can't dup out off DATA_FD range"));
data_read[i + 1][1] = newfd;
}
while(data_write[i + 1][0] >= DATA_FD_OFFSET &&
data_write[i + 1][0] <= DATA_FD_OFFSET + DATA_FD_COUNT*2 - 1) {
newfd = dup(data_write[i + 1][0]);
if(newfd == -1)
- error("Can't dup out off DATA_FD range");
+ error(_("Can't dup out off DATA_FD range"));
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);
*/
for (i = 0; i < DATA_FD_COUNT; i++) {
if (dup2(data_read[i + 1][1], i*2 + DATA_FD_OFFSET) < 0) {
- error("dup %d to %d failed: %s\n", data_read[i + 1][1],
+ error(_("dup %d to %d failed: %s\n"), data_read[i + 1][1],
i + DATA_FD_OFFSET, strerror(errno));
}
aclose(data_read[i + 1][1]);
if (dup2(data_write[i + 1][0], i*2 + 1 + DATA_FD_OFFSET) < 0) {
- error("dup %d to %d failed: %s\n", data_write[i + 1][0],
+ error(_("dup %d to %d failed: %s\n"), data_write[i + 1][0],
i + DATA_FD_OFFSET, strerror(errno));
}
aclose(data_write[i + 1][0]);
}
/* close all unneeded fd */
+ close(STDERR_FILENO);
+ dup2(data_write[STDERR_PIPE][1], 2);
+ aclose(data_write[STDERR_PIPE][0]);
+ aclose(data_write[STDERR_PIPE][1]);
safe_fd(DATA_FD_OFFSET, DATA_FD_COUNT*2);
- close(2);
execle(cmd, cmd, "amandad", auth, (char *)NULL, safe_env());
- error("could not exec service %s: %s\n", cmd, strerror(errno));
+ error(_("could not exec service %s: %s\n"), cmd, strerror(errno));
/*NOTREACHED*/
}
return NULL;
int i;
struct datafd_handle *dh;
-#ifdef AMANDAD_DEBUG
- dbprintf(("%s: closing service: %s\n",
- debug_prefix_time(NULL), (as->cmd)?as->cmd:"??UNKONWN??"));
-#endif
+ amandad_debug(1, _("closing service: %s\n"),
+ (as->cmd)?as->cmd:_("??UNKONWN??"));
assert(as != NULL);
size_t size)
{
pid_t pid;
- ssize_t writesize;
+ size_t writesize;
switch (pid=fork()) {
case -1:
case 0: /* this is the child */
close(as->repfd);
- writesize = fullwrite(as->reqfd, bufp, size);
- exit(writesize != (ssize_t)size);
+ writesize = full_write(as->reqfd, bufp, size);
+ exit(writesize != size);
/* NOTREACHED */
}
return -1;
security_handle_t * handle,
pkt_t * pkt)
{
- dbprintf(("%s: sending %s pkt:\n<<<<<\n%s>>>>>\n",
- debug_prefix_time(NULL), pkt_type2str(pkt->type), pkt->body));
+ dbprintf(_("sending %s pkt:\n<<<<<\n%s>>>>>\n"),
+ pkt_type2str(pkt->type), pkt->body);
if (handle)
return security_sendpkt(handle, pkt);
else
return 1;
}
-#ifdef AMANDAD_DEBUG
/*
* Convert a state into a string
*/
for (i = 0; i < (int)(sizeof(states) / sizeof(states[0])); i++)
if (state == states[i].state)
return (states[i].str);
- return ("INVALID STATE");
+ return (_("INVALID STATE"));
}
/*
for (i = 0; i < (int)(sizeof(actions) / sizeof(actions[0])); i++)
if (action == actions[i].action)
return (actions[i].str);
- return ("UNKNOWN ACTION");
+ return (_("UNKNOWN ACTION"));
}
-#endif /* AMANDAD_DEBUG */
+
+static char *
+amandad_get_security_conf(
+ char * string,
+ void * arg)
+{
+ (void)arg; /* Quiet unused parameter warning */
+
+ if (!string || !*string)
+ return(NULL);
+
+ if (strcmp(string, "kencrypt")==0) {
+ if (amandad_kencrypt == KENCRYPT_YES)
+ return ("yes");
+ else
+ return (NULL);
+ }
+ return(NULL);
+}
+