]> granicus.if.org Git - apache/blob - support/fcgistarter.c
Merge the fcgi-proxy-dev branch to trunk, adding a FastCGI back end for
[apache] / support / fcgistarter.c
1 /* Copyright 2006 The Apache Software Foundation or its licensors, as
2  * applicable.
3  *
4  * Licensed under the Apache License, Version 2.0 (the "License");
5  * you may not use this file except in compliance with the License.
6  * 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()
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 *argv[])
57 {
58     apr_file_t *infd, *skwrapper;
59     apr_sockaddr_t *skaddr;
60     apr_procattr_t *pattr;
61     apr_getopt_t *gopt;
62     apr_socket_t *skt;
63     apr_pool_t *pool;
64     apr_status_t rv;
65     apr_proc_t proc;
66
67
68     /* Command line arguments */
69     int num_to_start = 1, port = 0;
70     const char *interface = NULL;
71     const char *command = NULL;
72
73     apr_initialize();
74
75     atexit(apr_terminate);
76
77     apr_pool_create(&pool, NULL);
78
79     rv = apr_getopt_init(&gopt, pool, argc, argv);
80     if (rv) {
81         return EXIT_FAILURE;
82     }
83
84     for (;;) {
85         const char *arg;
86         char opt;
87
88         rv = apr_getopt(gopt, "c:p:i:N:", &opt, &arg);
89         if (APR_STATUS_IS_EOF(rv)) {
90             break;
91         } else if (rv) {
92             usage();
93         } else {
94             switch (opt) {
95             case 'c':
96                 command = arg;
97                 break;
98
99             case 'p':
100                 port = atoi(arg);
101                 if (! port) {
102                     usage();
103                 }
104                 break;
105
106             case 'i':
107                 interface = arg;
108                 break;
109
110             case 'N':
111                 num_to_start = atoi(arg);
112                 if (! num_to_start) {
113                     usage();
114                 }
115                 break;
116
117             default:
118                 break;
119             }
120         }
121     }
122
123     if (! command || ! port) {
124         usage();
125     }
126
127     rv = apr_socket_create(&skt, APR_INET, SOCK_STREAM, APR_PROTO_TCP, pool);
128     if (rv) {
129         exit_error(rv, "apr_socket_create");
130     }
131
132     rv = apr_sockaddr_info_get(&skaddr, interface, APR_UNSPEC, port, 0, pool);
133     if (rv) {
134         exit_error(rv, "apr_sockaddr_info_get");
135     }
136
137     rv = apr_socket_bind(skt, skaddr);
138     if (rv) {
139         exit_error(rv, "apr_socket_bind");
140     }
141
142     rv = apr_socket_listen(skt, 1024);
143     if (rv) {
144         exit_error(rv, "apr_socket_listen");
145     }
146
147     while (--num_to_start >= 0) {
148         rv = apr_proc_fork(&proc, pool);
149         if (rv == APR_INCHILD) {
150             apr_os_file_t oft = 0;
151             apr_os_sock_t oskt;
152
153             rv = apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
154             if (rv) {
155                 exit_error(rv, "apr_proc_detach");
156             }
157
158 #if defined(WIN32) || defined(OS2) || defined(NETWARE)
159 #error "Please implement me."
160 #else
161
162             /* Ok, so we need a file that has file descriptor 0 (which
163              * FastCGI wants), but points to our socket.  This isn't really
164              * possible in APR, so we cheat a bit.  I have no idea how to
165              * do this on a non-unix platform, so for now this is platform
166              * specific.  Ick.
167              *
168              * Note that this has to happen post-detach, otherwise fd 0
169              * gets closed during apr_proc_detach and it's all for nothing.
170              *
171              * Unfortunately, doing this post detach means we have no way
172              * to let anyone know if there's a problem at this point :( */
173
174             rv = apr_os_file_put(&infd, &oft, APR_READ | APR_WRITE, pool);
175             if (rv) {
176                 exit(EXIT_FAILURE);
177             }
178
179             rv = apr_os_sock_get(&oskt, skt);
180             if (rv) {
181                 exit(EXIT_FAILURE);
182             }
183
184             rv = apr_os_file_put(&skwrapper, &oskt, APR_READ | APR_WRITE,
185                                  pool);
186             if (rv) {
187                 exit(EXIT_FAILURE);
188             }
189
190             rv = apr_file_dup2(infd, skwrapper, pool);
191             if (rv) {
192                 exit(EXIT_FAILURE);
193             }
194
195             /* XXX Can't use apr_proc_create because there's no way to get
196              *     infd into the procattr without going through another dup2,
197              *     which means by the time it gets to the fastcgi process it
198              *     is no longer fd 0, so it doesn't work.  Sigh. */
199
200             execl(command, NULL);
201 #endif
202         } else if (rv == APR_INPARENT) {
203             if (num_to_start == 0) {
204                 apr_socket_close(skt);
205             }
206         } else {
207             exit_error(rv, "apr_proc_fork");
208         }
209     }
210
211     return EXIT_SUCCESS;
212 }