Project

General

Profile

spawn-fcgi-win32.c

keathmilligan, 2009-01-21 15:15

 
1
#include <sys/types.h>
2
#include <sys/time.h>
3
#include <sys/stat.h>
4
#include <stdlib.h>
5
#include <string.h>
6
#include <errno.h>
7
#include <stdio.h>
8
#include <unistd.h>
9
#include <fcntl.h>
10
#include <getopt.h>
11
#include <ws2tcpip.h>
12

    
13
#define FCGI_LISTENSOCK_FILENO 0
14

    
15
#define UNIX_PATH_LEN 108
16

    
17
typedef unsigned short int sa_family_t;
18

    
19
struct sockaddr_un {
20
        sa_family_t        sun_family;              /* address family AF_LOCAL/AF_UNIX */
21
        char                sun_path[UNIX_PATH_LEN]; /* 108 bytes of socket address     */
22
};
23

    
24
/* Evaluates the actual length of `sockaddr_un' structure. */
25
#define SUN_LEN(p) ((size_t)(((struct sockaddr_un *) NULL)->sun_path) \
26
                                   + strlen ((p)->sun_path))
27

    
28

    
29
int fcgi_spawn_connection(char *appPath, char **appArgv, char *addr, unsigned short port, const char *unixsocket, int fork_count, int child_count, int pid_fd, int nofork) {
30
        SOCKET fcgi_fd;
31
        int socket_type, rc = 0;
32

    
33
        struct sockaddr_un fcgi_addr_un;
34
        struct sockaddr_in fcgi_addr_in;
35
        struct sockaddr *fcgi_addr;
36

    
37
        socklen_t servlen;
38

    
39
    WORD  wVersion;
40
    WSADATA wsaData;
41

    
42
    if (child_count < 2) {
43
                child_count = 5;
44
        }
45

    
46
        if (child_count > 256) {
47
                child_count = 256;
48
        }
49

    
50

    
51
        if (unixsocket) {
52
                memset(&fcgi_addr, 0, sizeof(fcgi_addr));
53

    
54
                fcgi_addr_un.sun_family = AF_UNIX;
55
                strcpy(fcgi_addr_un.sun_path, unixsocket);
56

    
57
#ifdef SUN_LEN
58
                servlen = SUN_LEN(&fcgi_addr_un);
59
#else
60
                /* stevens says: */
61
                servlen = strlen(fcgi_addr_un.sun_path) + sizeof(fcgi_addr_un.sun_family);
62
#endif
63
                socket_type = AF_UNIX;
64
                fcgi_addr = (struct sockaddr *) &fcgi_addr_un;
65
        } else {
66
                fcgi_addr_in.sin_family = AF_INET;
67
                if (addr != NULL) {
68
                        fcgi_addr_in.sin_addr.s_addr = inet_addr(addr);
69
                } else {
70
                        fcgi_addr_in.sin_addr.s_addr = htonl(INADDR_ANY);
71
                }
72
                fcgi_addr_in.sin_port = htons(port);
73
                servlen = sizeof(fcgi_addr_in);
74

    
75
                socket_type = AF_INET;
76
                fcgi_addr = (struct sockaddr *) &fcgi_addr_in;
77
        }
78

    
79
    /*
80
     * Initialize windows sockets library.
81
     */
82
    wVersion = MAKEWORD(2,0);
83
    if (WSAStartup( wVersion, &wsaData )) {
84
                fprintf(stderr, "%s.%d: error %d starting Windows sockets\n",
85
                        __FILE__, __LINE__, WSAGetLastError());
86
    }
87

    
88
        if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) {
89
                fprintf(stderr, "%s.%d\n",
90
                        __FILE__, __LINE__);
91
                return -1;
92
        }
