Imported Upstream version 3.3.2
[debian/amanda] / amandad-src / amandad.c
index cfb7a47948ec7edeb04786fe85e9058f389a468c..11009d854ef405fbbfd443427c61e2bfba83e9ab 100644 (file)
@@ -39,7 +39,6 @@
 #include "amfeatures.h"
 #include "packet.h"
 #include "version.h"
-#include "queue.h"
 #include "security.h"
 #include "stream.h"
 #include "util.h"
@@ -82,7 +81,8 @@ typedef enum {
     SERVICE_SENDBACKUP,
     SERVICE_SELFCHECK,
     SERVICE_AMINDEXD,
-    SERVICE_AMIDXTAPED
+    SERVICE_AMIDXTAPED,
+    SERVICE_AMDUMPD
 } service_t;
 
 static struct services {
@@ -95,7 +95,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,18 +144,12 @@ 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 int exit_on_qlength = 1;
@@ -175,6 +170,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 +214,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 +244,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 +265,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 +281,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 +292,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 +436,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*/
        }
     }
@@ -443,23 +455,32 @@ main(
        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]);
     }
@@ -514,7 +535,7 @@ exit_check(
     /*
      * If things are still running, then don't exit.
      */
-    if (serviceq.qlength > 0)
+    if (g_slist_length(serviceq) > 0)
        return;
 
     /*
@@ -537,11 +558,13 @@ 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;
 
@@ -553,7 +576,7 @@ protocol_accept(
     }
 
     /*
-     * 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.
      */
@@ -578,6 +601,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.
      */
@@ -639,7 +666,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,
@@ -649,8 +676,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"),
@@ -901,20 +928,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);
@@ -1147,6 +1161,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));
@@ -1218,6 +1233,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
  */
@@ -1255,7 +1292,7 @@ errfd_recv(
     }
 
     /* for each line terminate by '\n' */
-    while (as->errbuf != NULL  && (r = index(as->errbuf, '\n')) != NULL) {
+    while (as->errbuf != NULL  && (r = strchr(as->errbuf, '\n')) != NULL) {
        char *s;
 
        *r = '\0';
@@ -1397,6 +1434,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) {
@@ -1464,6 +1502,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);
@@ -1510,6 +1551,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);
@@ -1614,8 +1657,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:
@@ -1624,6 +1666,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
         */
@@ -1670,6 +1722,21 @@ service_new(
                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);
 
@@ -1698,7 +1765,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*/
     }
@@ -1713,6 +1780,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"),
@@ -1730,11 +1799,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];
@@ -1754,13 +1829,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);
@@ -1768,7 +1855,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);
     }