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