"Fossies" - the Fresh Open Source Software Archive

Member "mod_ftp-0.9.6/modules/ftp/ftp_lowportd.c" (25 Aug 2009, 21278 Bytes) of package /linux/www/apache_httpd_modules/old/mod_ftp-0.9.6-beta.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) C and C++ source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "ftp_lowportd.c" see the Fossies "Dox" file reference documentation.

    1 /* Licensed to the Apache Software Foundation (ASF) under one or more
    2  * contributor license agreements.  See the NOTICE file distributed with
    3  * this work for additional information regarding copyright ownership.
    4  * The ASF licenses this file to You under the Apache License, Version 2.0
    5  * (the "License"); you may not use this file except in compliance with
    6  * the License.  You may obtain a copy of the License at
    7  *
    8  *     http://www.apache.org/licenses/LICENSE-2.0
    9  *
   10  * Unless required by applicable law or agreed to in writing, software
   11  * distributed under the License is distributed on an "AS IS" BASIS,
   12  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
   13  * See the License for the specific language governing permissions and
   14  * limitations under the License.
   15  */
   16 
   17 #define CORE_PRIVATE
   18 #include "mod_ftp.h"
   19 #include "ftp_internal.h"
   20 #include "apr_signal.h"
   21 #include "ap_listen.h"
   22 #include "ap_mpm.h"
   23 
   24 #ifdef HAVE_FTP_LOWPORTD
   25 #include "unixd.h"
   26 
   27 #if APR_HAVE_SYS_SOCKET_H
   28 #include <sys/socket.h>
   29 #endif
   30 #if APR_HAVE_SYS_UN_H
   31 #include <sys/un.h>  /* for sockaddr_un */
   32 #endif
   33 
   34 typedef struct lowportd_req_t {
   35     pid_t       ppid;
   36     server_rec *server;
   37 #if APR_HAVE_IPV6
   38     struct sockaddr_in6 sockaddr;
   39 #else
   40     struct sockaddr_in sockaddr;
   41 #endif
   42     size_t sockaddr_len;
   43 } lowportd_req_t;
   44 
   45 static apr_pool_t *pdaemon = NULL;
   46 static const char *sockname;
   47 static struct sockaddr_un *daemon_addr;
   48 static apr_socklen_t daemon_addr_len;
   49 static pid_t parent_pid;
   50 static pid_t daemon_pid;
   51 static int daemon_should_exit = 0;
   52 static server_rec *main_server_conf;
   53 
   54 /* The APR other-child API doesn't tell us how the daemon exited
   55  * (SIGSEGV vs. exit(1)).  The other-child maintenance function
   56  * needs to decide whether to restart the daemon after a failure
   57  * based on whether or not it exited due to a fatal startup error
   58  * or something that happened at steady-state.  This exit status
   59  * is unlikely to collide with exit signals.
   60  */
   61 #define DAEMON_STARTUP_ERROR 254
   62 
   63 /* DEFAULT_CGID_LISTENBACKLOG controls the max depth on the unix socket's
   64  * pending connection queue.  If a bunch of cgi requests arrive at about
   65  * the same time, connections from httpd threads/processes will back up
   66  * in the queue while the cgid process slowly forks off a child to process
   67  * each connection on the unix socket.  If the queue is too short, the
   68  * httpd process will get ECONNREFUSED when trying to connect.
   69  */
   70 #ifndef DEFAULT_CGID_LISTENBACKLOG
   71 #define DEFAULT_CGID_LISTENBACKLOG 100
   72 #endif
   73 
   74 /* DEFAULT_CONNECT_ATTEMPTS controls how many times we'll try to connect
   75  * to the cgi daemon from the thread/process handling the cgi request.
   76  * Generally we want to retry when we get ECONNREFUSED since it is
   77  * probably because the listen queue is full.  We need to try harder so
   78  * the client doesn't see it as a 503 error.
   79  *
   80  * Set this to 0 to continually retry until the connect works or Apache
   81  * terminates.
   82  */
   83 #ifndef DEFAULT_CONNECT_ATTEMPTS
   84 #define DEFAULT_CONNECT_ATTEMPTS  15
   85 #endif
   86 
   87 #define DEFAULT_SOCKET  DEFAULT_REL_RUNTIMEDIR "/ftp-lowportd-sock"
   88 
   89 /* deal with incomplete reads, writes and signals
   90  * assume you really have to read/write buf_size bytes
   91  */
   92 static apr_status_t sock_read(int fd, void *vbuf, size_t buf_size)
   93 {
   94     char *buf = vbuf;
   95     int rc;
   96     size_t bytes_read = 0;
   97 
   98     do {
   99         do {
  100             rc = read(fd, buf + bytes_read, buf_size - bytes_read);
  101         } while (rc < 0 && errno == EINTR);
  102         switch(rc) {
  103         case -1:
  104             return errno;
  105         case 0: /* unexpected */
  106             return ECONNRESET;
  107         default:
  108             bytes_read += rc;
  109         }
  110     } while (bytes_read < buf_size);
  111 
  112     return APR_SUCCESS;
  113 }
  114 
  115 static apr_status_t sock_write(int fd, const void *vbuf, size_t buf_size)
  116 {
  117     const char *buf = vbuf;
  118     int rc;
  119 
  120     while (buf_size) {
  121         while ((rc = write(fd, buf, buf_size)) < 0)
  122             if (errno != EINTR)
  123                 return errno;
  124         buf += rc;
  125         buf_size -= rc;
  126     }
  127     return APR_SUCCESS;
  128 }
  129 
  130 static int connect_to_daemon(int *sdptr, request_rec *r)
  131 {
  132     int sd;
  133     int connect_tries = 0;
  134     apr_interval_time_t sliding_timer = 100000; /* 100 milliseconds */
  135 
  136     while (1) {
  137         ++connect_tries;
  138         if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
  139             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, errno, r,
  140                           "unable to create socket to ftp low numbered port "
  141                           "connection daemon after multiple attempts");
  142             return errno;
  143         }
  144         if (connect(sd, (struct sockaddr *)daemon_addr, daemon_addr_len) < 0) {
  145             if (errno == ECONNREFUSED 
  146                     && connect_tries < DEFAULT_CONNECT_ATTEMPTS) {
  147                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, errno, r,
  148                               "connect #%d to cgi daemon failed, "
  149                               "sleeping before retry", connect_tries);
  150                 close(sd);
  151                 apr_sleep(sliding_timer);
  152                 if (sliding_timer < apr_time_from_sec(2)) {
  153                     sliding_timer *= 2;
  154                 }
  155             }
  156             else {
  157                 close(sd);
  158                 ap_log_rerror(APLOG_MARK, APLOG_DEBUG, errno, r,
  159                               "unable to connect to ftp low numbered port "
  160                               "connection daemon after multiple attempts");
  161                 return errno;
  162             }
  163         }
  164         else {
  165             break; /* we got connected! */
  166         }
  167         /* gotta try again, but make sure the daemon is still around */
  168         if (kill(daemon_pid, 0) != 0) {
  169             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, errno, r,
  170                           "ftp low numbered port daemon is gone!  "
  171                           "Is Apache terminating?");
  172             return errno;
  173         }
  174     }
  175     *sdptr = sd;
  176     return APR_SUCCESS;
  177 }
  178 
  179 apr_status_t ftp_request_lowport(apr_socket_t **sock, request_rec *r,
  180                                  apr_sockaddr_t *sa, apr_pool_t *p)
  181 {
  182     apr_os_sock_info_t sockinfo = {0};
  183     lowportd_req_t req = {0};
  184     apr_status_t stat;
  185     int sd = -1;
  186     struct msghdr msg = {0};
  187     int one;
  188     struct iovec iov = {(void*)&one, sizeof(one)};
  189 #ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
  190     struct cmsghdr *cmsg;
  191     union {
  192         struct cmsghdr align;
  193         char ccmsg[CMSG_SPACE(sizeof(*sockinfo.os_sock))];
  194     } msgbuf;
  195 
  196     msg.msg_control = msgbuf.ccmsg;
  197     msg.msg_controllen = sizeof(msgbuf.ccmsg);
  198 #else
  199     int fd = -1;
  200 
  201     msg.msg_accrightslen = sizeof(fd);
  202     msg.msg_accrights = (caddr_t)&fd;
  203     sockinfo.os_sock = &fd;
  204 #endif
  205     msg.msg_iov = &iov;
  206     msg.msg_iovlen = 1;
  207 
  208     if (sa->salen > sizeof(req.sockaddr))
  209     {
  210         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, r,
  211                       "ftp low numbered port request; unexpected sa len");
  212         return APR_EINVAL;
  213     }
  214 
  215     req.ppid = parent_pid;
  216     req.server = r->server;
  217     req.sockaddr_len = sa->salen;
  218     memcpy(&req.sockaddr, &sa->sa, sa->salen);
  219 
  220     if ((stat = connect_to_daemon(&sd, r)) != APR_SUCCESS) {
  221         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, stat, r,
  222                       "ftp low numbered port request; failed to connect");
  223         return stat;
  224     }
  225 
  226     /* Write the request header */
  227     if ((stat = sock_write(sd, &req, sizeof(req))) != APR_SUCCESS) {
  228         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, stat, r,
  229                       "ftp low numbered port request; failed to send request");
  230         close(sd);
  231         return stat;
  232     }
  233 
  234     while (recvmsg(sd, &msg, 0) == -1)
  235         if (errno != EINTR) {
  236             stat = errno;
  237             ap_log_rerror(APLOG_MARK, APLOG_DEBUG, stat, r,
  238                           "ftp low numbered port request; receive failed");
  239             close(sd);
  240             return stat;
  241         }
  242 
  243 #ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
  244     cmsg = CMSG_FIRSTHDR(&msg);
  245     if (cmsg && cmsg->cmsg_len == CMSG_LEN(sizeof(int))
  246              && cmsg->cmsg_level == SOL_SOCKET && cmsg->cmsg_type == SCM_RIGHTS)
  247         sockinfo.os_sock = (int *)CMSG_DATA(cmsg);
  248     else
  249 #else
  250     if (msg.msg_accrightslen != sizeof(fd))
  251 #endif
  252     {
  253         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, APR_EINVAL, r,
  254                       "ftp low numbered port request; unexpected response");
  255         close(sd);
  256         return APR_EINVAL;
  257     }
  258     sockinfo.family = sa->sa.sin.sin_family;
  259     sockinfo.type = SOCK_STREAM;
  260 #if APR_MAJOR_VERSION > 0
  261     sockinfo.protocol = IPPROTO_TCP;
  262 #endif
  263     sockinfo.local = (struct sockaddr *)&sa->sa;
  264 
  265     stat = apr_os_sock_make(sock, &sockinfo, p); 
  266     if (stat != APR_SUCCESS) {
  267         ap_log_rerror(APLOG_MARK, APLOG_DEBUG, stat, r,
  268                       "ftp low numbered port request; sock_make failed");
  269     }
  270 
  271     close(sd);
  272     return APR_SUCCESS;
  273 }
  274 
  275 static void daemon_signal_handler(int sig)
  276 {
  277     if (sig == SIGHUP) {
  278         ++daemon_should_exit;
  279     }
  280 }
  281 
  282 static int lowportd_server(void *data)
  283 {
  284     int sd, sd2, rc;
  285     mode_t omask;
  286     apr_pool_t *ptrans;
  287     server_rec *main_server = data;
  288     apr_status_t rv;
  289 
  290     apr_pool_create(&ptrans, pdaemon);
  291 
  292     apr_signal(SIGCHLD, SIG_IGN);
  293     apr_signal(SIGHUP, daemon_signal_handler);
  294 
  295 #if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER > 0
  296     /* Close our copy of the listening sockets */
  297     ap_close_listeners();
  298 #endif
  299 
  300     if ((sd = socket(AF_UNIX, SOCK_STREAM, 0)) < 0) {
  301         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
  302                      "Couldn't create unix domain socket");
  303         return errno;
  304     }
  305 
  306     omask = umask(0077); /* so that only Apache can use socket */
  307     rc = bind(sd, (struct sockaddr *)daemon_addr, daemon_addr_len);
  308     umask(omask); /* can't fail, so can't clobber errno */
  309     if (rc < 0) {
  310         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
  311                      "Couldn't bind unix domain socket %s",
  312                      sockname);
  313         return errno;
  314     }
  315 
  316     /* Not all flavors of unix use the current umask for AF_UNIX perms */
  317 #if APR_MAJOR_VERSION > 0
  318     rv = apr_file_perms_set(sockname, APR_FPROT_UREAD|APR_FPROT_UWRITE
  319                                                      |APR_FPROT_UEXECUTE);
  320 #else
  321     rv = apr_file_perms_set(sockname, APR_UREAD|APR_UWRITE
  322                                                |APR_UEXECUTE);
  323 #endif
  324     if (rv != APR_SUCCESS) {
  325         ap_log_error(APLOG_MARK, APLOG_CRIT, rv, main_server,
  326                      "Couldn't set permissions on unix domain socket %s",
  327                      sockname);
  328         return rv;
  329     }
  330 
  331     if (listen(sd, DEFAULT_CGID_LISTENBACKLOG) < 0) {
  332         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
  333                      "Couldn't listen on unix domain socket");
  334         return errno;
  335     }
  336 
  337     if (!geteuid()) {
  338 #if MODULE_MAGIC_NUMBER_MAJOR < 20081201
  339         if (chown(sockname, unixd_config.user_id, -1) < 0) {
  340 #else
  341         if (chown(sockname, ap_unixd_config.user_id, -1) < 0) {
  342 #endif
  343             ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
  344                          "Couldn't change owner of unix domain socket %s",
  345                          sockname);
  346             return errno;
  347         }
  348     }
  349 
  350     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, main_server,
  351                  "FTP low numbered port daemon waiting for port requests");
  352 
  353     while (!daemon_should_exit) {
  354         apr_proc_t *procnew = NULL;
  355         lowportd_req_t req;
  356         apr_status_t stat;
  357         apr_socklen_t len;
  358         struct sockaddr_un unix_addr;
  359         server_rec *server;
  360         int fd;
  361         int one = 1;
  362         struct msghdr msg = {0};
  363         struct iovec iov = {(void*)&one, sizeof(one)};
  364 #ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
  365         struct cmsghdr *cmsg;
  366         union {
  367             struct cmsghdr align;
  368             char ccmsg[CMSG_SPACE(sizeof(fd))];
  369         } msgbuf;
  370 #endif
  371 
  372         apr_pool_clear(ptrans);
  373 
  374         len = sizeof(unix_addr);
  375         sd2 = accept(sd, (struct sockaddr *)&unix_addr, &len);
  376         if (sd2 < 0) {
  377 #if defined(ENETDOWN)
  378             if (errno == ENETDOWN) {
  379                 /* The network has been shut down, die off with error msg */
  380                 ++daemon_should_exit;
  381             }
  382 #endif
  383             if (errno != EINTR) {
  384                 ap_log_error(APLOG_MARK, APLOG_ERR, errno,
  385                              main_server,
  386                              "FTP Error accepting on lowportd socket");
  387             }
  388             continue;
  389         }
  390 
  391         procnew = apr_pcalloc(ptrans, sizeof(*procnew));
  392         stat = sock_read(sd2, &req, sizeof(req));
  393         if (stat != APR_SUCCESS) {
  394             ap_log_error(APLOG_MARK, APLOG_ERR, stat,
  395                          main_server,
  396                          "FTP Error reading request on lowportd socket");
  397             close(sd2);
  398             continue;
  399         }
  400 
  401         if (req.ppid != parent_pid) {
  402             ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
  403                          "FTP low port request received from wrong server "
  404                          "instance; see FTPLowPortSock directive");
  405             close(sd2);
  406             continue;
  407         }
  408 
  409         for (server = main_server; server; server = server->next)
  410              if (server == req.server)
  411                  break;
  412         if (!server) {
  413             ap_log_error(APLOG_MARK, APLOG_ERR, 0, main_server,
  414                          "FTP low port request received for invalid server");
  415             close(sd2);
  416             continue;
  417         }
  418 
  419 #if APR_HAVE_IPV6
  420         fd = socket(req.sockaddr.sin6_family, SOCK_STREAM, APR_PROTO_TCP);
  421 #else
  422         fd = socket(req.sockaddr.sin_family, SOCK_STREAM, APR_PROTO_TCP);
  423 #endif
  424         if (fd < 0) {
  425             ap_log_error(APLOG_MARK, APLOG_ERR, errno, server,
  426                          "FTP low port daemon failed to create socket");
  427             close(sd2);
  428             continue;
  429         }
  430 
  431         if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR,
  432                        (void*)&one, sizeof(one)) == -1)
  433             ap_log_error(APLOG_MARK, APLOG_DEBUG, errno, server,
  434                          "FTP low port daemon failed to set reuseaddr flag");
  435 
  436         if (bind(fd, (struct sockaddr *)&req.sockaddr, req.sockaddr_len)
  437                 == -1) {
  438             ap_log_error(APLOG_MARK, APLOG_ERR, errno, server,
  439                          "FTP low port daemon failed to create socket");
  440             close(sd2);
  441             continue;
  442         }
  443 
  444         msg.msg_iov = &iov;
  445         msg.msg_iovlen = 1;
  446 
  447 #ifdef HAVE_STRUCT_MSGHDR_MSG_CONTROL
  448         msg.msg_control = msgbuf.ccmsg;
  449         msg.msg_controllen = sizeof(msgbuf.ccmsg);
  450 
  451 
  452         cmsg = CMSG_FIRSTHDR(&msg);
  453         cmsg->cmsg_level = SOL_SOCKET;
  454         cmsg->cmsg_type = SCM_RIGHTS;
  455         cmsg->cmsg_len = CMSG_LEN(sizeof(fd));
  456         *(int*)CMSG_DATA(cmsg) = fd;
  457         msg.msg_controllen = cmsg->cmsg_len;
  458 #else
  459         msg.msg_accrightslen = sizeof(fd);
  460         msg.msg_accrights = (caddr_t)&fd;
  461 #endif
  462 
  463         while (sendmsg(sd2, &msg, 0) == -1)
  464             if (errno != EINTR) {
  465                 ap_log_error(APLOG_MARK, APLOG_ERR, errno, server,
  466                              "FTP low port daemon; error sending bound fd");
  467                 break;
  468             }
  469 
  470         ap_log_error(APLOG_MARK, APLOG_DEBUG, errno, server,
  471                      "FTP low port daemon success; sent bound socket fd");
  472 
  473         close(fd);
  474         close(sd2);
  475     }
  476 
  477     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, main_server,
  478                  "FTP low numbered port daemon exiting");
  479 
  480     return -1; /* should be <= 0 to distinguish from startup errors */
  481 }
  482 
  483 static void lowportd_maint(int reason, void *data, apr_wait_t status);
  484 
  485 static int lowportd_start(apr_pool_t *p, server_rec *main_server,
  486                           apr_proc_t *procnew)
  487 {
  488     /* for lowportd_maint */
  489     main_server_conf = main_server;
  490 
  491     daemon_should_exit = 0; /* clear setting from previous generation */
  492     if ((daemon_pid = fork()) < 0) {
  493         ap_log_error(APLOG_MARK, APLOG_ERR, errno, main_server,
  494                      "mod_ftp: Couldn't spawn lowportd daemon process");
  495         return DECLINED;
  496     }
  497     else if (daemon_pid == 0) {
  498         exit(lowportd_server(main_server) > 0 ? DAEMON_STARTUP_ERROR : -1);
  499     }
  500     procnew->pid = daemon_pid;
  501     procnew->err = procnew->in = procnew->out = NULL;
  502     apr_pool_note_subprocess(p, procnew, APR_KILL_AFTER_TIMEOUT);
  503 #if APR_HAS_OTHER_CHILD
  504     apr_proc_other_child_register(procnew, lowportd_maint, procnew, NULL, p);
  505 #endif
  506     return OK;
  507 }
  508 
  509 #if APR_HAS_OTHER_CHILD
  510 static void lowportd_maint(int reason, void *data, apr_wait_t status)
  511 {
  512     apr_proc_t *proc = data;
  513     int mpm_state;
  514     int stopping;
  515 
  516     switch (reason) {
  517         case APR_OC_REASON_DEATH:
  518             apr_proc_other_child_unregister(data);
  519             /* If apache is not terminating or restarting,
  520              * restart the daemon
  521              */
  522             stopping = 1; /* if MPM doesn't support query,
  523                            * assume we shouldn't restart daemon
  524                            */
  525             if (ap_mpm_query(AP_MPMQ_MPM_STATE, &mpm_state) == APR_SUCCESS &&
  526                 mpm_state != AP_MPMQ_STOPPING) {
  527                 stopping = 0;
  528             }
  529             if (!stopping) {
  530                 if (status == DAEMON_STARTUP_ERROR) {
  531                     ap_log_error(APLOG_MARK, APLOG_CRIT, 0, NULL,
  532                                  "lowportd daemon failed to initialize");
  533                 }
  534                 else {
  535                     ap_log_error(APLOG_MARK, APLOG_ERR, 0, NULL,
  536                                  "lowportd daemon process died, restarting");
  537                     lowportd_start(pdaemon, main_server_conf, proc);
  538                 }
  539             }
  540             break;
  541         case APR_OC_REASON_RESTART:
  542             /* don't do anything; server is stopping or restarting */
  543             apr_proc_other_child_unregister(data);
  544             break;
  545         case APR_OC_REASON_LOST:
  546             /* Restart the child daemon process */
  547             apr_proc_other_child_unregister(data);
  548             lowportd_start(pdaemon, main_server_conf, proc);
  549             break;
  550         case APR_OC_REASON_UNREGISTER:
  551             /* we get here when pdaemon is cleaned up, which is cleaned
  552              * up when pconf gets cleaned up
  553              */
  554             kill(proc->pid, SIGHUP); /* send signal to daemon to die */
  555 
  556             /* Remove the cgi socket, we must do it here in order to try and
  557              * guarantee the same permissions as when the socket was created.
  558              */
  559             if (unlink(sockname) < 0 && errno != ENOENT) {
  560                 ap_log_error(APLOG_MARK, APLOG_ERR, errno, NULL,
  561                              "Couldn't unlink unix domain socket %s",
  562                              sockname);
  563             }
  564             break;
  565     }
  566 }
  567 #endif
  568 
  569 int lowportd_pre_config(apr_pool_t *pconf, apr_pool_t *plog, apr_pool_t *ptemp)
  570 {
  571 #if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER > 0
  572     sockname = ap_append_pid(pconf, DEFAULT_SOCKET, ".");
  573 #endif
  574     return OK;
  575 }
  576 
  577 int lowportd_post_config(apr_pool_t *p, apr_pool_t *plog, apr_pool_t *ptemp,
  578                          server_rec *main_server)
  579 {
  580     apr_proc_t *procnew = NULL;
  581     int first_time = 0;
  582     const char *userdata_key = "lowportd_config";
  583     int ret = OK;
  584     void *data;
  585 
  586     pdaemon = p;
  587 
  588     apr_pool_userdata_get(&data, userdata_key, main_server->process->pool);
  589     if (!data) {
  590         first_time = 1;
  591         procnew = apr_pcalloc(main_server->process->pool, sizeof(*procnew));
  592         procnew->pid = -1;
  593         procnew->err = procnew->in = procnew->out = NULL;
  594         apr_pool_userdata_set((const void *)procnew, userdata_key,
  595                      apr_pool_cleanup_null, main_server->process->pool);
  596     }
  597     else {
  598         procnew = data;
  599     }
  600 
  601     if (!first_time) {
  602         parent_pid = getpid();
  603         sockname = ap_server_root_relative(p, sockname);
  604 
  605         daemon_addr_len = APR_OFFSETOF(struct sockaddr_un, sun_path)
  606                         + strlen(sockname);
  607         daemon_addr = (struct sockaddr_un *)apr_palloc(p, daemon_addr_len + 1);
  608         daemon_addr->sun_family = AF_UNIX;
  609         strcpy(daemon_addr->sun_path, sockname);
  610 
  611         ret = lowportd_start(p, main_server, procnew);
  612     }
  613     return ret;
  614 }
  615 
  616 const char *lowportd_set_socket(cmd_parms *cmd, void *dummy, const char *arg)
  617 {
  618     const char *err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
  619     if (err != NULL) {
  620         return err;
  621     }
  622 
  623     /* Make sure the pid is appended to the sockname */
  624 #if AP_SERVER_MAJORVERSION_NUMBER > 2 || AP_SERVER_MINORVERSION_NUMBER > 0
  625     arg = ap_append_pid(cmd->pool, arg, ".");
  626 #endif
  627     sockname = ap_server_root_relative(cmd->pool, arg);
  628 
  629     if (!sockname) {
  630         return apr_pstrcat(cmd->pool, "Invalid FTPLowPortSock path",
  631                            arg, NULL);
  632     }
  633 
  634     return NULL;
  635 }
  636 
  637 #else /* !APR_HAVE_SOCK_UN_H */
  638 
  639 const char *lowportd_set_socket(cmd_parms *cmd, void *dummy, const char *arg)
  640 {
  641     ap_log_error(APLOG_MARK, APLOG_NOTICE, 0, NULL,
  642                  "FTPLowPortSock directive ignored, this platform does "
  643                  "not support the low-numbered-port daemon");
  644     return NULL;
  645 }
  646 
  647 #endif /* !APR_HAVE_SOCK_UN_H */