Blame support/fcgistarter.c

Packit 90a5c9
/* Licensed to the Apache Software Foundation (ASF) under one or more
Packit 90a5c9
 * contributor license agreements.  See the NOTICE file distributed with
Packit 90a5c9
 * this work for additional information regarding copyright ownership.
Packit 90a5c9
 * The ASF licenses this file to You under the Apache License, Version 2.0
Packit 90a5c9
 * (the "License"); you may not use this file except in compliance with
Packit 90a5c9
 * the License.  You may obtain a copy of the License at
Packit 90a5c9
 *
Packit 90a5c9
 *     http://www.apache.org/licenses/LICENSE-2.0
Packit 90a5c9
 *
Packit 90a5c9
 * Unless required by applicable law or agreed to in writing, software
Packit 90a5c9
 * distributed under the License is distributed on an "AS IS" BASIS,
Packit 90a5c9
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
Packit 90a5c9
 * See the License for the specific language governing permissions and
Packit 90a5c9
 * limitations under the License.
Packit 90a5c9
 */
Packit 90a5c9
Packit 90a5c9
#include <apr.h>
Packit 90a5c9
#include <apr_pools.h>
Packit 90a5c9
#include <apr_network_io.h>
Packit 90a5c9
#include <apr_thread_proc.h>
Packit 90a5c9
#include <apr_getopt.h>
Packit 90a5c9
#include <apr_portable.h>
Packit 90a5c9
Packit 90a5c9
#if APR_HAVE_STDLIB_H
Packit 90a5c9
#include <stdlib.h> /* For EXIT_SUCCESS, EXIT_FAILURE */
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
#if APR_HAVE_UNISTD_H
Packit 90a5c9
#include <unistd.h> /* For execl */
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
static const char *usage_message =
Packit 90a5c9
    "usage: fcgistarter -c <command> -p <port> [-i <interface> -N <num>]\n"
Packit 90a5c9
    "\n"
Packit 90a5c9
    "If an interface is not specified, any available will be used.\n";