93

    
94
        if (-1 == connect(fcgi_fd, fcgi_addr, servlen)) {
95
                /* server is not up, spawn in  */
96
                int val;
97

    
98
                if (unixsocket) unlink(unixsocket);
99

    
100
                close(fcgi_fd);
101

    
102
                /* reopen socket */
103
                if (-1 == (fcgi_fd = socket(socket_type, SOCK_STREAM, 0))) {
104
                        fprintf(stderr, "%s.%d\n",
105
                                __FILE__, __LINE__);
106
                        return -1;
107
                }
108

    
109
                val = 1;
110
                if (setsockopt(fcgi_fd, SOL_SOCKET, SO_REUSEADDR,         (const char*)&val, sizeof(val)) < 0) {
111
                        fprintf(stderr, "%s.%d\n",
112
                                __FILE__, __LINE__);
113
                        return -1;
114
                }
115

    
116
                /* create socket */
117
                if (-1 == bind(fcgi_fd, fcgi_addr, servlen)) {
118
                        fprintf(stderr, "%s.%d: bind failed: %s\n",
119
                                __FILE__, __LINE__,
120
                                strerror(errno));
121
                        return -1;
122
                }
123

    
124
                if (-1 == listen(fcgi_fd, 1024)) {
125
                        fprintf(stderr, "%s.%d: fd = -1\n",
126
                                __FILE__, __LINE__);
127
                        return -1;
128
                }
129

    
130

    
131
                while (fork_count-- > 0) {
132

    
133
                        PROCESS_INFORMATION pi;
134
                        STARTUPINFO si;
135

    
136
                        ZeroMemory(&si,sizeof(STARTUPINFO));
137
                        si.cb = sizeof(STARTUPINFO);
138
                        si.dwFlags = STARTF_USESTDHANDLES;
139
                        si.hStdOutput = INVALID_HANDLE_VALUE;
140
                        si.hStdInput  = (HANDLE)fcgi_fd;
141
                        si.hStdError  = INVALID_HANDLE_VALUE;
142
                        if (!CreateProcess(NULL,appPath,NULL,NULL,TRUE,        CREATE_NO_WINDOW,NULL,NULL,&si,&pi)) {
143
                                fprintf(stderr, "%s.%d: CreateProcess failed\n",
144
                                        __FILE__, __LINE__);
145
                                return -1;
146
                        } else {
147
                                fprintf(stdout, "%s.%d: child spawned successfully: PID: %lu\n",
148
                                        __FILE__, __LINE__,
149
                                        pi.dwProcessId);
150
                                CloseHandle(pi.hThread);
151
                        }
152

    
153
                }
154

    
155
        } else {
156
                fprintf(stderr, "%s.%d: socket is already used, can't spawn\n",
157
                        __FILE__, __LINE__);
158
                return -1;
159
        }
160

    
161

    
162
        closesocket(fcgi_fd);
163

    
164
        return rc;
