X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=amandad-src%2Famandad.c;h=cfb7a47948ec7edeb04786fe85e9058f389a468c;hb=2627875b7d18858bc1f9f7652811e4d8c15a23eb;hp=772cd39df3bc5d66d69e15a35441165a0cebe217;hpb=12179dea039515c06168c0037d048566a3f623de;p=debian%2Famanda diff --git a/amandad-src/amandad.c b/amandad-src/amandad.c index 772cd39..cfb7a47 100644 --- a/amandad-src/amandad.c +++ b/amandad-src/amandad.c @@ -32,8 +32,6 @@ * master side */ -/*#define AMANDAD_DEBUG*/ - #include "amanda.h" #include "amandad.h" #include "clock.h" @@ -45,10 +43,17 @@ #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 @@ -63,6 +68,37 @@ typedef enum { A_START, A_RECVPKT, A_RECVREP, A_PENDING, A_FINISH, A_CONTINUE, 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. * @@ -72,6 +108,7 @@ typedef action_t (*state_t)(struct active_service *, action_t, pkt_t *); * 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 */ @@ -80,13 +117,19 @@ struct active_service { 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. @@ -103,22 +146,6 @@ struct active_service { 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. */ @@ -129,20 +156,10 @@ static struct { 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); @@ -158,41 +175,20 @@ static action_t s_sendrep(struct active_service *, action_t, pkt_t *); 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( @@ -204,13 +200,21 @@ 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(); @@ -229,51 +233,35 @@ main( 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; @@ -297,7 +285,7 @@ main( 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; @@ -312,35 +300,47 @@ main( 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*/ } @@ -349,41 +349,50 @@ main( * 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 */ @@ -406,8 +415,7 @@ main( 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; @@ -422,37 +430,54 @@ main( 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) { + error(_("Amanda 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")); + } + } + + /* 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 @@ -498,15 +523,6 @@ exit_check( 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); } @@ -524,6 +540,7 @@ protocol_accept( struct active_service *as; char *pktbody, *tok, *service, *arguments; char *service_path = NULL; + GSList *errlist = NULL; int i; pkt_out.body = NULL; @@ -535,12 +552,37 @@ protocol_accept( 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); @@ -548,15 +590,15 @@ protocol_accept( 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; } @@ -592,18 +634,16 @@ protocol_accept( 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; } @@ -613,9 +653,9 @@ protocol_accept( 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)); - pkt_init(&pkt_out, P_ACK, ""); + dbprintf(_("%s %s: already running, acking req\n"), + service, arguments); + pkt_init_empty(&pkt_out, P_ACK); goto send_pkt_out_no_delete; } } @@ -624,14 +664,12 @@ protocol_accept( * 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; } @@ -651,9 +689,9 @@ protocol_accept( 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) @@ -682,34 +720,22 @@ state_machine( 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; /* @@ -723,28 +749,23 @@ state_machine( * 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); -#ifdef AMANDAD_DEBUG - dbprintf(("%s: state_machine: %p leaving (A_SENDNAK)\n", - debug_prefix_time(NULL), as)); -#endif + security_recvpkt(as->security_handle, protocol_recv, as, -1); + 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: @@ -770,10 +791,10 @@ s_sendack( (void)action; /* Quiet unused parameter warning */ (void)pkt; /* Quiet unused parameter warning */ - pkt_init(&ack, P_ACK, ""); + 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); } @@ -791,6 +812,8 @@ s_sendack( 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); } @@ -806,8 +829,13 @@ s_repwait( 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 @@ -820,11 +848,11 @@ s_repwait( * 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(&as->rep_pkt, P_ACK, ""); + pkt_init_empty(&as->rep_pkt, P_ACK); do_sendpkt(as->security_handle, &as->rep_pkt); + security_recvpkt(as->security_handle, protocol_recv, as, -1); return (A_PENDING); } /* something unexpected. Nak it */ @@ -833,9 +861,8 @@ s_repwait( 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); } @@ -852,14 +879,78 @@ s_repwait( } 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. @@ -879,7 +970,7 @@ s_repwait( pkt_init(&as->rep_pkt, P_PREP, "%s", as->repbuf); do_sendpkt(as->security_handle, &as->rep_pkt); amfree(as->rep_pkt.body); - pkt_init(&as->rep_pkt, P_REP, ""); + pkt_init_empty(&as->rep_pkt, P_REP); } return (A_PENDING); @@ -932,9 +1023,14 @@ s_processrep( * 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(&as->rep_pkt, P_REP, ""); + pkt_init_empty(&as->rep_pkt, P_REP); tok = strtok(repbuf, " "); if (tok == NULL) goto error; @@ -978,7 +1074,7 @@ error: * state. */ as->state = s_sendrep; - as->repretry = MAX_REP_RETRIES; + as->repretry = getconf_int(CNF_REP_TRIES); amfree(repbuf); return (A_CONTINUE); } @@ -1025,20 +1121,15 @@ s_ackwait( 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); } @@ -1046,6 +1137,10 @@ s_ackwait( 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 */ @@ -1053,15 +1148,24 @@ s_ackwait( if (dh->netfd == NULL) continue; if (security_stream_accept(dh->netfd) < 0) { - dbprintf(("%s: stream %d 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); @@ -1089,10 +1193,7 @@ s_ackwait( * 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 { @@ -1117,6 +1218,79 @@ repfd_recv( 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 = index(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 */ @@ -1147,17 +1321,17 @@ protocol_recv( 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; } } @@ -1185,7 +1359,7 @@ process_readnetfd( * 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; } @@ -1207,9 +1381,37 @@ process_readnetfd( 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; @@ -1238,10 +1440,9 @@ process_writenetfd( 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 { @@ -1281,8 +1482,8 @@ allocstream( /* 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); } @@ -1300,11 +1501,12 @@ static struct active_service * 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; @@ -1314,33 +1516,48 @@ service_new( 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; @@ -1372,6 +1589,14 @@ service_new( 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) @@ -1398,16 +1623,12 @@ service_new( * 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*/ } @@ -1418,12 +1639,17 @@ service_new( * 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]); aclose(data_write[0][1]); + for (i = 0; i < DATA_FD_COUNT; i++) { + aclose(data_read[i + 1][0]); + aclose(data_write[i + 1][1]); + } + /* * Make sure they are not open in the range DATA_FD_OFFSET to * DATA_FD_OFFSET + DATA_FD_COUNT*2 - 1 @@ -1433,18 +1659,18 @@ service_new( 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; } } - for (i = 0; i < DATA_FD_COUNT; i++) + for (i = 0; i < DATA_FD_COUNT*2; i++) close(DATA_FD_OFFSET + i); /* @@ -1453,26 +1679,27 @@ service_new( */ 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][0]); 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]); - aclose(data_write[i + 1][1]); } /* 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; @@ -1488,10 +1715,8 @@ service_delete( 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); @@ -1560,7 +1785,7 @@ writebuf( size_t size) { pid_t pid; - ssize_t writesize; + size_t writesize; switch (pid=fork()) { case -1: @@ -1572,8 +1797,8 @@ writebuf( 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; @@ -1584,12 +1809,14 @@ do_sendpkt( 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)); - return security_sendpkt(handle, pkt); + 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 */ @@ -1614,7 +1841,7 @@ state2str( 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")); } /* @@ -1644,6 +1871,25 @@ action2str( 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); +} +