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
8 * http://www.apache.org/licenses/LICENSE-2.0
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.
18 #include <apr_pools.h>
19 #include <apr_network_io.h>
20 #include <apr_thread_proc.h>
21 #include <apr_getopt.h>
22 #include <apr_portable.h>
25 #include <stdlib.h> /* For EXIT_SUCCESS, EXIT_FAILURE */
29 #include <unistd.h> /* For execl */
32 static const char *usage_message =
33 "usage: fcgistarter -c <command> -p <port> [-i <interface> -N <num>]\n"
35 "If an interface is not specified, any available will be used.\n";
37 static void usage(void)
39 fprintf(stderr, "%s", usage_message);
44 static void exit_error(apr_status_t rv, const char *func)
51 apr_strerror(rv, buffer, sizeof(buffer)));
56 int main(int argc, const char * const argv[])
58 apr_file_t *infd, *skwrapper;
59 apr_sockaddr_t *skaddr;
67 /* Command line arguments */
68 int num_to_start = 1, port = 0;
69 const char *interface = NULL;
70 const char *command = NULL;
72 apr_app_initialize(&argc, &argv, NULL);
74 atexit(apr_terminate);
76 apr_pool_create(&pool, NULL);
78 rv = apr_getopt_init(&gopt, pool, argc, argv);
87 rv = apr_getopt(gopt, "c:p:i:N:", &opt, &arg);
88 if (APR_STATUS_IS_EOF(rv)) {
110 num_to_start = atoi(arg);
111 if (! num_to_start) {
122 if (! command || ! port) {
126 rv = apr_sockaddr_info_get(&skaddr, interface, APR_UNSPEC, port, 0, pool);
128 exit_error(rv, "apr_sockaddr_info_get");
131 rv = apr_socket_create(&skt, skaddr->family, SOCK_STREAM, APR_PROTO_TCP, pool);
133 exit_error(rv, "apr_socket_create");
136 rv = apr_socket_opt_set(skt, APR_SO_REUSEADDR, 1);
138 exit_error(rv, "apr_socket_opt_set(APR_SO_REUSEADDR)");
141 rv = apr_socket_bind(skt, skaddr);
143 exit_error(rv, "apr_socket_bind");
146 rv = apr_socket_listen(skt, 1024);
148 exit_error(rv, "apr_socket_listen");
151 rv = apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
153 exit_error(rv, "apr_proc_detach");
156 #if defined(WIN32) || defined(OS2) || defined(NETWARE)
158 #error "Please implement me."
162 while (--num_to_start >= 0) {
163 rv = apr_proc_fork(&proc, pool);
164 if (rv == APR_INCHILD) {
165 apr_os_file_t oft = 0;
168 /* Ok, so we need a file that has file descriptor 0 (which
169 * FastCGI wants), but points to our socket. This isn't really
170 * possible in APR, so we cheat a bit. I have no idea how to
171 * do this on a non-unix platform, so for now this is platform
174 * Note that this has to happen post-detach, otherwise fd 0
175 * gets closed during apr_proc_detach and it's all for nothing.
177 * Unfortunately, doing this post detach means we have no way
178 * to let anyone know if there's a problem at this point :( */
180 rv = apr_os_file_put(&infd, &oft, APR_READ | APR_WRITE, pool);
185 rv = apr_os_sock_get(&oskt, skt);
190 rv = apr_os_file_put(&skwrapper, &oskt, APR_READ | APR_WRITE,
196 rv = apr_file_dup2(infd, skwrapper, pool);
201 /* XXX Can't use apr_proc_create because there's no way to get
202 * infd into the procattr without going through another dup2,
203 * which means by the time it gets to the fastcgi process it
204 * is no longer fd 0, so it doesn't work. Sigh. */
206 execl(command, command, NULL);
208 } else if (rv == APR_INPARENT) {
209 if (num_to_start == 0) {
210 apr_socket_close(skt);
213 exit_error(rv, "apr_proc_fork");