diff options
Diffstat (limited to 'support/fcgistarter.c')
-rw-r--r-- | support/fcgistarter.c | 220 |
1 files changed, 220 insertions, 0 deletions
diff --git a/support/fcgistarter.c b/support/fcgistarter.c new file mode 100644 index 0000000..9cfda51 --- /dev/null +++ b/support/fcgistarter.c @@ -0,0 +1,220 @@ +/* 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 <apr.h> +#include <apr_pools.h> +#include <apr_network_io.h> +#include <apr_thread_proc.h> +#include <apr_getopt.h> +#include <apr_portable.h> + +#if APR_HAVE_STDLIB_H +#include <stdlib.h> /* For EXIT_SUCCESS, EXIT_FAILURE */ +#endif + +#if APR_HAVE_UNISTD_H +#include <unistd.h> /* For execl */ +#endif + +static const char *usage_message = + "usage: fcgistarter -c <command> -p <port> [-i <interface> -N <num>]\n" + "\n" + "If an interface is not specified, any available will be used.\n"; + +static void usage(void) +{ + fprintf(stderr, "%s", usage_message); + + exit(EXIT_FAILURE); +} + +static void exit_error(apr_status_t rv, const char *func) +{ + char buffer[1024]; + + fprintf(stderr, + "%s: %s\n", + func, + apr_strerror(rv, buffer, sizeof(buffer))); + + exit(EXIT_FAILURE); +} + +int main(int argc, const char * const argv[]) +{ + apr_file_t *infd, *skwrapper; + apr_sockaddr_t *skaddr; + apr_getopt_t *gopt; + apr_socket_t *skt; + apr_pool_t *pool; + apr_status_t rv; + apr_proc_t proc; + + + /* Command line arguments */ + int num_to_start = 1, port = 0; + const char *interface = NULL; + const char *command = NULL; + + apr_app_initialize(&argc, &argv, NULL); + + atexit(apr_terminate); + + apr_pool_create(&pool, NULL); + + rv = apr_getopt_init(&gopt, pool, argc, argv); + if (rv) { + return EXIT_FAILURE; + } + + for (;;) { + const char *arg; + char opt; + + rv = apr_getopt(gopt, "c:p:i:N:", &opt, &arg); + if (APR_STATUS_IS_EOF(rv)) { + break; + } else if (rv) { + usage(); + } else { + switch (opt) { + case 'c': + command = arg; + break; + + case 'p': + port = atoi(arg); + if (! port) { + usage(); + } + break; + + case 'i': + interface = arg; + break; + + case 'N': + num_to_start = atoi(arg); + if (! num_to_start) { + usage(); + } + break; + + default: + break; + } + } + } + + if (! command || ! port) { + usage(); + } + + rv = apr_sockaddr_info_get(&skaddr, interface, APR_UNSPEC, port, 0, pool); + if (rv) { + exit_error(rv, "apr_sockaddr_info_get"); + } + + rv = apr_socket_create(&skt, skaddr->family, SOCK_STREAM, APR_PROTO_TCP, pool); + if (rv) { + exit_error(rv, "apr_socket_create"); + } + + rv = apr_socket_opt_set(skt, APR_SO_REUSEADDR, 1); + if (rv) { + exit_error(rv, "apr_socket_opt_set(APR_SO_REUSEADDR)"); + } + + rv = apr_socket_bind(skt, skaddr); + if (rv) { + exit_error(rv, "apr_socket_bind"); + } + + rv = apr_socket_listen(skt, 1024); + if (rv) { + exit_error(rv, "apr_socket_listen"); + } + + rv = apr_proc_detach(APR_PROC_DETACH_DAEMONIZE); + if (rv) { + exit_error(rv, "apr_proc_detach"); + } + +#if defined(WIN32) || defined(OS2) || defined(NETWARE) + +#error "Please implement me." + +#else + + while (--num_to_start >= 0) { + rv = apr_proc_fork(&proc, pool); + if (rv == APR_INCHILD) { + apr_os_file_t oft = 0; + apr_os_sock_t oskt; + + /* Ok, so we need a file that has file descriptor 0 (which + * FastCGI wants), but points to our socket. This isn't really + * possible in APR, so we cheat a bit. I have no idea how to + * do this on a non-unix platform, so for now this is platform + * specific. Ick. + * + * Note that this has to happen post-detach, otherwise fd 0 + * gets closed during apr_proc_detach and it's all for nothing. + * + * Unfortunately, doing this post detach means we have no way + * to let anyone know if there's a problem at this point :( */ + + rv = apr_os_file_put(&infd, &oft, APR_READ | APR_WRITE, pool); + if (rv) { + exit(EXIT_FAILURE); + } + + rv = apr_os_sock_get(&oskt, skt); + if (rv) { + exit(EXIT_FAILURE); + } + + rv = apr_os_file_put(&skwrapper, &oskt, APR_READ | APR_WRITE, + pool); + if (rv) { + exit(EXIT_FAILURE); + } + + rv = apr_file_dup2(infd, skwrapper, pool); + if (rv) { + exit(EXIT_FAILURE); + } + + /* XXX Can't use apr_proc_create because there's no way to get + * infd into the procattr without going through another dup2, + * which means by the time it gets to the fastcgi process it + * is no longer fd 0, so it doesn't work. Sigh. */ + + execl(command, command, NULL); + + } else if (rv == APR_INPARENT) { + if (num_to_start == 0) { + apr_socket_close(skt); + } + } else { + exit_error(rv, "apr_proc_fork"); + } + } + +#endif + + return EXIT_SUCCESS; +} |