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#include <apr.h>
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>
23
24#if APR_HAVE_STDLIB_H
25#include <stdlib.h> /* For EXIT_SUCCESS, EXIT_FAILURE */
26#endif
27
28#if APR_HAVE_UNISTD_H
29#include <unistd.h> /* For execl */
30#endif
31
32static const char *usage_message =
33    "usage: fcgistarter -c <command> -p <port> [-i <interface> -N <num>]\n"
34    "\n"
35    "If an interface is not specified, any available will be used.\n";
36
37static void usage(void)
38{
39    fprintf(stderr, "%s", usage_message);
40
41    exit(EXIT_FAILURE);
42}
43
44static void exit_error(apr_status_t rv, const char *func)
45{
46    char buffer[1024];
47
48    fprintf(stderr,
49            "%s: %s\n",
50            func,
51            apr_strerror(rv, buffer, sizeof(buffer)));
52
53    exit(EXIT_FAILURE);
54}
55
56int main(int argc, const char * const argv[])
57{
58    apr_file_t *infd, *skwrapper;
59    apr_sockaddr_t *skaddr;
60    apr_getopt_t *gopt;
61    apr_socket_t *skt;
62    apr_pool_t *pool;
63    apr_status_t rv;
64    apr_proc_t proc;
65
66
67    /* Command line arguments */
68    int num_to_start = 1, port = 0;
69    const char *interface = NULL;
70    const char *command = NULL;
71
72    apr_app_initialize(&argc, &argv, NULL);
73
74    atexit(apr_terminate);
75
76    apr_pool_create(&pool, NULL);
77
78    rv = apr_getopt_init(&gopt, pool, argc, argv);
79    if (rv) {
80        return EXIT_FAILURE;
81    }
82
83    for (;;) {
84        const char *arg;
85        char opt;
86
87        rv = apr_getopt(gopt, "c:p:i:N:", &opt, &arg);
88        if (APR_STATUS_IS_EOF(rv)) {
89            break;
90        } else if (rv) {
91            usage();
92        } else {
93            switch (opt) {
94            case 'c':
95                command = arg;
96                break;
97
98            case 'p':
99                port = atoi(arg);
100                if (! port) {
101                    usage();
102                }
103                break;
104
105            case 'i':
106                interface = arg;
107                break;
108
109            case 'N':
110                num_to_start = atoi(arg);
111                if (! num_to_start) {
112                    usage();
113                }
114                break;
115
116            default:
117                break;
118            }
119        }
120    }
121
122    if (! command || ! port) {
123        usage();
124    }
125
126    rv = apr_sockaddr_info_get(&skaddr, interface, APR_UNSPEC, port, 0, pool);
127    if (rv) {
128        exit_error(rv, "apr_sockaddr_info_get");
129    }
130
131    rv = apr_socket_create(&skt, skaddr->family, SOCK_STREAM, APR_PROTO_TCP, pool);
132    if (rv) {
133        exit_error(rv, "apr_socket_create");
134    }
135
136    rv = apr_socket_opt_set(skt, APR_SO_REUSEADDR, 1);
137    if (rv) {
138        exit_error(rv, "apr_socket_opt_set(APR_SO_REUSEADDR)");
139    }
140
141    rv = apr_socket_bind(skt, skaddr);
142    if (rv) {
143        exit_error(rv, "apr_socket_bind");
144    }
145
146    rv = apr_socket_listen(skt, 1024);
147    if (rv) {
148        exit_error(rv, "apr_socket_listen");
149    }
150
151    rv = apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
152    if (rv) {
153        exit_error(rv, "apr_proc_detach");
154    }
155
156#if defined(WIN32) || defined(OS2) || defined(NETWARE)
157
158#error "Please implement me."
159
160#else
161
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;
166            apr_os_sock_t oskt;
167
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
172             * specific.  Ick.
173             *
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.
176             *
177             * Unfortunately, doing this post detach means we have no way
178             * to let anyone know if there's a problem at this point :( */
179
180            rv = apr_os_file_put(&infd, &oft, APR_READ | APR_WRITE, pool);
181            if (rv) {
182                exit(EXIT_FAILURE);
183            }
184
185            rv = apr_os_sock_get(&oskt, skt);
186            if (rv) {
187                exit(EXIT_FAILURE);
188            }
189
190            rv = apr_os_file_put(&skwrapper, &oskt, APR_READ | APR_WRITE,
191                                 pool);
192            if (rv) {
193                exit(EXIT_FAILURE);
194            }
195
196            rv = apr_file_dup2(infd, skwrapper, pool);
197            if (rv) {
198                exit(EXIT_FAILURE);
199            }
200
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. */
205
206            execl(command, command, NULL);
207
208        } else if (rv == APR_INPARENT) {
209            if (num_to_start == 0) {
210                apr_socket_close(skt);
211            }
212        } else {
213            exit_error(rv, "apr_proc_fork");
214        }
215    }
216
217#endif
218
219    return EXIT_SUCCESS;
220}
221