You are viewing a plain text version of this content. The canonical link for it is here.
Posted to cvs@httpd.apache.org by wr...@apache.org on 2009/05/29 07:49:08 UTC

svn commit: r779848 - in /httpd/mod_ftp/trunk/modules/ftp: ftp_internal.h ftp_lowportd.c

Author: wrowe
Date: Fri May 29 05:49:07 2009
New Revision: 779848

URL: http://svn.apache.org/viewvc?rev=779848&view=rev
Log:
Add a low numbered port daemon, mod ftp lowportd, which exists for the server
generation and will serve properly formed requests as bound sockets through
a unix domain pipe.  We trust sys/un.h to deteremine availability.

This likely needs a bit of refactoring to accomodate msg_accrights if a few
platforms still use this mechanism, however I'm too tired to bother at the
moment and still steamed at deleting this source when at 90% completion.
So it hits subversion in its working form for linux.

Mostly derived from code at mod_cgid, look there if something 'interesting'
is discovered, since the two code bases likely share any issues.


Added:
    httpd/mod_ftp/trunk/modules/ftp/ftp_lowportd.c   (with props)
Modified:
    httpd/mod_ftp/trunk/modules/ftp/ftp_internal.h

Modified: httpd/mod_ftp/trunk/modules/ftp/ftp_internal.h
URL: http://svn.apache.org/viewvc/httpd/mod_ftp/trunk/modules/ftp/ftp_internal.h?rev=779848&r1=779847&r2=779848&view=diff
==============================================================================
--- httpd/mod_ftp/trunk/modules/ftp/ftp_internal.h (original)
+++ httpd/mod_ftp/trunk/modules/ftp/ftp_internal.h Fri May 29 05:49:07 2009
@@ -347,4 +347,20 @@
     struct ftp_cmd_entry *next;       /* Pointer to the next handler */
 };
 
+/* FTP low-numbered-port allocation daemon
+ *
+ * ftp_lowportd.c
+ */
+/* Lone onfiguration option */
+const char *lowportd_set_socket(cmd_parms *cmd, void *dummy, const char *arg);
+
+#if APR_HAVE_SYS_UN_H
+/* Initialization */
+int lowportd_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp);
+int lowportd_post_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp,
+                         server_rec *main_server);
+apr_status_t ftp_request_lowport(apr_socket_t **sock, request_rec *r,
+                                 apr_sockaddr_t *sa, apr_pool_t *p);
+#endif
+
 #endif

