]> granicus.if.org Git - apache/blob - support/fcgistarter.c
update license header text
[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 *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_initialize();
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_socket_create(&skt, APR_INET, SOCK_STREAM, APR_PROTO_TCP, pool);
127     if (rv) {
128         exit_error(rv, "apr_socket_create");
129     }
130
131     rv = apr_sockaddr_info_get(&skaddr, interface, APR_UNSPEC, port, 0, pool);
132     if (rv) {
133         exit_error(rv, "apr_sockaddr_info_get");
134     }
135
136     rv = apr_socket_bind(skt, skaddr);
137     if (rv) {
138         exit_error(rv, "apr_socket_bind");
139     }
140
141     rv = apr_socket_listen(skt, 1024);
142     if (rv) {
143         exit_error(rv, "apr_socket_listen");
144     }
145
146     rv = apr_proc_detach(APR_PROC_DETACH_DAEMONIZE);
147     if (rv) {
148         exit_error(rv, "apr_proc_detach");
149     }
150
151     while (--num_to_start >= 0) {
152         rv = apr_proc_fork(&proc, pool);
153         if (rv == APR_INCHILD) {
154             apr_os_file_t oft = 0;
155             apr_os_sock_t oskt;
156
157 #if defined(WIN32) || defined(OS2) || defined(NETWARE)
158 #error "Please implement me."
159 #else
160
161             /* Ok, so we need a file that has file descriptor 0 (which
162              * FastCGI wants), but points to our socket.  This isn't really
163              * possible in APR, so we cheat a bit.  I have no idea how to
164              * do this on a non-unix platform, so for now this is platform
165              * specific.  Ick.
166              *
167              * Note that this has to happen post-detach, otherwise fd 0
168              * gets closed during apr_proc_detach and it's all for nothing.
169              *
170              * Unfortunately, doing this post detach means we have no way
171              * to let anyone know if there's a problem at this point :( */
172
173             rv = apr_os_file_put(&infd, &oft, APR_READ | APR_WRITE, pool);
174             if (rv) {
175                 exit(EXIT_FAILURE);
176             }
177
178             rv = apr_os_sock_get(&oskt, skt);
179             if (rv) {
180                 exit(EXIT_FAILURE);
181             }
182
183             rv = apr_os_file_put(&skwrapper, &oskt, APR_READ | APR_WRITE,
184                                  pool);
185             if (rv) {
186                 exit(EXIT_FAILURE);
187             }
188
189             rv = apr_file_dup2(infd, skwrapper, pool);
190             if (rv) {
191                 exit(EXIT_FAILURE);
192             }
193
194             /* XXX Can't use apr_proc_create because there's no way to get
195              *     infd into the procattr without going through another dup2,
196              *     which means by the time it gets to the fastcgi process it
197              *     is no longer fd 0, so it doesn't work.  Sigh. */
198
199             execl(command, command, NULL);
200 #endif
201         } else if (rv == APR_INPARENT) {
202             if (num_to_start == 0) {
203                 apr_socket_close(skt);
204             }
205         } else {
206             exit_error(rv, "apr_proc_fork");
207         }
208     }
209
210     return EXIT_SUCCESS;
211 }