elminate a static stat buffer
[mod_fastcgi.git] / fcgi_config.c
blob937601c20182ae66f40a2b053e50f161cd69ec99
1 /*
2 * $Id: fcgi_config.c,v 1.32 2002/07/23 00:54:18 robs Exp $
3 */
5 #include "fcgi.h"
7 #ifdef APACHE2
8 #include <limits.h>
9 #include <direct.h>
10 #endif
12 #ifdef WIN32
13 /* warning C4100: unreferenced formal parameter */
14 /* warning C4706: assignment within conditional expression */
15 #pragma warning( disable : 4100 4706 )
16 #endif
18 /*******************************************************************************
19 * Get the next configuration directive argument, & return an in_addr and port.
20 * The arg must be in the form "host:port" where host can be an IP or hostname.
21 * The pool arg should be persistant storage.
23 static const char *get_host_n_port(pool *p, const char **arg,
24 const char **host, u_short *port)
26 char *cvptr, *portStr;
27 long tmp;
29 *host = ap_getword_conf(p, arg);
30 if (**host == '\0')
31 return "\"\"";
33 portStr = strchr(*host, ':');
34 if (portStr == NULL)
35 return "missing port specification";
37 /* Split the host and port portions */
38 *portStr++ = '\0';
40 /* Convert port number */
41 tmp = (u_short) strtol(portStr, &cvptr, 10);
42 if (*cvptr != '\0' || tmp < 1 || tmp > USHRT_MAX)
43 return ap_pstrcat(p, "bad port number \"", portStr, "\"", NULL);
45 *port = (unsigned short) tmp;
47 return NULL;
50 /*******************************************************************************
51 * Get the next configuration directive argument, & return an u_short.
52 * The pool arg should be temporary storage.
54 static const char *get_u_short(pool *p, const char **arg,
55 u_short *num, u_short min)
57 char *ptr;
58 long tmp;
59 const char *txt = ap_getword_conf(p, arg);
61 if (*txt == '\0') {
62 return "\"\"";
65 tmp = strtol(txt, &ptr, 10);
67 if (*ptr != '\0') {
68 return ap_pstrcat(p, "\"", txt, "\" must be a positive integer", NULL);
71 if (tmp < min || tmp > USHRT_MAX) {
72 return ap_psprintf(p, "\"%u\" must be >= %u and < %u", *num, min, USHRT_MAX);
75 *num = (u_short) tmp;
77 return NULL;
80 static const char *get_int(pool *p, const char **arg, int *num, int min)
82 char *cp;
83 const char *val = ap_getword_conf(p, arg);
85 if (*val == '\0')
87 return "\"\"";
90 *num = (int) strtol(val, &cp, 10);
92 if (*cp != '\0')
94 return ap_pstrcat(p, "can't parse ", "\"", val, "\"", NULL);
96 else if (*num < min)
98 return ap_psprintf(p, "\"%d\" must be >= %d", *num, min);
101 return NULL;
104 /*******************************************************************************
105 * Get the next configuration directive argument, & return an u_int.
106 * The pool arg should be temporary storage.
108 static const char *get_u_int(pool *p, const char **arg,
109 u_int *num, u_int min)
111 char *ptr;
112 const char *val = ap_getword_conf(p, arg);
114 if (*val == '\0')
115 return "\"\"";
116 *num = (u_int)strtol(val, &ptr, 10);
118 if (*ptr != '\0')
119 return ap_pstrcat(p, "\"", val, "\" must be a positive integer", NULL);
120 else if (*num < min)
121 return ap_psprintf(p, "\"%u\" must be >= %u", *num, min);
122 return NULL;
125 /*******************************************************************************
126 * Get the next configuration directive argument, & return a float.
127 * The pool arg should be temporary storage.
129 static const char *get_float(pool *p, const char **arg,
130 float *num, float min, float max)
132 char *ptr;
133 const char *val = ap_getword_conf(p, arg);
135 if (*val == '\0')
136 return "\"\"";
137 *num = (float) strtod(val, &ptr);
139 if (*ptr != '\0')
140 return ap_pstrcat(p, "\"", val, "\" is not a floating point number", NULL);
141 if (*num < min || *num > max)
142 return ap_psprintf(p, "\"%f\" is not between %f and %f", *num, min, max);
143 return NULL;
146 const char *fcgi_config_set_env_var(pool *p, char **envp, unsigned int *envc, char * var)
148 if (*envc >= MAX_INIT_ENV_VARS) {
149 return "too many variables, must be <= MAX_INIT_ENV_VARS";
152 if (strchr(var, '=') == NULL) {
153 *(envp + *envc) = ap_pstrcat(p, var, "=", getenv(var), NULL);
155 else {
156 *(envp + *envc) = var;
159 (*envc)++;
161 return NULL;
164 /*******************************************************************************
165 * Get the next configuration directive argument, & add it to an env array.
166 * The pool arg should be permanent storage.
168 static const char *get_env_var(pool *p, const char **arg, char **envp, unsigned int *envc)
170 char * const val = ap_getword_conf(p, arg);
172 if (*val == '\0') {
173 return "\"\"";
176 return fcgi_config_set_env_var(p, envp, envc, val);
179 static const char *get_pass_header(pool *p, const char **arg, array_header **array)
181 const char **header;
183 if (!*array) {
184 *array = ap_make_array(p, 10, sizeof(char*));
187 header = (const char **)ap_push_array(*array);
188 *header = ap_getword_conf(p, arg);
190 return header ? NULL : "\"\"";
193 /*******************************************************************************
194 * Return a "standard" message for common configuration errors.
196 static const char *invalid_value(pool *p, const char *cmd, const char *id,
197 const char *opt, const char *err)
199 return ap_psprintf(p, "%s%s%s: invalid value for %s: %s",
200 cmd, id ? " " : "", id ? id : "", opt, err);
203 /*******************************************************************************
204 * Set/Reset the uid/gid that Apache and the PM will run as. This is ap_user_id
205 * and ap_group_id if we're started as root, and euid/egid otherwise. Also try
206 * to check that the config files don't set the User/Group after a FastCGI
207 * directive is used that depends on it.
209 /*@@@ To be complete, we should save a handle to the server each AppClass is
210 * configured in and at init() check that the user/group is still what we
211 * thought it was. Also the other directives should only be allowed in the
212 * parent Apache server.
214 const char *fcgi_config_set_fcgi_uid_n_gid(int set)
216 static int isSet = 0;
217 #ifndef WIN32
218 uid_t uid = geteuid();
219 gid_t gid = getegid();
221 if (set == 0) {
222 isSet = 0;
223 fcgi_user_id = (uid_t)-1;
224 fcgi_group_id = (gid_t)-1;
225 return NULL;
228 uid = uid ? uid : ap_user_id;
229 gid = gid ? gid : ap_group_id;
231 if (isSet && (uid != fcgi_user_id || gid != fcgi_group_id)) {
232 return "User/Group commands must preceed FastCGI server definitions";
235 isSet = 1;
236 fcgi_user_id = uid;
237 fcgi_group_id = gid;
238 #endif
239 return NULL;
242 apcb_t fcgi_config_reset_globals(void* dummy)
244 fcgi_config_pool = NULL;
245 fcgi_servers = NULL;
246 fcgi_config_set_fcgi_uid_n_gid(0);
247 fcgi_wrapper = NULL;
248 fcgi_socket_dir = DEFAULT_SOCK_DIR;
250 fcgi_dynamic_total_proc_count = 0;
251 fcgi_dynamic_epoch = 0;
252 fcgi_dynamic_last_analyzed = 0;
254 dynamicMaxProcs = FCGI_DEFAULT_MAX_PROCS;
255 dynamicMinProcs = FCGI_DEFAULT_MIN_PROCS;
256 dynamicMaxClassProcs = FCGI_DEFAULT_MAX_CLASS_PROCS;
257 dynamicKillInterval = FCGI_DEFAULT_KILL_INTERVAL;
258 dynamicUpdateInterval = FCGI_DEFAULT_UPDATE_INTERVAL;
259 dynamicGain = FCGI_DEFAULT_GAIN;
260 dynamicThreshold1 = FCGI_DEFAULT_THRESHOLD_1;
261 dynamicThresholdN = FCGI_DEFAULT_THRESHOLD_N;
262 dynamicPleaseStartDelay = FCGI_DEFAULT_START_PROCESS_DELAY;
263 dynamicAppConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
264 dynamicEnvp = &fcgi_empty_env;
265 dynamicProcessSlack = FCGI_DEFAULT_PROCESS_SLACK;
266 dynamicAutoRestart = FCGI_DEFAULT_RESTART_DYNAMIC;
267 dynamicAutoUpdate = FCGI_DEFAULT_AUTOUPDATE;
268 dynamicListenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
269 dynamicInitStartDelay = DEFAULT_INIT_START_DELAY;
270 dynamicRestartDelay = FCGI_DEFAULT_RESTART_DELAY;
271 dynamic_pass_headers = NULL;
272 dynamic_idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
273 dynamicFlush = FCGI_FLUSH;
275 #ifndef WIN32
276 /* Close any old pipe (HUP/USR1) */
277 if (fcgi_pm_pipe[0] != -1) {
278 close(fcgi_pm_pipe[0]);
279 fcgi_pm_pipe[0] = -1;
281 if (fcgi_pm_pipe[1] != -1) {
282 close(fcgi_pm_pipe[1]);
283 fcgi_pm_pipe[1] = -1;
285 #endif
287 return APCB_OK;
290 /*******************************************************************************
291 * Create a directory to hold Unix/Domain sockets.
293 const char *fcgi_config_make_dir(pool *tp, char *path)
295 struct stat finfo;
296 const char *err = NULL;
298 /* Is the directory spec'd correctly */
299 if (*path != '/') {
300 return "path is not absolute (it must start with a \"/\")";
302 else {
303 int i = strlen(path) - 1;
305 /* Strip trailing "/"s */
306 while(i > 0 && path[i] == '/') path[i--] = '\0';
309 /* Does it exist? */
310 if (stat(path, &finfo) != 0) {
311 /* No, but maybe we can create it */
312 #ifdef WIN32
313 if (mkdir(path) != 0)
314 #else
315 if (mkdir(path, S_IRWXU) != 0)
316 #endif
318 return ap_psprintf(tp,
319 "doesn't exist and can't be created: %s",
320 strerror(errno));
323 #ifndef WIN32
324 /* If we're root, we're gonna setuid/setgid so we need to chown */
325 if (geteuid() == 0 && chown(path, ap_user_id, ap_group_id) != 0) {
326 return ap_psprintf(tp,
327 "can't chown() to the server (uid %ld, gid %ld): %s",
328 (long)ap_user_id, (long)ap_group_id, strerror(errno));
330 #endif
332 else {
333 /* Yes, is it a directory? */
334 if (!S_ISDIR(finfo.st_mode))
335 return "isn't a directory!";
337 /* Can we RWX in there? */
338 #ifdef WIN32
339 err = fcgi_util_check_access(tp, NULL, &finfo, _S_IREAD | _S_IWRITE | _S_IEXEC, fcgi_user_id, fcgi_group_id);
340 #else
341 err = fcgi_util_check_access(tp, NULL, &finfo, R_OK | W_OK | X_OK,
342 fcgi_user_id, fcgi_group_id);
343 #endif
344 if (err != NULL) {
345 return ap_psprintf(tp,
346 "access for server (uid %ld, gid %ld) failed: %s",
347 (long)fcgi_user_id, (long)fcgi_group_id, err);
350 return NULL;
353 /*******************************************************************************
354 * Create a "dynamic" subdirectory. If the directory
355 * already exists we don't mess with it unless 'wax' is set.
357 const char *fcgi_config_make_dynamic_dir(pool *p, const int wax)
359 #ifndef WIN32
360 DIR *dp = NULL;
361 struct dirent *dirp = NULL;
362 const char *err;
363 pool *tp;
365 fcgi_dynamic_dir = ap_pstrcat(p, fcgi_socket_dir, "/dynamic", NULL);
367 if ((err = fcgi_config_make_dir(p, fcgi_dynamic_dir)))
368 return ap_psprintf(p, "can't create dynamic directory \"%s\": %s", fcgi_dynamic_dir, err);
370 /* Don't step on a running server unless its OK. */
371 if (!wax)
372 return NULL;
374 /* Create a subpool for the directory operations */
375 tp = ap_make_sub_pool(p);
377 dp = ap_popendir(tp, fcgi_dynamic_dir);
378 if (dp == NULL) {
379 ap_destroy_pool(tp);
380 return ap_psprintf(p, "can't open dynamic directory \"%s\": %s",
381 fcgi_dynamic_dir, strerror(errno));
384 /* Delete everything in the directory, its all FCGI specific */
385 while ((dirp = readdir(dp)) != NULL) {
386 if (strcmp(dirp->d_name, ".") == 0
387 || strcmp(dirp->d_name, "..") == 0) {
388 continue;
391 unlink(ap_pstrcat(tp, fcgi_dynamic_dir, "/", dirp->d_name, NULL));
394 ap_destroy_pool(tp);
396 #else
397 fcgi_dynamic_dir = ap_pstrcat(p, fcgi_socket_dir, "dynamic", NULL);
398 #endif
399 return NULL;
403 /*******************************************************************************
404 * Change the directory used for the Unix/Domain sockets from the default.
405 * Create the directory and the "dynamic" subdirectory.
407 const char *fcgi_config_set_socket_dir(cmd_parms *cmd, void *dummy, char *arg)
409 pool * const tp = cmd->temp_pool;
410 const char * const name = cmd->cmd->name;
411 const char *err;
413 if (strcmp(fcgi_socket_dir, DEFAULT_SOCK_DIR) != 0) {
414 return ap_psprintf(tp, "%s %s: already defined as \"%s\"",
415 name, arg, fcgi_socket_dir);
418 err = fcgi_config_set_fcgi_uid_n_gid(1);
419 if (err != NULL)
420 return ap_psprintf(tp, "%s %s: %s", name, arg, err);
422 if (fcgi_servers != NULL) {
423 return ap_psprintf(tp,
424 "The %s command must preceed static FastCGI server definitions",
425 name);
428 #ifndef WIN32
430 #ifdef APACHE2
431 if (apr_filepath_merge(&arg, "", arg, 0, cmd->pool))
432 return ap_psprintf(tp, "%s %s: invalid filepath", name, arg);
433 #else
434 arg = ap_os_canonical_filename(cmd->pool, arg);
435 #endif
437 arg = ap_server_root_relative(cmd->pool, arg);
438 #else /* WIN32 */
439 if (strncmp(arg, "\\\\.\\pipe\\", 9) != 0)
440 return ap_psprintf(tp, "%s %s is invalid format",name, arg);
441 #endif
443 fcgi_socket_dir = arg;
445 #ifndef WIN32
446 err = fcgi_config_make_dir(tp, fcgi_socket_dir);
447 if (err != NULL)
448 return ap_psprintf(tp, "%s %s: %s", name, arg, err);
450 err = fcgi_config_make_dynamic_dir(cmd->pool, 0);
451 if (err != NULL)
452 return ap_psprintf(tp, "%s %s: %s", name, arg, err);
453 #endif
455 return NULL;
458 /*******************************************************************************
459 * Enable, disable, or specify the path to a wrapper used to invoke all
460 * FastCGI applications.
462 const char *fcgi_config_set_wrapper(cmd_parms *cmd, void *dummy, const char *arg)
464 #ifdef APACHE2
466 * AP2TODO use ap_run_get_suexec_identity() (this will be hard as it
467 * takes a request_rec) and/or mod_userdir_user note
469 return ap_psprintf(cmd->temp_pool, "%s isn't supported yet under Apache2", cmd->cmd->name);
470 #else
471 const char *err = NULL;
472 const char * const name = cmd->cmd->name;
473 pool * const tp = cmd->temp_pool;
474 char * wrapper;
476 if (!ap_suexec_enabled && strcasecmp(arg, "On") == 0) {
477 fprintf(stderr, "Warning: \"%s On\" requires SUEXEC be enabled in Apache", name);
478 return NULL;
481 err = fcgi_config_set_fcgi_uid_n_gid(1);
482 if (err != NULL)
483 return ap_psprintf(tp, "%s %s: %s", name, arg, err);
485 if (fcgi_servers != NULL) {
486 return ap_psprintf(tp,
487 "The %s command must preceed static FastCGI server definitions", name);
490 if (strcasecmp(arg, "On") == 0) {
491 fcgi_wrapper = SUEXEC_BIN;
493 else if (strcasecmp(arg, "Off") == 0) {
494 fcgi_wrapper = NULL;
496 else {
497 wrapper = (char *) ap_os_canonical_filename(cmd->pool, arg);
498 wrapper = ap_server_root_relative(cmd->pool, wrapper);
500 #ifdef WIN32
501 err = fcgi_util_check_access(tp, wrapper, NULL, _S_IEXEC, fcgi_user_id, fcgi_group_id);
502 #else
503 err = fcgi_util_check_access(tp, wrapper, NULL, X_OK, fcgi_user_id, fcgi_group_id);
504 #endif
506 if (err != NULL) {
507 return ap_psprintf(tp,
508 "%s: \"%s\" access for server (uid %ld, gid %ld) failed: %s",
509 name, wrapper, (long)fcgi_user_id, (long)fcgi_group_id, err);
512 fcgi_wrapper = wrapper;
514 return NULL;
515 #endif /* !APACHE2 */
518 /*******************************************************************************
519 * Configure a static FastCGI server.
521 const char *fcgi_config_new_static_server(cmd_parms *cmd, void *dummy, const char *arg)
523 fcgi_server *s;
524 pool *p = cmd->pool, *tp = cmd->temp_pool;
525 const char *name = cmd->cmd->name;
526 char *fs_path = ap_getword_conf(p, &arg);
527 const char *option, *err;
529 /* Allocate temp storage for the array of initial environment variables */
530 char **envp = ap_pcalloc(tp, sizeof(char *) * (MAX_INIT_ENV_VARS + 3));
531 unsigned int envc = 0;
533 #ifdef WIN32
534 HANDLE mutex;
535 #endif
537 if (*fs_path == '\0')
538 return "AppClass requires a pathname!?";
540 if ((err = fcgi_config_set_fcgi_uid_n_gid(1)) != NULL)
541 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
543 #ifdef APACHE2
544 if (apr_filepath_merge(&fs_path, "", fs_path, 0, p))
545 return ap_psprintf(tp, "%s %s: invalid filepath", name, fs_path);
546 #else
547 fs_path = ap_os_canonical_filename(p, fs_path);
548 #endif
549 fs_path = ap_server_root_relative(p, fs_path);
551 ap_getparents(fs_path);
552 ap_no2slash(fs_path);
554 /* See if we've already got one of these configured */
555 s = fcgi_util_fs_get_by_id(fs_path, fcgi_util_get_server_uid(cmd->server),
556 fcgi_util_get_server_gid(cmd->server));
557 if (s != NULL) {
558 if (fcgi_wrapper) {
559 return ap_psprintf(tp,
560 "%s: redefinition of a previously defined FastCGI "
561 "server \"%s\" with uid=%ld and gid=%ld",
562 name, fs_path, (long) fcgi_util_get_server_uid(cmd->server),
563 (long) fcgi_util_get_server_gid(cmd->server));
565 else {
566 return ap_psprintf(tp,
567 "%s: redefinition of a previously defined FastCGI server \"%s\"",
568 name, fs_path);
572 err = fcgi_util_fs_is_path_ok(tp, fs_path, NULL);
573 if (err != NULL) {
574 return ap_psprintf(tp, "%s: \"%s\" %s", name, fs_path, err);
577 s = fcgi_util_fs_new(p);
578 s->fs_path = fs_path;
579 s->directive = APP_CLASS_STANDARD;
580 s->restartOnExit = TRUE;
581 s->numProcesses = 1;
583 #ifdef WIN32
584 // TCP FastCGI applications require SystemRoot be present in the environment
585 // Put it in both for consistency to the application
586 fcgi_config_set_env_var(tp, envp, &envc, "SystemRoot");
588 mutex = CreateMutex(NULL, FALSE, fs_path);
590 if (mutex == NULL)
592 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
593 "FastCGI: CreateMutex() failed");
594 return "failed to create FastCGI application accept mutex";
597 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
599 s->mutex_env_string = ap_psprintf(p, "_FCGI_MUTEX_=%ld", mutex);;
600 #else
601 if (fcgi_wrapper) {
602 struct passwd *pw;
603 struct group *gr;
605 s->uid = cmd->server->server_uid;
606 pw = getpwuid(s->uid);
607 if (pw == NULL) {
608 return ap_psprintf(tp, "mod_fastcgi: "
609 "getpwuid() couldn't determine the username for uid '%ld', "
610 "you probably need to modify the User directive: %s",
611 (long)s->uid, strerror(errno));
613 s->user = ap_pstrdup(p, pw->pw_name);
614 s->username = s->user;
616 s->gid = cmd->server->server_gid;
617 gr = getgrgid(s->gid);
618 if (gr == NULL) {
619 return ap_psprintf(tp, "mod_fastcgi: "
620 "getgrgid() couldn't determine the group name for gid '%ld', "
621 "you probably need to modify the Group directive: %s\n",
622 (long)s->gid, strerror(errno));
624 s->group = ap_pstrdup(p, gr->gr_name);
626 #endif
628 /* Parse directive arguments */
629 while (*arg) {
630 option = ap_getword_conf(tp, &arg);
632 if (strcasecmp(option, "-processes") == 0) {
633 if ((err = get_u_int(tp, &arg, &s->numProcesses, 1)))
634 return invalid_value(tp, name, fs_path, option, err);
636 else if (strcasecmp(option, "-restart-delay") == 0) {
637 if ((err = get_u_int(tp, &arg, &s->restartDelay, 0)))
638 return invalid_value(tp, name, fs_path, option, err);
640 else if (strcasecmp(option, "-init-start-delay") == 0) {
641 if ((err = get_int(tp, &arg, &s->initStartDelay, 0)))
642 return invalid_value(tp, name, fs_path, option, err);
644 else if (strcasecmp(option, "-priority") == 0) {
645 if ((err = get_u_int(tp, &arg, &s->processPriority, 0)))
646 return invalid_value(tp, name, fs_path, option, err);
648 else if (strcasecmp(option, "-listen-queue-depth") == 0) {
649 if ((err = get_u_int(tp, &arg, &s->listenQueueDepth, 1)))
650 return invalid_value(tp, name, fs_path, option, err);
652 else if (strcasecmp(option, "-appConnTimeout") == 0) {
653 if ((err = get_u_int(tp, &arg, &s->appConnectTimeout, 0)))
654 return invalid_value(tp, name, fs_path, option, err);
656 else if (strcasecmp(option, "-idle-timeout") == 0) {
657 if ((err = get_u_int(tp, &arg, &s->idle_timeout, 1)))
658 return invalid_value(tp, name, fs_path, option, err);
660 else if (strcasecmp(option, "-port") == 0) {
661 if ((err = get_u_short(tp, &arg, &s->port, 1)))
662 return invalid_value(tp, name, fs_path, option, err);
664 else if (strcasecmp(option, "-socket") == 0) {
665 s->socket_path = ap_getword_conf(tp, &arg);
666 if (*s->socket_path == '\0')
667 return invalid_value(tp, name, fs_path, option, "\"\"");
669 else if (strcasecmp(option, "-initial-env") == 0) {
670 if ((err = get_env_var(p, &arg, envp, &envc)))
671 return invalid_value(tp, name, fs_path, option, err);
673 else if (strcasecmp(option, "-pass-header") == 0) {
674 if ((err = get_pass_header(p, &arg, &s->pass_headers)))
675 return invalid_value(tp, name, fs_path, option, err);
677 else if (strcasecmp(option, "-flush") == 0) {
678 s->flush = 1;
680 else {
681 return ap_psprintf(tp, "%s %s: invalid option: %s", name, fs_path, option);
683 } /* while */
685 if (s->socket_path != NULL && s->port != 0) {
686 return ap_psprintf(tp,
687 "%s %s: -port and -socket are mutually exclusive options",
688 name, fs_path);
691 /* Move env array to a surviving pool */
692 s->envp = (char **)ap_pcalloc(p, sizeof(char *) * (envc + 4));
693 memcpy(s->envp, envp, sizeof(char *) * envc);
695 /* Initialize process structs */
696 s->procs = fcgi_util_fs_create_procs(p, s->numProcesses);
698 /* Build the appropriate sockaddr structure */
699 if (s->port != 0) {
700 err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->socket_addr,
701 &s->socket_addr_len, NULL, s->port);
702 if (err != NULL)
703 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
704 #ifdef WIN32
705 err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->dest_addr,
706 &s->socket_addr_len, "localhost", s->port);
707 if (err != NULL)
708 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
709 #endif
710 } else {
711 if (s->socket_path == NULL)
712 s->socket_path = fcgi_util_socket_hash_filename(tp, fs_path, s->user, s->group);
713 s->socket_path = fcgi_util_socket_make_path_absolute(p, s->socket_path, 0);
714 #ifndef WIN32
715 err = fcgi_util_socket_make_domain_addr(p, (struct sockaddr_un **)&s->socket_addr,
716 &s->socket_addr_len, s->socket_path);
717 if (err != NULL)
718 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
719 #endif
722 /* Add it to the list of FastCGI servers */
723 fcgi_util_fs_add(s);
725 return NULL;
728 /*******************************************************************************
729 * Configure a static FastCGI server that is started/managed elsewhere.
731 const char *fcgi_config_new_external_server(cmd_parms *cmd, void *dummy, const char *arg)
733 fcgi_server *s;
734 pool * const p = cmd->pool, *tp = cmd->temp_pool;
735 const char * const name = cmd->cmd->name;
736 char *fs_path = ap_getword_conf(p, &arg);
737 const char *option, *err;
739 if (!*fs_path) {
740 return ap_pstrcat(tp, name, " requires a path and either a -socket or -host option", NULL);
743 #ifdef APACHE2
744 if (apr_filepath_merge(&fs_path, "", fs_path, 0, p))
745 return ap_psprintf(tp, "%s %s: invalid filepath", name, fs_path);
746 #else
747 fs_path = ap_os_canonical_filename(p, fs_path);
748 #endif
750 fs_path = ap_server_root_relative(p, fs_path);
752 ap_getparents(fs_path);
753 ap_no2slash(fs_path);
755 /* See if we've already got one of these bettys configured */
756 s = fcgi_util_fs_get_by_id(fs_path, fcgi_util_get_server_uid(cmd->server),
757 fcgi_util_get_server_gid(cmd->server));
758 if (s != NULL) {
759 if (fcgi_wrapper) {
760 return ap_psprintf(tp,
761 "%s: redefinition of a previously defined class \"%s\" "
762 "with uid=%ld and gid=%ld",
763 name, fs_path, (long) fcgi_util_get_server_uid(cmd->server),
764 (long) fcgi_util_get_server_gid(cmd->server));
766 else
768 return ap_psprintf(tp,
769 "%s: redefinition of previously defined class \"%s\"", name, fs_path);
773 s = fcgi_util_fs_new(p);
774 s->fs_path = fs_path;
775 s->directive = APP_CLASS_EXTERNAL;
777 err = fcgi_util_fs_set_uid_n_gid(p, s, fcgi_util_get_server_uid(cmd->server),
778 fcgi_util_get_server_gid(cmd->server));
779 if (err != NULL)
780 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
782 /* Parse directive arguments */
783 while (*arg != '\0') {
784 option = ap_getword_conf(tp, &arg);
786 if (strcasecmp(option, "-host") == 0) {
787 if ((err = get_host_n_port(p, &arg, &s->host, &s->port)))
788 return invalid_value(tp, name, fs_path, option, err);
790 else if (strcasecmp(option, "-socket") == 0) {
791 s->socket_path = ap_getword_conf(tp, &arg);
792 if (*s->socket_path == '\0')
793 return invalid_value(tp, name, fs_path, option, "\"\"");
795 else if (strcasecmp(option, "-appConnTimeout") == 0) {
796 if ((err = get_u_int(tp, &arg, &s->appConnectTimeout, 0)))
797 return invalid_value(tp, name, fs_path, option, err);
799 else if (strcasecmp(option, "-idle-timeout") == 0) {
800 if ((err = get_u_int(tp, &arg, &s->idle_timeout, 1)))
801 return invalid_value(tp, name, fs_path, option, err);
803 else if (strcasecmp(option, "-pass-header") == 0) {
804 if ((err = get_pass_header(p, &arg, &s->pass_headers)))
805 return invalid_value(tp, name, fs_path, option, err);
807 else if (strcasecmp(option, "-flush") == 0) {
808 s->flush = 1;
810 else {
811 return ap_psprintf(tp, "%s %s: invalid option: %s", name, fs_path, option);
813 } /* while */
815 /* Require one of -socket or -host, but not both */
816 if (s->socket_path != NULL && s->port != 0) {
817 return ap_psprintf(tp,
818 "%s %s: -host and -socket are mutually exclusive options",
819 name, fs_path);
821 if (s->socket_path == NULL && s->port == 0) {
822 return ap_psprintf(tp,
823 "%s %s: -socket or -host option missing", name, fs_path);
826 /* Build the appropriate sockaddr structure */
827 if (s->port != 0) {
828 err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->socket_addr,
829 &s->socket_addr_len, s->host, s->port);
830 if (err != NULL)
831 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
832 } else {
833 s->socket_path = fcgi_util_socket_make_path_absolute(p, s->socket_path, 0);
834 #ifndef WIN32
835 err = fcgi_util_socket_make_domain_addr(p, (struct sockaddr_un **)&s->socket_addr,
836 &s->socket_addr_len, s->socket_path);
837 if (err != NULL)
838 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
839 #endif
842 /* Add it to the list of FastCGI servers */
843 fcgi_util_fs_add(s);
845 return NULL;
849 *----------------------------------------------------------------------
851 * fcgi_config_set_config --
853 * Implements the FastCGI FCGIConfig configuration directive.
854 * This command adds routines to control the execution of the
855 * dynamic FastCGI processes.
858 *----------------------------------------------------------------------
860 const char *fcgi_config_set_config(cmd_parms *cmd, void *dummy, const char *arg)
862 pool * const p = cmd->pool;
863 pool * const tp = cmd->temp_pool;
864 const char *err, *option;
865 const char * const name = cmd->cmd->name;
867 /* Allocate temp storage for an initial environment */
868 unsigned int envc = 0;
869 char **envp = (char **)ap_pcalloc(tp, sizeof(char *) * (MAX_INIT_ENV_VARS + 3));
871 /* Parse the directive arguments */
872 while (*arg) {
873 option = ap_getword_conf(tp, &arg);
875 if (strcasecmp(option, "-maxProcesses") == 0) {
876 if ((err = get_u_int(tp, &arg, &dynamicMaxProcs, 1)))
877 return invalid_value(tp, name, NULL, option, err);
879 else if (strcasecmp(option, "-minProcesses") == 0) {
880 if ((err = get_int(tp, &arg, &dynamicMinProcs, 0)))
881 return invalid_value(tp, name, NULL, option, err);
883 else if (strcasecmp(option, "-maxClassProcesses") == 0) {
884 if ((err = get_int(tp, &arg, &dynamicMaxClassProcs, 1)))
885 return invalid_value(tp, name, NULL, option, err);
887 else if (strcasecmp(option, "-killInterval") == 0) {
888 if ((err = get_u_int(tp, &arg, &dynamicKillInterval, 1)))
889 return invalid_value(tp, name, NULL, option, err);
891 else if (strcasecmp(option, "-updateInterval") == 0) {
892 if ((err = get_u_int(tp, &arg, &dynamicUpdateInterval, 1)))
893 return invalid_value(tp, name, NULL, option, err);
895 else if (strcasecmp(option, "-gainValue") == 0) {
896 if ((err = get_float(tp, &arg, &dynamicGain, 0.0, 1.0)))
897 return invalid_value(tp, name, NULL, option, err);
899 else if ((strcasecmp(option, "-singleThreshold") == 0)
900 || (strcasecmp(option, "-singleThreshhold") == 0))
902 if ((err = get_int(tp, &arg, &dynamicThreshold1, 0)))
903 return invalid_value(tp, name, NULL, option, err);
905 else if ((strcasecmp(option, "-multiThreshold") == 0)
906 || (strcasecmp(option, "-multiThreshhold") == 0))
908 if ((err = get_int(tp, &arg, &dynamicThresholdN, 0)))
909 return invalid_value(tp, name, NULL, option, err);
911 else if (strcasecmp(option, "-startDelay") == 0) {
912 if ((err = get_u_int(tp, &arg, &dynamicPleaseStartDelay, 1)))
913 return invalid_value(tp, name, NULL, option, err);
915 else if (strcasecmp(option, "-initial-env") == 0) {
916 if ((err = get_env_var(p, &arg, envp, &envc)))
917 return invalid_value(tp, name, NULL, option, err);
919 else if (strcasecmp(option, "-pass-header") == 0) {
920 if ((err = get_pass_header(p, &arg, &dynamic_pass_headers)))
921 return invalid_value(tp, name, NULL, option, err);
923 else if (strcasecmp(option, "-appConnTimeout") == 0) {
924 if ((err = get_u_int(tp, &arg, &dynamicAppConnectTimeout, 0)))
925 return invalid_value(tp, name, NULL, option, err);
927 else if (strcasecmp(option, "-idle-timeout") == 0) {
928 if ((err = get_u_int(tp, &arg, &dynamic_idle_timeout, 1)))
929 return invalid_value(tp, name, NULL, option, err);
931 else if (strcasecmp(option, "-listen-queue-depth") == 0) {
932 if ((err = get_u_int(tp, &arg, &dynamicListenQueueDepth, 1)))
933 return invalid_value(tp, name, NULL, option, err);
935 else if (strcasecmp(option, "-restart-delay") == 0) {
936 if ((err = get_u_int(tp, &arg, &dynamicRestartDelay, 0)))
937 return invalid_value(tp, name, NULL, option, err);
939 else if (strcasecmp(option, "-init-start-delay") == 0) {
940 if ((err = get_u_int(tp, &arg, &dynamicInitStartDelay, 0)))
941 return invalid_value(tp, name, NULL, option, err);
943 else if (strcasecmp(option, "-processSlack") == 0) {
944 if ((err = get_u_int(tp, &arg, &dynamicProcessSlack, 1)))
945 return invalid_value(tp, name, NULL, option, err);
947 else if (strcasecmp(option, "-restart") == 0) {
948 dynamicAutoRestart = 1;
950 else if (strcasecmp(option, "-autoUpdate") == 0) {
951 dynamicAutoUpdate = 1;
953 else if (strcasecmp(option, "-flush") == 0) {
954 dynamicFlush = TRUE;
956 else {
957 return ap_psprintf(tp, "%s: invalid option: %s", name, option);
959 } /* while */
961 if (dynamicProcessSlack >= dynamicMaxProcs + 1) {
962 /* the kill policy would work unexpectedly */
963 return ap_psprintf(tp,
964 "%s: processSlack (%u) must be less than maxProcesses (%u) + 1",
965 name, dynamicProcessSlack, dynamicMaxProcs);
968 /* Move env array to a surviving pool, leave 2 extra slots for
969 * WIN32 _FCGI_MUTEX_ and _FCGI_SHUTDOWN_EVENT_ */
970 dynamicEnvp = (char **)ap_pcalloc(p, sizeof(char *) * (envc + 4));
971 memcpy(dynamicEnvp, envp, sizeof(char *) * envc);
973 return NULL;
976 void *fcgi_config_create_dir_config(pool *p, char *dummy)
978 fcgi_dir_config *dir_config = ap_pcalloc(p, sizeof(fcgi_dir_config));
980 dir_config->authenticator_options = FCGI_AUTHORITATIVE;
981 dir_config->authorizer_options = FCGI_AUTHORITATIVE;
982 dir_config->access_checker_options = FCGI_AUTHORITATIVE;
984 return dir_config;
988 const char *fcgi_config_new_auth_server(cmd_parms * const cmd,
989 fcgi_dir_config *dir_config, const char *fs_path, const char * const compat)
991 pool * const tp = cmd->temp_pool;
992 char * auth_server;
994 #ifdef APACHE2
995 if (apr_filepath_merge(&auth_server, "", fs_path, 0, cmd->pool))
996 return ap_psprintf(tp, "%s %s: invalid filepath", cmd->cmd->name, fs_path);
997 #else
998 auth_server = (char *) ap_os_canonical_filename(cmd->pool, fs_path);
999 #endif
1001 auth_server = ap_server_root_relative(cmd->pool, auth_server);
1003 /* Make sure its already configured or at least a candidate for dynamic */
1004 if (fcgi_util_fs_get_by_id(auth_server, fcgi_util_get_server_uid(cmd->server),
1005 fcgi_util_get_server_gid(cmd->server)) == NULL)
1007 const char *err = fcgi_util_fs_is_path_ok(tp, auth_server, NULL);
1008 if (err)
1009 return ap_psprintf(tp, "%s: \"%s\" %s", cmd->cmd->name, auth_server, err);
1012 if (compat && strcasecmp(compat, "-compat"))
1013 return ap_psprintf(cmd->temp_pool, "%s: unknown option: \"%s\"", cmd->cmd->name, compat);
1015 switch((int)cmd->info) {
1016 case FCGI_AUTH_TYPE_AUTHENTICATOR:
1017 dir_config->authenticator = auth_server;
1018 dir_config->authenticator_options |= (compat) ? FCGI_COMPAT : 0;
1019 break;
1020 case FCGI_AUTH_TYPE_AUTHORIZER:
1021 dir_config->authorizer = auth_server;
1022 dir_config->authorizer_options |= (compat) ? FCGI_COMPAT : 0;
1023 break;
1024 case FCGI_AUTH_TYPE_ACCESS_CHECKER:
1025 dir_config->access_checker = auth_server;
1026 dir_config->access_checker_options |= (compat) ? FCGI_COMPAT : 0;
1027 break;
1030 return NULL;
1033 const char *fcgi_config_set_authoritative_slot(const cmd_parms * const cmd,
1034 fcgi_dir_config * const dir_config, int arg)
1036 int offset = (int)(long)cmd->info;
1038 if (arg)
1039 *((u_char *)dir_config + offset) |= FCGI_AUTHORITATIVE;
1040 else
1041 *((u_char *)dir_config + offset) &= ~FCGI_AUTHORITATIVE;
1043 return NULL;