Packit 90a5c9
Packit 90a5c9
static void usage(void)
Packit 90a5c9
{
Packit 90a5c9
    fprintf(stderr, "%s", usage_message);
Packit 90a5c9
Packit 90a5c9
    exit(EXIT_FAILURE);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
static void exit_error(apr_status_t rv, const char *func)
Packit 90a5c9
{
Packit 90a5c9
    char buffer[1024];
Packit 90a5c9
Packit 90a5c9
    fprintf(stderr,
Packit 90a5c9
            "%s: %s\n",
Packit 90a5c9
            func,
Packit 90a5c9
            apr_strerror(rv, buffer, sizeof(buffer)));
Packit 90a5c9
Packit 90a5c9
    exit(EXIT_FAILURE);
Packit 90a5c9
}
Packit 90a5c9
Packit 90a5c9
int main(int argc, const char * const argv[])
Packit 90a5c9
{
Packit 90a5c9
    apr_file_t *infd, *skwrapper;
Packit 90a5c9
    apr_sockaddr_t *skaddr;
Packit 90a5c9
    apr_getopt_t *gopt;
Packit 90a5c9
    apr_socket_t *skt;
Packit 90a5c9
    apr_pool_t *pool;
Packit 90a5c9
    apr_status_t rv;
Packit 90a5c9
    apr_proc_t proc;
Packit 90a5c9
Packit 90a5c9
Packit 90a5c9
    /* Command line arguments */
Packit 90a5c9
    int num_to_start = 1, port = 0;
Packit 90a5c9
    const char *interface = NULL;
Packit 90a5c9
    const char *command = NULL;
Packit 90a5c9
Packit 90a5c9
    apr_app_initialize(&argc, &argv, NULL);
Packit 90a5c9
Packit 90a5c9
    atexit(apr_terminate);
Packit 90a5c9
Packit 90a5c9
    apr_pool_create(&pool, NULL);
Packit 90a5c9
Packit 90a5c9
    rv = apr_getopt_init(&gopt, pool, argc, argv);
Packit 90a5c9
    if (rv) {
Packit 90a5c9
        return EXIT_FAILURE;
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    for (;;) {
Packit 90a5c9
        const char *arg;
Packit 90a5c9
        char opt;
Packit 90a5c9
Packit 90a5c9
        rv = apr_getopt(gopt, "c:p:i:N:", &opt, &arg;;
Packit 90a5c9
        if (APR_STATUS_IS_EOF(rv)) {
Packit 90a5c9
            break;
Packit 90a5c9
        } else if (rv) {
Packit 90a5c9
            usage();
Packit 90a5c9
        } else {
Packit 90a5c9
            switch (opt) {
Packit 90a5c9
            case 'c':
Packit 90a5c9
                command = arg;
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'p':
Packit 90a5c9
                port = atoi(arg);
Packit 90a5c9
                if (! port) {
Packit 90a5c9
                    usage();
Packit 90a5c9
                }
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'i':
Packit 90a5c9
                interface = arg;
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            case 'N':
Packit 90a5c9
                num_to_start = atoi(arg);
Packit 90a5c9
                if (! num_to_start) {
Packit 90a5c9
                    usage();
Packit 90a5c9
                }
Packit 90a5c9
                break;
Packit 90a5c9
Packit 90a5c9
            default:
Packit 90a5c9
                break;
Packit 90a5c9
            }
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    if (! command || ! port) {
Packit 90a5c9
        usage();
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    rv = apr_sockaddr_info_get(&skaddr, interface, APR_UNSPEC, port, 0, pool);
Packit 90a5c9
    if (rv) {
Packit 90a5c9
        exit_error(rv, "apr_sockaddr_info_get");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    rv = apr_socket_create(&skt, skaddr->family, SOCK_STREAM, APR_PROTO_TCP, pool);
Packit 90a5c9
    if (rv) {
Packit 90a5c9
        exit_error(rv, "apr_socket_create");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    rv = apr_socket_opt_set(skt, APR_SO_REUSEADDR, 1);
Packit 90a5c9
    if (rv) {
Packit 90a5c9
        exit_error(rv, "apr_socket_opt_set(APR_SO_REUSEADDR)");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    rv = apr_socket_bind(skt, skaddr);
Packit 90a5c9
    if (rv) {
Packit 90a5c9
        exit_error(rv, "apr_socket_bind");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    rv = apr_socket_listen(skt, 1024);
Packit 90a5c9
    if (rv) {
Packit 90a5c9
        exit_error(rv, "apr_socket_listen");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
    rv = apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
Packit 90a5c9
    if (rv) {
Packit 90a5c9
        exit_error(rv, "apr_proc_detach");
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
#if defined(WIN32) || defined(OS2) || defined(NETWARE)
Packit 90a5c9
Packit 90a5c9
#error "Please implement me."
Packit 90a5c9
Packit 90a5c9
#else
Packit 90a5c9
Packit 90a5c9
    while (--num_to_start >= 0) {
Packit 90a5c9
        rv = apr_proc_fork(&proc, pool);
Packit 90a5c9
        if (rv == APR_INCHILD) {
Packit 90a5c9
            apr_os_file_t oft = 0;
Packit 90a5c9
            apr_os_sock_t oskt;
Packit 90a5c9
Packit 90a5c9
            /* Ok, so we need a file that has file descriptor 0 (which
Packit 90a5c9
             * FastCGI wants), but points to our socket.  This isn't really
Packit 90a5c9
             * possible in APR, so we cheat a bit.  I have no idea how to
Packit 90a5c9
             * do this on a non-unix platform, so for now this is platform
Packit 90a5c9
             * specific.  Ick.
Packit 90a5c9
             *
Packit 90a5c9
             * Note that this has to happen post-detach, otherwise fd 0
Packit 90a5c9
             * gets closed during apr_proc_detach and it's all for nothing.
Packit 90a5c9
             *
Packit 90a5c9
             * Unfortunately, doing this post detach means we have no way
Packit 90a5c9
             * to let anyone know if there's a problem at this point :( */
Packit 90a5c9
Packit 90a5c9
            rv = apr_os_file_put(&infd, &oft, APR_READ | APR_WRITE, pool);
Packit 90a5c9
            if (rv) {
Packit 90a5c9
                exit(EXIT_FAILURE);
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            rv = apr_os_sock_get(&oskt, skt);
Packit 90a5c9
            if (rv) {
Packit 90a5c9
                exit(EXIT_FAILURE);
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            rv = apr_os_file_put(&skwrapper, &oskt, APR_READ | APR_WRITE,
Packit 90a5c9
                                 pool);
Packit 90a5c9
            if (rv) {
Packit 90a5c9
                exit(EXIT_FAILURE);
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            rv = apr_file_dup2(infd, skwrapper, pool);
Packit 90a5c9
            if (rv) {
Packit 90a5c9
                exit(EXIT_FAILURE);
Packit 90a5c9
            }
Packit 90a5c9
Packit 90a5c9
            /* XXX Can't use apr_proc_create because there's no way to get
Packit 90a5c9
             *     infd into the procattr without going through another dup2,
Packit 90a5c9
             *     which means by the time it gets to the fastcgi process it
Packit 90a5c9
             *     is no longer fd 0, so it doesn't work.  Sigh. */
Packit 90a5c9
Packit 90a5c9
            execl(command, command, NULL);
Packit 90a5c9
Packit 90a5c9
        } else if (rv == APR_INPARENT) {
Packit 90a5c9
            if (num_to_start == 0) {
Packit 90a5c9
                apr_socket_close(skt);
Packit 90a5c9
            }
Packit 90a5c9
        } else {
Packit 90a5c9
            exit_error(rv, "apr_proc_fork");
Packit 90a5c9
        }
Packit 90a5c9
    }
Packit 90a5c9
Packit 90a5c9
#endif
Packit 90a5c9
Packit 90a5c9
    return EXIT_SUCCESS;
Packit 90a5c9
}