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
|
}
|