165
}
166

    
167

    
168
void show_version () {
169
        char *b = "spawn-fcgi-win32 - spawns fastcgi processes\n";
170
        write(1, b, strlen(b));
171
}
172

    
173
void show_help () {
174
        char *b =
175
"Usage: spawn-fcgi [options] -- <fcgiapp> [fcgi app arguments]\n"
176
"\n"
177
"spawn-fcgi-win32 - spawns fastcgi processes\n"
178
"\n"
179
"Options:\n"
180
" -f <fcgiapp> filename of the fcgi-application\n"
181
" -a <addr>    bind to ip address\n"
182
" -p <port>    bind to tcp-port\n"
183
" -s <path>    bind to unix-domain socket\n"
184
" -C <childs>  (PHP only) numbers of childs to spawn (default 5)\n"
185
" -F <childs>  numbers of childs to fork (default 1)\n"
186
" -P <path>    name of PID-file for spawed process\n"
187
" -n           no fork (for daemontools)\n"
188
" -v           show version\n"
189
" -h           show this help\n"
190
"(root only)\n"
191
" -c <dir>     chroot to directory\n"
192
" -u <user>    change to user-id\n"
193
" -g <group>   change to group-id\n"
194
;
195
        write(1, b, strlen(b));
196
}
197

    
198

    
199
int main(int argc, char **argv) {
200
        char *fcgi_app = NULL, *unixsocket = NULL, *pid_file = NULL,
201
                *addr = NULL;
202
        char **fcgi_app_argv = { NULL };
203
        unsigned short port = 0;
204
        int child_count = 5;
205
        int fork_count = 1;
206
        int pid_fd = -1;
207
        int nofork = 0;
208
        int o;
209
        struct sockaddr_un un;
210

    
211
        while (-1 != (o = getopt(argc, argv, "c:f:g:hna:p:u:vC:F:s:P:"))) {
212
                switch(o) {
213
                case 'f': fcgi_app = optarg; break;
214
                case 'a': addr = optarg;/* ip addr */ break;
215
                case 'p': port = strtol(optarg, NULL, 10);/* port */ break;
216
                case 'C': child_count = strtol(optarg, NULL, 10);/*  */ break;
217
                case 'F': fork_count = strtol(optarg, NULL, 10);/*  */ break;
218
                case 's': unixsocket = optarg; /* unix-domain socket */ break;
219
                case 'n': nofork = 1; break;
220
                case 'P': pid_file = optarg; /* PID file */ break;
221
                case 'v': show_version(); return 0;
222
                case 'h': show_help(); return 0;
223
                default:
224
                        show_help();
225
                        return -1;
226
                }
227
        }
228

    
229
        if (optind < argc) {
230
                fcgi_app_argv = &argv[optind];
231
        }
232

    
233
        if ((fcgi_app == NULL && fcgi_app_argv == NULL) || (port == 0 && unixsocket == NULL)) {
234
                show_help();
235
                return -1;
236
        }
237

    
238
        if (unixsocket && port) {
239
                fprintf(stderr, "%s.%d: %s\n",
240
                        __FILE__, __LINE__,
241
                        "either a unix domain socket or a tcp-port, but not both\n");
242

    
243
                return -1;
244
        }
245

    
246
        if (unixsocket && strlen(unixsocket) > sizeof(un.sun_path) - 1) {
247
                fprintf(stderr, "%s.%d: %s\n",
248
                        __FILE__, __LINE__,
249
                        "path of the unix socket is too long\n");
250

    
251
                return -1;
252
        }
253

    
254
        if (pid_file &&
255
            (-1 == (pid_fd = open(pid_file, O_WRONLY | O_CREAT | O_EXCL | O_TRUNC)))) {
256
                struct stat st;
257
                if (errno != EEXIST) {
258
                        fprintf(stderr, "%s.%d: opening pid-file '%s' failed: %s\n",
259
                                __FILE__, __LINE__,
260
                                pid_file, strerror(errno));
261

    
262
                        return -1;
263
                }
264

    
265
                /* ok, file exists */
266

    
267
                if (0 != stat(pid_file, &st)) {
268
                        fprintf(stderr, "%s.%d: stating pid-file '%s' failed: %s\n",
269
                                __FILE__, __LINE__,
270
                                pid_file, strerror(errno));
271

    
272
                        return -1;
273
                }
274

    
275
                /* is it a regular file ? */
276

    
277
                if (!S_ISREG(st.st_mode)) {
278
                        fprintf(stderr, "%s.%d: pid-file exists and isn't regular file: '%s'\n",
279
                                __FILE__, __LINE__,
280
                                pid_file);
281

    
282
                        return -1;
283
                }
284

    
285
                if (-1 == (pid_fd = open(pid_file, O_WRONLY | O_CREAT | O_TRUNC))) {
286
                        fprintf(stderr, "%s.%d: opening pid-file '%s' failed: %s\n",
287
                                __FILE__, __LINE__,
288
                                pid_file, strerror(errno));
289

    
290
                        return -1;
291
                }
292
        }
293

    
294
    return fcgi_spawn_connection(fcgi_app, fcgi_app_argv, addr, port, unixsocket, fork_count, child_count, pid_fd, nofork);
295
}