Return-Path: Delivered-To: apmail-httpd-cvs-archive@www.apache.org Received: (qmail 14619 invoked from network); 29 May 2009 05:49:23 -0000 Received: from hermes.apache.org (HELO mail.apache.org) (140.211.11.3) by minotaur.apache.org with SMTP; 29 May 2009 05:49:23 -0000 Received: (qmail 86315 invoked by uid 500); 29 May 2009 05:49:35 -0000 Delivered-To: apmail-httpd-cvs-archive@httpd.apache.org Received: (qmail 86227 invoked by uid 500); 29 May 2009 05:49:35 -0000 Mailing-List: contact cvs-help@httpd.apache.org; run by ezmlm Precedence: bulk Reply-To: dev@httpd.apache.org list-help: list-unsubscribe: List-Post: List-Id: Delivered-To: mailing list cvs@httpd.apache.org Received: (qmail 86205 invoked by uid 99); 29 May 2009 05:49:34 -0000 Received: from nike.apache.org (HELO nike.apache.org) (192.87.106.230) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 29 May 2009 05:49:34 +0000 X-ASF-Spam-Status: No, hits=-2000.0 required=10.0 tests=ALL_TRUSTED X-Spam-Check-By: apache.org Received: from [140.211.11.4] (HELO eris.apache.org) (140.211.11.4) by apache.org (qpsmtpd/0.29) with ESMTP; Fri, 29 May 2009 05:49:30 +0000 Received: by eris.apache.org (Postfix, from userid 65534) id 5A36E2388873; Fri, 29 May 2009 05:49:08 +0000 (UTC) Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit Subject: svn commit: r779848 - in /httpd/mod_ftp/trunk/modules/ftp: ftp_internal.h ftp_lowportd.c Date: Fri, 29 May 2009 05:49:08 -0000 To: cvs@httpd.apache.org From: wrowe@apache.org X-Mailer: svnmailer-1.0.8 Message-Id: <20090529054908.5A36E2388873@eris.apache.org> X-Virus-Checked: Checked by ClamAV on apache.org 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 /* for sockaddr_un */ + +#if APR_HAVE_SYS_SOCKET_H +#include +#endif +#if APR_HAVE_UNISTD_H +#include +#endif +#if APR_HAVE_SYS_TYPES_H +#include +#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