[WIN32] Add (back) support for use of TerminateProcess() to accomodate
[mod_fastcgi.git] / fcgi_util.c
blobcf1c9d034f4235bfbb2002743ed56122cafabf44
1 /*
2 * $Id: fcgi_util.c,v 1.21 2002/02/13 02:50:24 robs Exp $
3 */
5 #include "fcgi.h"
7 #ifdef WIN32
8 #pragma warning( disable : 4100 )
9 #endif
11 /*******************************************************************************
12 * Compute printable MD5 hash. Pool p is used for scratch as well as for
13 * allocating the hash - use temp storage, and dup it if you need to keep it.
15 char *
16 fcgi_util_socket_hash_filename(pool *p, const char *path,
17 const char *user, const char *group)
19 char *buf = ap_pstrcat(p, path, user, group, NULL);
21 /* Canonicalize the path (remove "//", ".", "..") */
22 ap_getparents(buf);
24 return ap_md5(p, (unsigned char *)buf);
28 /*******************************************************************************
29 * Concat src1 and src2 using the approprate path seperator for the platform.
31 static char * make_full_path(pool *a, const char *src1, const char *src2)
33 #ifdef WIN32
34 register int x;
35 char * p ;
36 char * q ;
38 x = strlen(src1);
40 if (x == 0) {
41 p = ap_pstrcat(a, "\\", src2, NULL);
43 else if (src1[x - 1] != '\\' && src1[x - 1] != '/') {
44 p = ap_pstrcat(a, src1, "\\", src2, NULL);
46 else {
47 p = ap_pstrcat(a, src1, src2, NULL);
50 q = p ;
51 while (*q)
53 if (*q == '/') {
54 *q = '\\' ;
56 ++q;
59 return p ;
60 #else
61 return ap_make_full_path(a, src1, src2);
62 #endif
65 /*******************************************************************************
66 * Return absolute path to file in either "regular" FCGI socket directory or
67 * the dynamic directory. Result is allocated in pool p.
69 const char *
70 fcgi_util_socket_make_path_absolute(pool * const p,
71 const char *const file, const int dynamic)
73 if (ap_os_is_path_absolute(file))
75 return file;
77 else
79 const char * parent_dir = dynamic ? fcgi_dynamic_dir : fcgi_socket_dir;
80 return (const char *) make_full_path(p, parent_dir, file);
84 #ifndef WIN32
85 /*******************************************************************************
86 * Build a Domain Socket Address structure, and calculate its size.
87 * The error message is allocated from the pool p. If you don't want the
88 * struct sockaddr_un also allocated from p, pass it preallocated (!=NULL).
90 const char *
91 fcgi_util_socket_make_domain_addr(pool *p, struct sockaddr_un **socket_addr,
92 int *socket_addr_len, const char *socket_path)
94 int socket_pathLen = strlen(socket_path);
96 if (socket_pathLen >= sizeof((*socket_addr)->sun_path)) {
97 return ap_pstrcat(p, "path \"", socket_path,
98 "\" is too long for a Domain socket", NULL);
101 if (*socket_addr == NULL)
102 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_un));
103 else
104 memset(*socket_addr, 0, sizeof(struct sockaddr_un));
106 (*socket_addr)->sun_family = AF_UNIX;
107 strcpy((*socket_addr)->sun_path, socket_path);
109 *socket_addr_len = SUN_LEN(*socket_addr);
110 return NULL;
112 #endif
114 /*******************************************************************************
115 * Convert a hostname or IP address string to an in_addr struct.
117 static int
118 convert_string_to_in_addr(const char * const hostname, struct in_addr * const addr)
120 struct hostent *hp;
121 int count;
123 addr->s_addr = inet_addr((char *)hostname);
125 if (addr->s_addr == INADDR_NONE) {
126 if ((hp = gethostbyname((char *)hostname)) == NULL)
127 return -1;
129 memcpy((char *) addr, hp->h_addr, hp->h_length);
130 count = 0;
131 while (hp->h_addr_list[count] != 0)
132 count++;
134 return count;
136 return 1;
140 /*******************************************************************************
141 * Build an Inet Socket Address structure, and calculate its size.
142 * The error message is allocated from the pool p. If you don't want the
143 * struct sockaddr_in also allocated from p, pass it preallocated (!=NULL).
145 const char *
146 fcgi_util_socket_make_inet_addr(pool *p, struct sockaddr_in **socket_addr,
147 int *socket_addr_len, const char *host, unsigned short port)
149 if (*socket_addr == NULL)
150 *socket_addr = ap_pcalloc(p, sizeof(struct sockaddr_in));
151 else
152 memset(*socket_addr, 0, sizeof(struct sockaddr_in));
154 (*socket_addr)->sin_family = AF_INET;
155 (*socket_addr)->sin_port = htons(port);
157 /* Get an in_addr represention of the host */
158 if (host != NULL) {
159 if (convert_string_to_in_addr(host, &(*socket_addr)->sin_addr) != 1) {
160 return ap_pstrcat(p, "failed to resolve \"", host,
161 "\" to exactly one IP address", NULL);
163 } else {
164 (*socket_addr)->sin_addr.s_addr = htonl(INADDR_ANY);
167 *socket_addr_len = sizeof(struct sockaddr_in);
168 return NULL;
171 /*******************************************************************************
172 * Determine if a process with uid/gid can access a file with mode permissions.
174 const char *
175 fcgi_util_check_access(pool *tp,
176 const char * const path, const struct stat *statBuf,
177 const int mode, const uid_t uid, const gid_t gid)
179 if (statBuf == NULL) {
180 static struct stat staticStatBuf;
182 if (stat(path, &staticStatBuf) < 0)
183 return ap_psprintf(tp, "stat(%s) failed: %s", path, strerror(errno));
184 statBuf = &staticStatBuf;
187 #ifndef WIN32
188 /* If the uid owns the file, check the owner bits */
189 if (uid == statBuf->st_uid) {
190 if (mode & R_OK && !(statBuf->st_mode & S_IRUSR))
191 return "read not allowed by owner";
192 if (mode & W_OK && !(statBuf->st_mode & S_IWUSR))
193 return "write not allowed by owner";
194 if (mode & X_OK && !(statBuf->st_mode & S_IXUSR))
195 return "execute not allowed by owner";
196 return NULL;
198 #else
199 if (mode & _S_IREAD && !(statBuf->st_mode & _S_IREAD))
200 return "read not allowed";
201 if (mode & _S_IWRITE && !(statBuf->st_mode & _S_IWRITE))
202 return "write not allowed";
204 // I don't think this works on FAT, but since I don't know how to check..
205 // if (mode & _S_IEXEC && !(statBuf->st_mode & _S_IEXEC))
206 // return "execute not allowed";
207 #endif
209 #if !defined(__EMX__) && !defined(WIN32)
210 /* If the gid is same as the file's group, check the group bits */
211 if (gid == statBuf->st_gid) {
212 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
213 return "read not allowed by group";
214 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
215 return "write not allowed by group";
216 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
217 return "execute not allowed by group";
218 return NULL;
221 /* Get the user membership for the file's group. If the
222 * uid is a member, check the group bits. */
224 const struct group * const gr = getgrgid(statBuf->st_gid);
225 const struct passwd * const pw = getpwuid(uid);
227 if (gr != NULL && pw != NULL) {
228 char **user = gr->gr_mem;
229 for ( ; *user != NULL; user++) {
230 if (strcmp(*user, pw->pw_name) == 0) {
231 if (mode & R_OK && !(statBuf->st_mode & S_IRGRP))
232 return "read not allowed by group";
233 if (mode & W_OK && !(statBuf->st_mode & S_IWGRP))
234 return "write not allowed by group";
235 if (mode & X_OK && !(statBuf->st_mode & S_IXGRP))
236 return "execute not allowed by group";
237 return NULL;
243 /* That just leaves the other bits.. */
244 if (mode & R_OK && !(statBuf->st_mode & S_IROTH))
245 return "read not allowed";
246 if (mode & W_OK && !(statBuf->st_mode & S_IWOTH))
247 return "write not allowed";
248 if (mode & X_OK && !(statBuf->st_mode & S_IXOTH))
249 return "execute not allowed";
250 #endif
252 return NULL;
256 /*******************************************************************************
257 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
258 * enabled with matching uid and gid.
260 fcgi_server *
261 fcgi_util_fs_get_by_id(const char *ePath, uid_t uid, gid_t gid)
263 char path[FCGI_MAXPATH];
264 fcgi_server *s;
266 /* @@@ This should now be done in the loop below */
267 ap_cpystrn(path, ePath, FCGI_MAXPATH);
268 ap_no2slash(path);
270 for (s = fcgi_servers; s != NULL; s = s->next) {
271 int i;
272 const char *fs_path = s->fs_path;
273 for (i = 0; fs_path[i] && path[i]; ++i) {
274 if (fs_path[i] != path[i]) {
275 break;
278 if (fs_path[i]) {
279 continue;
281 if (path[i] == '\0' || path[i] == '/') {
282 if (fcgi_wrapper == NULL || (uid == s->uid && gid == s->gid))
283 return s;
286 return NULL;
289 /*******************************************************************************
290 * Find a FastCGI server with a matching fs_path, and if fcgi_wrapper is
291 * enabled with matching user and group.
293 fcgi_server *
294 fcgi_util_fs_get(const char *ePath, const char *user, const char *group)
296 char path[FCGI_MAXPATH];
297 fcgi_server *s;
299 ap_cpystrn(path, ePath, FCGI_MAXPATH);
300 ap_no2slash(path);
302 for (s = fcgi_servers; s != NULL; s = s->next) {
303 if (strcmp(s->fs_path, path) == 0) {
304 if (fcgi_wrapper == NULL)
305 return s;
307 if (strcmp(user, s->user) == 0
308 && (user[0] == '~' || strcmp(group, s->group) == 0))
310 return s;
314 return NULL;
317 const char *
318 fcgi_util_fs_is_path_ok(pool * const p, const char * const fs_path, struct stat *finfo)
320 const char *err;
322 if (finfo == NULL) {
323 finfo = (struct stat *)ap_palloc(p, sizeof(struct stat));
324 if (stat(fs_path, finfo) < 0)
325 return ap_psprintf(p, "stat(%s) failed: %s", fs_path, strerror(errno));
328 /* No Parse Header scripts aren't allowed.
329 * @@@ Well... we really could quite easily */
330 if (strncmp(strrchr(fs_path, '/'), "/nph-", 5) == 0)
331 return ap_psprintf(p, "NPH scripts cannot be run as FastCGI");
333 if (finfo->st_mode == 0)
334 return ap_psprintf(p, "script not found or unable to stat()");
336 if (S_ISDIR(finfo->st_mode))
337 return ap_psprintf(p, "script is a directory!");
339 /* Let the wrapper determine what it can and can't execute */
340 if (! fcgi_wrapper)
342 #ifdef WIN32
343 err = fcgi_util_check_access(p, fs_path, finfo, _S_IEXEC, fcgi_user_id, fcgi_group_id);
344 #else
345 err = fcgi_util_check_access(p, fs_path, finfo, X_OK, fcgi_user_id, fcgi_group_id);
346 #endif
347 if (err) {
348 return ap_psprintf(p,
349 "access for server (uid %ld, gid %ld) not allowed: %s",
350 (long)fcgi_user_id, (long)fcgi_group_id, err);
354 return NULL;
359 /*******************************************************************************
360 * Allocate a new FastCGI server record from pool p with default values.
362 fcgi_server *
363 fcgi_util_fs_new(pool *p)
365 fcgi_server *s = (fcgi_server *) ap_pcalloc(p, sizeof(fcgi_server));
367 /* Initialize anything who's init state is not zeroizzzzed */
368 s->listenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
369 s->appConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
370 s->idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
371 s->initStartDelay = DEFAULT_INIT_START_DELAY;
372 s->restartDelay = FCGI_DEFAULT_RESTART_DELAY;
373 s->restartOnExit = FALSE;
374 s->directive = APP_CLASS_UNKNOWN;
375 s->processPriority = FCGI_DEFAULT_PRIORITY;
376 s->envp = &fcgi_empty_env;
378 #ifdef WIN32
379 s->listenFd = (int) INVALID_HANDLE_VALUE;
380 #else
381 s->listenFd = -2;
382 #endif
384 return s;
387 /*******************************************************************************
388 * Add the server to the linked list of FastCGI servers.
390 void
391 fcgi_util_fs_add(fcgi_server *s)
393 s->next = fcgi_servers;
394 fcgi_servers = s;
397 /*******************************************************************************
398 * Configure uid, gid, user, group, username for wrapper.
400 const char *
401 fcgi_util_fs_set_uid_n_gid(pool *p, fcgi_server *s, uid_t uid, gid_t gid)
403 #ifndef WIN32
404 struct passwd *pw;
405 struct group *gr;
406 #endif
408 if (fcgi_wrapper == NULL)
409 return NULL;
411 #ifndef WIN32
412 s->uid = uid;
413 pw = getpwuid(uid);
414 if (pw == NULL) {
415 return ap_psprintf(p,
416 "getpwuid() couldn't determine the username for uid '%ld', "
417 "you probably need to modify the User directive: %s",
418 (long)uid, strerror(errno));
420 s->user = ap_pstrdup(p, pw->pw_name);
421 s->username = s->user;
423 s->gid = gid;
424 gr = getgrgid(gid);
425 if (gr == NULL) {
426 return ap_psprintf(p,
427 "getgrgid() couldn't determine the group name for gid '%ld', "
428 "you probably need to modify the Group directive: %s",
429 (long)gid, strerror(errno));
431 s->group = ap_pstrdup(p, gr->gr_name);
432 #endif
433 return NULL;
436 /*******************************************************************************
437 * Allocate an array of ServerProcess records.
439 ServerProcess *
440 fcgi_util_fs_create_procs(pool *p, int num)
442 int i;
443 ServerProcess *proc = (ServerProcess *)ap_pcalloc(p, sizeof(ServerProcess) * num);
445 for (i = 0; i < num; i++) {
446 #ifdef WIN32
447 proc[i].handle = INVALID_HANDLE_VALUE;
448 proc[i].terminationEvent = INVALID_HANDLE_VALUE;
449 #endif
450 proc[i].pid = 0;
451 proc[i].state = FCGI_READY_STATE;
453 return proc;
456 int fcgi_util_ticks(struct timeval * tv)
458 #ifdef WIN32
459 // millisecs is sufficent granularity
460 DWORD millis = GetTickCount();
462 tv->tv_sec = millis / 1000;
463 tv->tv_usec = millis - (tv->tv_sec * 1000);
465 return 0;
466 #else
467 return gettimeofday(tv, NULL);
468 #endif