]> granicus.if.org Git - apache/blob - support/fcgistarter.c
fcgistarter: Specify SO_REUSEADDR to allow starting a server
[apache] / support / fcgistarter.c
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
32 static 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
37 static void usage(void)
38 {
39     fprintf(stderr, "%s", usage_message);
40
41     exit(EXIT_FAILURE);
42 }
43
44 static 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
56 int 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 }