Added: httpd/mod_ftp/trunk/modules/ftp/ftp_lowportd.c
URL: http://svn.apache.org/viewvc/httpd/mod_ftp/trunk/modules/ftp/ftp_lowportd.c?rev=779848&view=auto
==============================================================================
--- httpd/mod_ftp/trunk/modules/ftp/ftp_lowportd.c (added)
+++ httpd/mod_ftp/trunk/modules/ftp/ftp_lowportd.c Fri May 29 05:49:07 2009
@@ -0,0 +1,608 @@
+/* Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#include "mod_ftp.h"
+#include "ftp_internal.h"
+#include "apr_signal.h"
+#include "ap_listen.h"
+#include "ap_mpm.h"
+#include "unixd.h"
+
+/* The module is enabled by the presence of unix domain sockets */
+#if APR_HAVE_SYS_UN_H
+#include <sys/un.h>  /* for sockaddr_un */
+
+#if APR_HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if APR_HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if APR_HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+
+typedef struct lowportd_req_t {
+    pid_t       ppid;
+    server_rec *server;
+#if APR_HAVE_IPV6
+    struct sockaddr_in6 sockaddr;
+#else
+    struct sockaddr_in sockaddr;
+#endif
+    size_t sockaddr_len;
+} lowportd_req_t;
+
+static apr_pool_t *pdaemon = NULL;
+static const char *sockname;
+static struct sockaddr_un *daemon_addr;
+static apr_socklen_t daemon_addr_len;
+static pid_t parent_pid;
+static pid_t daemon_pid;
+static int daemon_should_exit = 0;
+
+/* The APR other-child API doesn't tell us how the daemon exited
+ * (SIGSEGV vs. exit(1)).  The other-child maintenance function
+ * needs to decide whether to restart the daemon after a failure
+ * based on whether or not it exited due to a fatal startup error
+ * or something that happened at steady-state.  This exit status
+ * is unlikely to collide with exit signals.
+ */
+#define DAEMON_STARTUP_ERROR 254
+
+/* DEFAULT_CGID_LISTENBACKLOG controls the max depth on the unix socket's
+ * pending connection queue.  If a bunch of cgi requests arrive at about
+ * the same time, connections from httpd threads/processes will back up
+ * in the queue while the cgid process slowly forks off a child to process
+ * each connection on the unix socket.  If the queue is too short, the
+ * httpd process will get ECONNREFUSED when trying to connect.
+ */
+#ifndef DEFAULT_CGID_LISTENBACKLOG
+#define DEFAULT_CGID_LISTENBACKLOG 100
+#endif
+
+/* DEFAULT_CONNECT_ATTEMPTS controls how many times we'll try to connect
+ * to the cgi daemon from the thread/process handling the cgi request.
+ * Generally we want to retry when we get ECONNREFUSED since it is
+ * probably because the listen queue is full.  We need to try harder so
+ * the client doesn't see it as a 503 error.
+ *
+ * Set this to 0 to continually retry until the connect works or Apache
+ * terminates.
+ */
+#ifndef DEFAULT_CONNECT_ATTEMPTS
+#define DEFAULT_CONNECT_ATTEMPTS  15
+#endif
+
+#define DEFAULT_SOCKET  DEFAULT_REL_RUNTIMEDIR "/ftp-lowportd-sock"
+
+/* deal with incomplete reads, writes and signals
+ * assume you really have to read/write buf_size bytes
+ */
+static apr_status_t sock_read(int fd, void *vbuf, size_t buf_size)
+{
+    char *buf = vbuf;
+    int rc;
+    size_t bytes_read = 0;
+
+    do {
+        do {
+            rc = read(fd, buf + bytes_read, buf_size - bytes_read);
+        } while (rc < 0 && errno == EINTR);
+        switch(rc) {
+        case -1:
+            return errno;
+        case 0: /* unexpected */
+            return ECONNRESET;
+        default:
+            bytes_read += rc;
+        }
+    } while (bytes_read < buf_size);
+
+    return APR_SUCCESS;
+}
+
+static apr_status_t sock_write(int fd, const void *buf, size_t buf_size)
+{
+    int rc;
+
+    while (buf_size) {
+        while ((rc = write(fd, buf, buf_size)) < 0)
+            if (errno != EINTR)
+                return errno;
+        buf += rc;
+        buf_size -= rc;
+    }
+    return APR_SUCCESS;
+}
+
+static int connect_to_daemon(int *sdptr, request_rec *r)
+{
+    int sd;
+    int connect_tries = 0;
+    apr_interval_time_t sliding_timer = 100000; /* 100 milliseconds */
+
+    while (1) {
+        ++connect_tries;
+        if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, errno, r,
+                          "unable to create socket to ftp low numbered port "
+                          "connection daemon after multiple attempts");
+            return errno;
+        }
+        if (connect(sd, (struct sockaddr *)daemon_addr, daemon_addr_len) < 0) {
+            if (errno == ECONNREFUSED 
+                    && connect_tries < DEFAULT_CONNECT_ATTEMPTS) {
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, errno, r,
+                              "connect #%d to cgi daemon failed, "
+                              "sleeping before retry", connect_tries);
+                close(sd);
+                apr_sleep(sliding_timer);
+                if (sliding_timer < apr_time_from_sec(2)) {
+                    sliding_timer *= 2;
+                }
+            }
+            else {
+                close(sd);
+                ap_log_rerror(APLOG_MARK, APLOG_DEBUG, errno, r,
+                              "unable to connect to ftp low numbered port "
+                              "connection daemon after multiple attempts");
+                return errno;
+            }
+        }
+        else {
+            break; /* we got connected! */
+        }
+        /* gotta try again, but make sure the daemon is still around */
+        if (kill(daemon_pid, 0) != 0) {
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, errno, r,
+                          "ftp low numbered port daemon is gone!  "
+                          "Is Apache terminating?");
+            return errno;
+        }
+    }
+    *sdptr = sd;
+    return APR_SUCCESS;
+}
+
+apr_status_t ftp_request_lowport(apr_socket_t **sock, request_rec *r,
+                                 apr_sockaddr_t *sa, apr_pool_t *p)
+{
+    apr_os_sock_info_t sockinfo = {0};
+    lowportd_req_t req = {0};
+    apr_status_t stat;
+    int sd = -1;
+    struct msghdr msg = {0};
+    struct cmsghdr *cmsg;
+    union {
+        struct cmsghdr align;
+        char ccmsg[CMSG_SPACE(sizeof(*sockinfo.os_sock))];
+    } msgbuf;
+    int one;
+    struct iovec iov = {&one, sizeof(one)};
+
+    msg.msg_control = msgbuf.ccmsg;
+    msg.msg_controllen = sizeof(msgbuf.ccmsg);
+    msg.msg_iov = &iov;
+    msg.msg_iovlen = 1;
+
+    if (sa->salen > sizeof(req.sockaddr))
+    {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, r,
+                      "ftp low numbered port request; unexpected sa len");
+        return APR_EINVAL;
+    }
+
+    req.ppid = parent_pid;
+    req.server = r->server;
+    req.sockaddr_len = sa->salen;
+    memcpy(&req.sockaddr, &sa->sa, sa->salen);
+
+    if ((stat = connect_to_daemon(&sd, r)) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, stat, r,
+                      "ftp low numbered port request; failed to connect");
+        return stat;
+    }
+
+    /* Write the request header */
+    if ((stat = sock_write(sd, &req, sizeof(req))) != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, stat, r,
+                      "ftp low numbered port request; failed to send request");
+        close(sd);
+        return stat;
+    }
+
+    while (recvmsg(sd, &msg, 0) == -1)
+        if (errno != EINTR) {
+            stat = errno;
+            ap_log_rerror(APLOG_MARK, APLOG_DEBUG, stat, r,
+                          "ftp low numbered port request; receive failed");
+            close(sd);
+            return stat;
+        }
+
+    cmsg = CMSG_FIRSTHDR(&msg);
+    if (!cmsg || cmsg->cmsg_level != SOL_SOCKET 
+              || cmsg->cmsg_type != SCM_RIGHTS) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, r,
+                      "ftp low numbered port request; unexpected response");
+        close(sd);
+        return APR_EINVAL;
+    }
+
+    sockinfo.os_sock = (int *)CMSG_DATA(cmsg);
+    sockinfo.family = sa->sa.sin.sin_family;
+    sockinfo.type = SOCK_STREAM;
+    sockinfo.protocol = IPPROTO_TCP;
+    sockinfo.local = (struct sockaddr *)&sa->sa;
+
+    stat = apr_os_sock_make(sock, &sockinfo, p); 
+    if (stat != APR_SUCCESS) {
+        ap_log_rerror(APLOG_MARK, APLOG_DEBUG, stat, r,
+                      "ftp low numbered port request; sock_make failed");
+    }
+
+    close(sd);
+    return APR_SUCCESS;
+}
+
+static void daemon_signal_handler(int sig)
+{
+    if (sig == SIGHUP) {
+        ++daemon_should_exit;
+    }
+}
+
+static int lowportd_server(void *data)
+{
+    int sd, sd2, rc;
+    mode_t omask;
+    apr_pool_t *ptrans;
+    server_rec *main_server = data;
+    apr_status_t rv;
+
+    apr_pool_create(&ptrans, pdaemon);
+
+    apr_signal(SIGCHLD, SIG_IGN);
+    apr_signal(SIGHUP, daemon_signal_handler);
+
+    /* Close our copy of the listening sockets */
+    ap_close_listeners();
+
+    if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
+                     "Couldn't create unix domain socket");
+        return errno;
+    }
+
+    omask = umask(0077); /* so that only Apache can use socket */
+    rc = bind(sd, (struct sockaddr *)daemon_addr, daemon_addr_len);
+    umask(omask); /* can't fail, so can't clobber errno */
+    if (rc < 0) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
+                     "Couldn't bind unix domain socket %s",
+                     sockname);
+        return errno;
+    }
+
+    /* Not all flavors of unix use the current umask for AF_UNIX perms */
+    rv = apr_file_perms_set(sockname, APR_FPROT_UREAD|APR_FPROT_UWRITE
+                                                     |APR_FPROT_UEXECUTE);
+    if (rv != APR_SUCCESS) {
+        ap_log_error(APLOG_MARK, APLOG_CRIT, rv, main_server,
+                     "Couldn't set permissions on unix domain socket %s",
+                     sockname);
+        return rv;
+    }
+
+    if (listen(sd, DEFAULT_CGID_LISTENBACKLOG) < 0) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
+                     "Couldn't listen on unix domain socket");
+        return errno;
+    }
+
+    if (!geteuid()) {
+        if (chown(sockname, ap_unixd_config.user_id, -1) < 0) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
+                         "Couldn't change owner of unix domain socket %s",
+                         sockname);
+            return errno;
+        }
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, main_server,
+                 "FTP low numbered port daemon waiting for port requests");
+
+    while (!daemon_should_exit) {
+        apr_proc_t *procnew = NULL;
+        lowportd_req_t req;
+        apr_status_t stat;
+        apr_socklen_t len;
+        struct sockaddr_un unix_addr;
+        server_rec *server;
+        int fd;
+        int one = 1;
+        struct msghdr msg = {0};
+        struct cmsghdr *cmsg;
+        union {
+            struct cmsghdr align;
+            char ccmsg[CMSG_SPACE(sizeof(fd))];
+        } msgbuf;
+        struct iovec iov = {&one, sizeof(one)};
+
+        apr_pool_clear(ptrans);
+
+        len = sizeof(unix_addr);
+        sd2 = accept(sd, (struct sockaddr *)&unix_addr, &len);
+        if (sd2 < 0) {
+#if defined(ENETDOWN)
+            if (errno == ENETDOWN) {
+                /* The network has been shut down, die off with error msg */
+                ++daemon_should_exit;
+            }
+#endif
+            if (errno != EINTR) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, errno,
+                             (server_rec *)data,
+                             "FTP Error accepting on lowportd socket");
+            }
+            continue;
+        }
+
+        procnew = apr_pcalloc(ptrans, sizeof(*procnew));
+        stat = sock_read(sd2, &req, sizeof(req));
+        if (stat != APR_SUCCESS) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, stat,
+                         main_server,
+                         "FTP Error reading request on lowportd socket");
+            close(sd2);
+            continue;
+        }
+
+        if (req.ppid != parent_pid) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
+                         "FTP low port request received from wrong server "
+                         "instance; see FTPLowPortSock directive");
+            close(sd2);
+            continue;
+        }
+
+        for (server = ap_server_conf; server; server = server->next)
+             if (server == req.server)
+                 break;
+        if (!server) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
+                         "FTP low port request received for invalid server");
+            close(sd2);
+            continue;
+        }
+
+#if APR_HAVE_IPV6
+        fd = socket(req.sockaddr.sin6_family, SOCK_STREAM, APR_PROTO_TCP);
+#else
+        fd = socket(req.sockaddr.sin_family, SOCK_STREAM, APR_PROTO_TCP);
+#endif
+        if (fd < 0) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, errno, server,
+                         "FTP low port daemon failed to create socket");
+            close(sd2);
+            continue;
+        }
+
+        if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
+                       (void*)&one, sizeof(one)) == -1)
+            ap_log_error(APLOG_MARK, APLOG_DEBUG, errno, server,
+                         "FTP low port daemon failed to set reuseaddr flag");
+
+        if (bind(fd, (struct sockaddr *)&req.sockaddr, req.sockaddr_len)
+                == -1) {
+            ap_log_error(APLOG_MARK, APLOG_ERR, errno, server,
+                         "FTP low port daemon failed to create socket");
+            close(sd2);
+            continue;
+        }
+
+        msg.msg_control = msgbuf.ccmsg;
+        msg.msg_controllen = sizeof(msgbuf.ccmsg);
+        msg.msg_iov = &iov;
+        msg.msg_iovlen = 1;
+
+        cmsg = CMSG_FIRSTHDR(&msg);
+        cmsg->cmsg_level = SOL_SOCKET;
+        cmsg->cmsg_type = SCM_RIGHTS;
+        cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
+        *(int*)CMSG_DATA(cmsg) = fd;
+        msg.msg_controllen = cmsg->cmsg_len;
+
+        while (sendmsg(sd2, &msg, 0) == -1)
+            if (errno != EINTR) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, errno, server,
+                             "FTP low port daemon; error sending bound fd");
+                break;
+            }
+
+        ap_log_error(APLOG_MARK, APLOG_DEBUG, errno, server,
+                     "FTP low port daemon success; sent bound socket fd");
+
+        close(fd);
+        close(sd2);
+    }
+
+    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, main_server,
+                 "FTP low numbered port daemon exiting");
+
+    return -1; /* should be <= 0 to distinguish from startup errors */
+}
+
+static void lowportd_maint(int reason, void *data, apr_wait_t status);
+
+static int lowportd_start(apr_pool_t *p, server_rec *main_server,
+                      apr_proc_t *procnew)
+{
+
+    daemon_should_exit = 0; /* clear setting from previous generation */
+    if ((daemon_pid = fork()) < 0) {
+        ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
+                     "mod_ftp: Couldn't spawn lowportd daemon process");
+        return DECLINED;
+    }
+    else if (daemon_pid == 0) {
+        exit(lowportd_server(main_server) > 0 ? DAEMON_STARTUP_ERROR : -1);
+    }
+    procnew->pid = daemon_pid;
+    procnew->err = procnew->in = procnew->out = NULL;
+    apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
+#if APR_HAS_OTHER_CHILD
+    apr_proc_other_child_register(procnew, lowportd_maint, procnew, NULL, p);
+#endif
+    return OK;
+}
+
+#if APR_HAS_OTHER_CHILD
+static void lowportd_maint(int reason, void *data, apr_wait_t status)
+{
+    apr_proc_t *proc = data;
+    int mpm_state;
+    int stopping;
+
+    switch (reason) {
+        case APR_OC_REASON_DEATH:
+            apr_proc_other_child_unregister(data);
+            /* If apache is not terminating or restarting,
+             * restart the daemon
+             */
+            stopping = 1; /* if MPM doesn't support query,
+                           * assume we shouldn't restart daemon
+                           */
+            if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state) == APR_SUCCESS &&
+                mpm_state != AP_MPMQ_STOPPING) {
+                stopping = 0;
+            }
+            if (!stopping) {
+                if (status == DAEMON_STARTUP_ERROR) {
+                    ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
+                                 "lowportd daemon failed to initialize");
+                }
+                else {
+                    ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
+                                 "lowportd daemon process died, restarting");
+                    lowportd_start(pdaemon, ap_server_conf, proc);
+                }
+            }
+            break;
+        case APR_OC_REASON_RESTART:
+            /* don't do anything; server is stopping or restarting */
+            apr_proc_other_child_unregister(data);
+            break;
+        case APR_OC_REASON_LOST:
+            /* Restart the child daemon process */
+            apr_proc_other_child_unregister(data);
+            lowportd_start(pdaemon, ap_server_conf, proc);
+            break;
+        case APR_OC_REASON_UNREGISTER:
+            /* we get here when pdaemon is cleaned up, which is cleaned
+             * up when pconf gets cleaned up
+             */
+            kill(proc->pid, SIGHUP); /* send signal to daemon to die */
+
+            /* Remove the cgi socket, we must do it here in order to try and
+             * guarantee the same permissions as when the socket was created.
+             */
+            if (unlink(sockname) < 0 && errno != ENOENT) {
+                ap_log_error(APLOG_MARK, APLOG_ERR, errno, NULL,
+                             "Couldn't unlink unix domain socket %s",
+                             sockname);
+            }
+            break;
+    }
+}
+#endif
+
+int lowportd_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
+{
+    sockname = ap_append_pid(pconf, DEFAULT_SOCKET, ".");
+    return OK;
+}
+
+int lowportd_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
+                         server_rec *main_server)
+{
+    apr_proc_t *procnew = NULL;
+    int first_time = 0;
+    const char *userdata_key = "lowportd_config";
+    int ret = OK;
+    void *data;
+
+    pdaemon = p;
+
+    apr_pool_userdata_get(&data, userdata_key, main_server->process->pool);
+    if (!data) {
+        first_time = 1;
+        procnew = apr_pcalloc(main_server->process->pool, sizeof(*procnew));
+        procnew->pid = -1;
+        procnew->err = procnew->in = procnew->out = NULL;
+        apr_pool_userdata_set((const void *)procnew, userdata_key,
+                     apr_pool_cleanup_null, main_server->process->pool);
+    }
+    else {
+        procnew = data;
+    }
+
+    if (!first_time) {
+        parent_pid = getpid();
+        sockname = ap_server_root_relative(p, sockname);
+
+        daemon_addr_len = APR_OFFSETOF(struct sockaddr_un, sun_path)
+                        + strlen(sockname);
+        daemon_addr = (struct sockaddr_un *)apr_palloc(p, daemon_addr_len + 1);
+        daemon_addr->sun_family = AF_UNIX;
+        strcpy(daemon_addr->sun_path, sockname);
+
+        ret = lowportd_start(p, main_server, procnew);
+    }
+    return ret;
+}
+
+const char *lowportd_set_socket(cmd_parms *cmd, void *dummy, const char *arg)
+{
+    const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
+    if (err != NULL) {
+        return err;
+    }
+
+    /* Make sure the pid is appended to the sockname */
+    sockname = ap_append_pid(cmd->pool, arg, ".");
+    sockname = ap_server_root_relative(cmd->pool, sockname);
+
+    if (!sockname) {
+        return apr_pstrcat(cmd->pool, "Invalid FTPLowPortSock path",
+                           arg, NULL);
+    }
+
+    return NULL;
+}
+
+#else /* !APR_HAVE_SOCK_UN_H */
+
+const char *lowportd_set_socket(cmd_parms *cmd, void *dummy, const char *arg)
+{
+    ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
+                 "FTPLowPortSock directive ignored, this platform does "
+                 "not support the low-numbered-port daemon");
+    return NULL;
+}
+
+#endif /* !APR_HAVE_SOCK_UN_H */

Propchange: httpd/mod_ftp/trunk/modules/ftp/ftp_lowportd.c
------------------------------------------------------------------------------
    svn:eol-style = native