X-Git-Url: https://git.gag.com/?a=blobdiff_plain;f=amandad-src%2Famandad.c;h=d864c3fa1bc7ca511ac93374fbccf9004a8d87f6;hb=HEAD;hp=8d9b3d6b680133603cc551655523d55d9fde2b91;hpb=e9de482962ca61612054c6e0382814b04e416129;p=debian%2Famanda diff --git a/amandad-src/amandad.c b/amandad-src/amandad.c index 8d9b3d6..d864c3f 100644 --- a/amandad-src/amandad.c +++ b/amandad-src/amandad.c @@ -1,6 +1,7 @@ /* * 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. * All Rights Reserved. * * Permission to use, copy, modify, distribute, and sell this software and its @@ -39,7 +40,6 @@ #include "amfeatures.h" #include "packet.h" #include "version.h" -#include "queue.h" #include "security.h" #include "stream.h" #include "util.h" @@ -82,7 +82,8 @@ typedef enum { SERVICE_SENDBACKUP, SERVICE_SELFCHECK, SERVICE_AMINDEXD, - SERVICE_AMIDXTAPED + SERVICE_AMIDXTAPED, + SERVICE_AMDUMPD } service_t; static struct services { @@ -95,7 +96,8 @@ 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])) @@ -143,20 +145,14 @@ struct active_service { 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 event_handle_t *exit_event; static int exit_on_qlength = 1; static char *auth = NULL; static kencrypt_type amandad_kencrypt = KENCRYPT_NONE; @@ -175,6 +171,7 @@ 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 process_errfd(void *cookie); static void errfd_recv(void *); static void timeout_repfd(void *); static void protocol_recv(void *, pkt_t *, security_status_t); @@ -218,6 +215,17 @@ main( 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) @@ -237,6 +245,11 @@ main( /*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); @@ -253,7 +266,8 @@ main( 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 @@ -268,19 +282,10 @@ main( 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]; @@ -288,6 +293,14 @@ main( 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; } @@ -424,13 +437,13 @@ main( } /* - * 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*/ } } @@ -439,17 +452,18 @@ main( strcasecmp(auth, "ssh") == 0 || strcasecmp(auth, "local") == 0 || strcasecmp(auth, "bsdtcp") == 0) { - wait_30s = 0; exit_on_qlength = 1; } - if (getuid() == 0) { +#ifndef SINGLE_USERID + if (geteuid() == 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); @@ -460,13 +474,13 @@ main( 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]); } @@ -490,8 +504,7 @@ main( * Schedule an event that will try to exit every 30 seconds if there * are no requests outstanding. */ - if(wait_30s) - (void)event_register((event_id_t)30, EV_TIME, exit_check, &no_exit); + exit_event = event_register((event_id_t)30, EV_TIME, exit_check, &no_exit); /* * Call event_loop() with an arg of 0, telling it to block until all @@ -521,7 +534,7 @@ exit_check( /* * If things are still running, then don't exit. */ - if (serviceq.qlength > 0) + if (g_slist_length(serviceq) > 0) return; /* @@ -530,6 +543,7 @@ exit_check( if (no_exit) return; + g_debug("timeout exit"); dbclose(); exit(0); } @@ -544,23 +558,30 @@ protocol_accept( pkt_t * pkt) { pkt_t pkt_out; + GSList *iter; struct active_service *as; char *pktbody, *tok, *service, *arguments; char *service_path = NULL; GSList *errlist = NULL; int i; + char *peer_name; pkt_out.body = NULL; /* * If handle is NULL, then the connection is closed. */ - if(handle == NULL) { + if (handle == NULL) { + if (exit_on_qlength && exit_event) { + /* remove the timeout, we will exit once the service terminate */ + event_release(exit_event); + exit_event = NULL; + } return; } /* - * 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. */ @@ -585,6 +606,10 @@ protocol_accept( return; } + peer_name = security_get_authenticated_peer_name(handle); + g_debug("authenticated peer name is '%s'", peer_name); + amfree(peer_name); + /* * If pkt is NULL, then there was a problem with the new connection. */ @@ -646,7 +671,7 @@ protocol_accept( 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, @@ -656,8 +681,8 @@ protocol_accept( } /* 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"), @@ -908,20 +933,7 @@ s_repwait( } } - /* 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); @@ -1154,6 +1166,7 @@ s_ackwait( 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)); @@ -1225,6 +1238,28 @@ repfd_recv( 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 */ @@ -1404,6 +1439,7 @@ process_readnetfd( /* 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) { @@ -1450,9 +1486,7 @@ process_writenetfd( dbprintf(_("process_writenetfd: dh->fd_write <= 0\n")); } else if (size > 0) { full_write(dh->fd_write, buf, (size_t)size); - security_stream_read(dh->netfd, process_writenetfd, dh); - } - else { + } else { aclose(dh->fd_write); } } @@ -1471,6 +1505,9 @@ allocstream( { 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); @@ -1517,6 +1554,8 @@ service_new( 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); @@ -1621,8 +1660,7 @@ service_new( /* 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: @@ -1631,6 +1669,16 @@ service_new( * 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 */ @@ -1720,7 +1768,7 @@ service_new( 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*/ } @@ -1735,6 +1783,8 @@ service_delete( struct active_service * as) { int i; + int count; + pid_t pid; struct datafd_handle *dh; amandad_debug(1, _("closing service: %s\n"), @@ -1752,11 +1802,17 @@ service_delete( 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]; @@ -1776,13 +1832,25 @@ service_delete( 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); @@ -1790,7 +1858,7 @@ service_delete( 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); }