commit mail test
[mod_fastcgi.git] / fcgi_config.c
blob4591478286d5d5b35653293657e99746db17f9b3
1 /*
2 * $Id: fcgi_config.c,v 1.49 2003/10/30 01:08:34 robs Exp $
3 */
5 #define CORE_PRIVATE
6 #include "fcgi.h"
8 #ifdef APACHE2
10 #include <limits.h>
11 #include "mpm_common.h" /* ap_uname2id, ap_gname2id */
13 #ifdef WIN32
14 #include <direct.h>
15 #else
16 #include <unistd.h>
17 #include "unixd.h"
18 #endif
20 #endif
22 #ifdef WIN32
23 /* warning C4100: unreferenced formal parameter */
24 /* warning C4706: assignment within conditional expression */
25 #pragma warning( disable : 4100 4706 )
26 #endif
28 /*******************************************************************************
29 * Get the next configuration directive argument, & return an in_addr and port.
30 * The arg must be in the form "host:port" where host can be an IP or hostname.
31 * The pool arg should be persistant storage.
33 static const char *get_host_n_port(pool *p, const char **arg,
34 const char **host, u_short *port)
36 char *cvptr, *portStr;
37 long tmp;
39 *host = ap_getword_conf(p, arg);
40 if (**host == '\0')
41 return "\"\"";
43 portStr = strchr(*host, ':');
44 if (portStr == NULL)
45 return "missing port specification";
47 /* Split the host and port portions */
48 *portStr++ = '\0';
50 /* Convert port number */
51 tmp = (u_short) strtol(portStr, &cvptr, 10);
52 if (*cvptr != '\0' || tmp < 1 || tmp > USHRT_MAX)
53 return ap_pstrcat(p, "bad port number \"", portStr, "\"", NULL);
55 *port = (unsigned short) tmp;
57 return NULL;
60 /*******************************************************************************
61 * Get the next configuration directive argument, & return an u_short.
62 * The pool arg should be temporary storage.
64 static const char *get_u_short(pool *p, const char **arg,
65 u_short *num, u_short min)
67 char *ptr;
68 long tmp;
69 const char *txt = ap_getword_conf(p, arg);
71 if (*txt == '\0') {
72 return "\"\"";
75 tmp = strtol(txt, &ptr, 10);
77 if (*ptr != '\0') {
78 return ap_pstrcat(p, "\"", txt, "\" must be a positive integer", NULL);
81 if (tmp < min || tmp > USHRT_MAX) {
82 return ap_psprintf(p, "\"%u\" must be >= %u and < %u", *num, min, USHRT_MAX);
85 *num = (u_short) tmp;
87 return NULL;
90 static const char *get_int(pool *p, const char **arg, int *num, int min)
92 char *cp;
93 const char *val = ap_getword_conf(p, arg);
95 if (*val == '\0')
97 return "\"\"";
100 *num = (int) strtol(val, &cp, 10);
102 if (*cp != '\0')
104 return ap_pstrcat(p, "can't parse ", "\"", val, "\"", NULL);
106 else if (*num < min)
108 return ap_psprintf(p, "\"%d\" must be >= %d", *num, min);
111 return NULL;
114 /*******************************************************************************
115 * Get the next configuration directive argument, & return an u_int.
116 * The pool arg should be temporary storage.
118 static const char *get_u_int(pool *p, const char **arg,
119 u_int *num, u_int min)
121 char *ptr;
122 const char *val = ap_getword_conf(p, arg);
124 if (*val == '\0')
125 return "\"\"";
126 *num = (u_int)strtol(val, &ptr, 10);
128 if (*ptr != '\0')
129 return ap_pstrcat(p, "\"", val, "\" must be a positive integer", NULL);
130 else if (*num < min)
131 return ap_psprintf(p, "\"%u\" must be >= %u", *num, min);
132 return NULL;
135 /*******************************************************************************
136 * Get the next configuration directive argument, & return a float.
137 * The pool arg should be temporary storage.
139 static const char *get_float(pool *p, const char **arg,
140 float *num, float min, float max)
142 char *ptr;
143 const char *val = ap_getword_conf(p, arg);
145 if (*val == '\0')
146 return "\"\"";
147 *num = (float) strtod(val, &ptr);
149 if (*ptr != '\0')
150 return ap_pstrcat(p, "\"", val, "\" is not a floating point number", NULL);
151 if (*num < min || *num > max)
152 return ap_psprintf(p, "\"%f\" is not between %f and %f", *num, min, max);
153 return NULL;
156 const char *fcgi_config_set_env_var(pool *p, char **envp, unsigned int *envc, char * var)
158 if (*envc >= MAX_INIT_ENV_VARS) {
159 return "too many variables, must be <= MAX_INIT_ENV_VARS";
162 if (strchr(var, '=') == NULL) {
163 *(envp + *envc) = ap_pstrcat(p, var, "=", getenv(var), NULL);
165 else {
166 *(envp + *envc) = var;
169 (*envc)++;
171 return NULL;
174 /*******************************************************************************
175 * Get the next configuration directive argument, & add it to an env array.
176 * The pool arg should be permanent storage.
178 static const char *get_env_var(pool *p, const char **arg, char **envp, unsigned int *envc)
180 char * const val = ap_getword_conf(p, arg);
182 if (*val == '\0') {
183 return "\"\"";
186 return fcgi_config_set_env_var(p, envp, envc, val);
189 static const char *get_pass_header(pool *p, const char **arg, array_header **array)
191 const char **header;
193 if (!*array) {
194 *array = ap_make_array(p, 10, sizeof(char*));
197 header = (const char **)ap_push_array(*array);
198 *header = ap_getword_conf(p, arg);
200 return header ? NULL : "\"\"";
203 /*******************************************************************************
204 * Return a "standard" message for common configuration errors.
206 static const char *invalid_value(pool *p, const char *cmd, const char *id,
207 const char *opt, const char *err)
209 return ap_psprintf(p, "%s%s%s: invalid value for %s: %s",
210 cmd, id ? " " : "", id ? id : "", opt, err);
213 /*******************************************************************************
214 * Set/Reset the uid/gid that Apache and the PM will run as. This is ap_user_id
215 * and ap_group_id if we're started as root, and euid/egid otherwise. Also try
216 * to check that the config files don't set the User/Group after a FastCGI
217 * directive is used that depends on it.
219 /*@@@ To be complete, we should save a handle to the server each AppClass is
220 * configured in and at init() check that the user/group is still what we
221 * thought it was. Also the other directives should only be allowed in the
222 * parent Apache server.
224 const char *fcgi_config_set_fcgi_uid_n_gid(int set)
226 static int isSet = 0;
228 #ifndef WIN32
230 uid_t uid = geteuid();
231 gid_t gid = getegid();
233 if (set == 0) {
234 isSet = 0;
235 fcgi_user_id = (uid_t)-1;
236 fcgi_group_id = (gid_t)-1;
237 return NULL;
240 if (uid == 0) {
241 uid = ap_user_id;
244 if (gid == 0) {
245 gid = ap_group_id;
248 if (isSet && (uid != fcgi_user_id || gid != fcgi_group_id)) {
249 return "User/Group commands must preceed FastCGI server definitions";
252 isSet = 1;
253 fcgi_user_id = uid;
254 fcgi_group_id = gid;
256 #endif /* !WIN32 */
258 return NULL;
261 apcb_t fcgi_config_reset_globals(void* dummy)
263 fcgi_config_pool = NULL;
264 fcgi_servers = NULL;
265 fcgi_config_set_fcgi_uid_n_gid(0);
266 fcgi_wrapper = NULL;
267 fcgi_socket_dir = NULL;
269 fcgi_dynamic_total_proc_count = 0;
270 fcgi_dynamic_epoch = 0;
271 fcgi_dynamic_last_analyzed = 0;
273 dynamicMaxProcs = FCGI_DEFAULT_MAX_PROCS;
274 dynamicMinProcs = FCGI_DEFAULT_MIN_PROCS;
275 dynamicMaxClassProcs = FCGI_DEFAULT_MAX_CLASS_PROCS;
276 dynamicKillInterval = FCGI_DEFAULT_KILL_INTERVAL;
277 dynamicUpdateInterval = FCGI_DEFAULT_UPDATE_INTERVAL;
278 dynamicGain = FCGI_DEFAULT_GAIN;
279 dynamicThreshold1 = FCGI_DEFAULT_THRESHOLD_1;
280 dynamicThresholdN = FCGI_DEFAULT_THRESHOLD_N;
281 dynamicPleaseStartDelay = FCGI_DEFAULT_START_PROCESS_DELAY;
282 dynamicAppConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
283 dynamicEnvp = &fcgi_empty_env;
284 dynamicProcessSlack = FCGI_DEFAULT_PROCESS_SLACK;
285 dynamicAutoRestart = FCGI_DEFAULT_RESTART_DYNAMIC;
286 dynamicAutoUpdate = FCGI_DEFAULT_AUTOUPDATE;
287 dynamicListenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
288 dynamicInitStartDelay = DEFAULT_INIT_START_DELAY;
289 dynamicRestartDelay = FCGI_DEFAULT_RESTART_DELAY;
290 dynamic_pass_headers = NULL;
291 dynamic_idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
292 dynamicFlush = FCGI_FLUSH;
294 #ifndef WIN32
295 /* Close any old pipe (HUP/USR1) */
296 if (fcgi_pm_pipe[0] != -1) {
297 close(fcgi_pm_pipe[0]);
298 fcgi_pm_pipe[0] = -1;
300 if (fcgi_pm_pipe[1] != -1) {
301 close(fcgi_pm_pipe[1]);
302 fcgi_pm_pipe[1] = -1;
304 #endif
306 return APCB_OK;
309 /*******************************************************************************
310 * Create a directory to hold Unix/Domain sockets.
312 const char *fcgi_config_make_dir(pool *tp, char *path)
314 struct stat finfo;
315 const char *err = NULL;
317 /* Is the directory spec'd correctly */
318 if (*path != '/') {
319 return "path is not absolute (it must start with a \"/\")";
321 else {
322 int i = strlen(path) - 1;
324 /* Strip trailing "/"s */
325 while(i > 0 && path[i] == '/') path[i--] = '\0';
328 /* Does it exist? */
329 if (stat(path, &finfo) != 0) {
330 /* No, but maybe we can create it */
331 #ifdef WIN32
332 if (mkdir(path) != 0)
333 #else
334 if (mkdir(path, S_IRWXU) != 0)
335 #endif
337 return ap_psprintf(tp,
338 "doesn't exist and can't be created: %s",
339 strerror(errno));
342 #ifndef WIN32
343 /* If we're root, we're gonna setuid/setgid so we need to chown */
344 if (geteuid() == 0 && chown(path, ap_user_id, ap_group_id) != 0) {
345 return ap_psprintf(tp,
346 "can't chown() to the server (uid %ld, gid %ld): %s",
347 (long)ap_user_id, (long)ap_group_id, strerror(errno));
349 #endif
351 else {
352 /* Yes, is it a directory? */
353 if (!S_ISDIR(finfo.st_mode))
354 return "isn't a directory!";
356 /* Can we RWX in there? */
357 #ifdef WIN32
358 err = fcgi_util_check_access(tp, NULL, &finfo, _S_IREAD | _S_IWRITE | _S_IEXEC, fcgi_user_id, fcgi_group_id);
359 #else
360 err = fcgi_util_check_access(tp, NULL, &finfo, R_OK | W_OK | X_OK,
361 fcgi_user_id, fcgi_group_id);
362 #endif
363 if (err != NULL) {
364 return ap_psprintf(tp,
365 "access for server (uid %ld, gid %ld) failed: %s",
366 (long)fcgi_user_id, (long)fcgi_group_id, err);
369 return NULL;
372 /*******************************************************************************
373 * Create a "dynamic" subdirectory. If the directory
374 * already exists we don't mess with it unless 'wax' is set.
376 #ifndef WIN32
377 const char *fcgi_config_make_dynamic_dir(pool *p, const int wax)
379 const char *err;
380 pool *tp;
382 fcgi_dynamic_dir = ap_pstrcat(p, fcgi_socket_dir, "/dynamic", NULL);
384 if ((err = fcgi_config_make_dir(p, fcgi_dynamic_dir)))
385 return ap_psprintf(p, "can't create dynamic directory \"%s\": %s", fcgi_dynamic_dir, err);
387 /* Don't step on a running server unless its OK. */
388 if (!wax)
389 return NULL;
391 #ifdef APACHE2
393 apr_dir_t * dir;
394 apr_finfo_t finfo;
396 if (apr_pool_create(&tp, p))
397 return "apr_pool_create() failed";
399 if (apr_dir_open(&dir, fcgi_dynamic_dir, tp))
400 return "apr_dir_open() failed";
402 /* delete the contents */
404 while (apr_dir_read(&finfo, APR_FINFO_NAME, dir) == APR_SUCCESS)
406 if (strcmp(finfo.name, ".") == 0 || strcmp(finfo.name, "..") == 0)
407 continue;
409 apr_file_remove(finfo.name, tp);
413 #else /* !APACHE2 */
415 DIR *dp;
416 struct dirent *dirp = NULL;
418 tp = ap_make_sub_pool(p);
420 dp = ap_popendir(tp, fcgi_dynamic_dir);
421 if (dp == NULL) {
422 ap_destroy_pool(tp);
423 return ap_psprintf(p, "can't open dynamic directory \"%s\": %s",
424 fcgi_dynamic_dir, strerror(errno));
427 /* delete the contents */
429 while ((dirp = readdir(dp)) != NULL)
431 if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
432 continue;
434 unlink(ap_pstrcat(tp, fcgi_dynamic_dir, "/", dirp->d_name, NULL));
438 #endif /* !APACHE2 */
440 ap_destroy_pool(tp);
442 return NULL;
444 #endif
446 /*******************************************************************************
447 * Change the directory used for the Unix/Domain sockets from the default.
448 * Create the directory and the "dynamic" subdirectory.
450 const char *fcgi_config_set_socket_dir(cmd_parms *cmd, void *dummy, const char *arg)
452 pool * const tp = cmd->temp_pool;
453 const char * const name = cmd->cmd->name;
454 const char *err;
455 char * arg_nc;
457 err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
458 if (err)
460 return err;
463 if (fcgi_socket_dir) {
464 return ap_psprintf(tp, "%s %s: already defined as \"%s\"",
465 name, arg, fcgi_socket_dir);
468 err = fcgi_config_set_fcgi_uid_n_gid(1);
469 if (err != NULL)
470 return ap_psprintf(tp, "%s %s: %s", name, arg, err);
472 if (fcgi_servers != NULL) {
473 return ap_psprintf(tp,
474 "The %s command must preceed static FastCGI server definitions",
475 name);
478 arg_nc = ap_pstrdup(cmd->pool, arg);
480 #ifndef WIN32
482 #ifdef APACHE2
483 if (apr_filepath_merge(&arg_nc, "", arg, 0, cmd->pool))
484 return ap_psprintf(tp, "%s %s: invalid filepath", name, arg);
485 #else
486 arg_nc = ap_os_canonical_filename(cmd->pool, arg_nc);
487 #endif
489 arg_nc = ap_server_root_relative(cmd->pool, arg_nc);
491 #else /* WIN32 */
493 if (strncmp(arg_nc, "\\\\.\\pipe\\", 9) != 0)
494 return ap_psprintf(tp, "%s %s is invalid format",name, arg_nc);
496 #endif
498 fcgi_socket_dir = arg_nc;
500 #ifdef WIN32
501 fcgi_dynamic_dir = ap_pstrcat(cmd->pool, fcgi_socket_dir, "dynamic", NULL);
502 #else
503 err = fcgi_config_make_dir(tp, fcgi_socket_dir);
504 if (err != NULL)
505 return ap_psprintf(tp, "%s %s: %s", name, arg_nc, err);
507 err = fcgi_config_make_dynamic_dir(cmd->pool, 0);
508 if (err != NULL)
509 return ap_psprintf(tp, "%s %s: %s", name, arg_nc, err);
510 #endif
512 return NULL;
515 /*******************************************************************************
516 * Enable, disable, or specify the path to a wrapper used to invoke all
517 * FastCGI applications.
519 const char *fcgi_config_set_wrapper(cmd_parms *cmd, void *dummy, const char *arg)
521 #ifdef WIN32
522 return ap_psprintf(cmd->temp_pool,
523 "the %s directive is not supported on WIN", cmd->cmd->name);
524 #else
526 const char *err = NULL;
527 const char * const name = cmd->cmd->name;
528 pool * const tp = cmd->temp_pool;
529 char * wrapper = NULL;
531 err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
532 if (err)
534 return err;
537 if (fcgi_wrapper)
539 return ap_psprintf(tp, "%s was already set to \"%s\"",
540 name, fcgi_wrapper);
543 err = fcgi_config_set_fcgi_uid_n_gid(1);
544 if (err != NULL)
545 return ap_psprintf(tp, "%s %s: %s", name, arg, err);
547 if (fcgi_servers != NULL) {
548 return ap_psprintf(tp,
549 "The %s command must preceed static FastCGI server definitions", name);
552 if (strcasecmp(arg, "Off") == 0) {
553 fcgi_wrapper = NULL;
554 return NULL;
557 if (strcasecmp(arg, "On") == 0)
559 wrapper = SUEXEC_BIN;
561 else
563 #ifdef APACHE2
564 if (apr_filepath_merge(&wrapper, "", arg, 0, cmd->pool))
565 return ap_psprintf(tp, "%s %s: invalid filepath", name, arg);
566 #else
567 wrapper = ap_os_canonical_filename(cmd->pool, (char *) arg);
568 #endif
570 wrapper = ap_server_root_relative(cmd->pool, wrapper);
573 err = fcgi_util_check_access(tp, wrapper, NULL, X_OK, fcgi_user_id, fcgi_group_id);
574 if (err)
576 return ap_psprintf(tp, "%s: \"%s\" execute access for server "
577 "(uid %ld, gid %ld) failed: %s", name, wrapper,
578 (long) fcgi_user_id, (long) fcgi_group_id, err);
581 fcgi_wrapper = wrapper;
583 return NULL;
584 #endif /* !WIN32 */
587 /*******************************************************************************
588 * Configure a static FastCGI server.
590 const char *fcgi_config_new_static_server(cmd_parms *cmd, void *dummy, const char *arg)
592 fcgi_server *s;
593 pool *p = cmd->pool, *tp = cmd->temp_pool;
594 const char *name = cmd->cmd->name;
595 char *fs_path = ap_getword_conf(p, &arg);
596 const char *option, *err;
598 /* Allocate temp storage for the array of initial environment variables */
599 char **envp = ap_pcalloc(tp, sizeof(char *) * (MAX_INIT_ENV_VARS + 3));
600 unsigned int envc = 0;
602 #ifdef WIN32
603 HANDLE mutex;
604 #endif
606 err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
607 if (err)
609 return err;
612 if (*fs_path == '\0')
613 return "AppClass requires a pathname!?";
615 if ((err = fcgi_config_set_fcgi_uid_n_gid(1)) != NULL)
616 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
618 #ifdef APACHE2
619 if (apr_filepath_merge(&fs_path, "", fs_path, 0, p))
620 return ap_psprintf(tp, "%s %s: invalid filepath", name, fs_path);
621 #else
622 fs_path = ap_os_canonical_filename(p, fs_path);
623 #endif
624 fs_path = ap_server_root_relative(p, fs_path);
626 ap_getparents(fs_path);
627 ap_no2slash(fs_path);
629 /* See if we've already got one of these configured */
630 s = fcgi_util_fs_get_by_id(fs_path, fcgi_util_get_server_uid(cmd->server),
631 fcgi_util_get_server_gid(cmd->server));
632 if (s != NULL) {
633 if (fcgi_wrapper) {
634 return ap_psprintf(tp,
635 "%s: redefinition of a previously defined FastCGI "
636 "server \"%s\" with uid=%ld and gid=%ld",
637 name, fs_path, (long) fcgi_util_get_server_uid(cmd->server),
638 (long) fcgi_util_get_server_gid(cmd->server));
640 else {
641 return ap_psprintf(tp,
642 "%s: redefinition of a previously defined FastCGI server \"%s\"",
643 name, fs_path);
647 err = fcgi_util_fs_is_path_ok(tp, fs_path, NULL);
648 if (err != NULL) {
649 return ap_psprintf(tp, "%s: \"%s\" %s", name, fs_path, err);
652 s = fcgi_util_fs_new(p);
653 s->fs_path = fs_path;
654 s->directive = APP_CLASS_STANDARD;
655 s->restartOnExit = TRUE;
656 s->numProcesses = 1;
658 #ifdef WIN32
660 /* TCP FastCGI applications require SystemRoot be present in the environment
661 * Put it in both for consistency to the application */
662 fcgi_config_set_env_var(p, envp, &envc, "SystemRoot");
664 mutex = CreateMutex(NULL, FALSE, fs_path);
666 if (mutex == NULL)
668 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
669 "FastCGI: CreateMutex() failed");
670 return "failed to create FastCGI application accept mutex";
673 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
675 s->mutex_env_string = ap_psprintf(p, "_FCGI_MUTEX_=%ld", mutex);
677 #endif
679 /* Parse directive arguments */
680 while (*arg) {
681 option = ap_getword_conf(tp, &arg);
683 if (strcasecmp(option, "-processes") == 0) {
684 if ((err = get_u_int(tp, &arg, &s->numProcesses, 1)))
685 return invalid_value(tp, name, fs_path, option, err);
687 else if (strcasecmp(option, "-restart-delay") == 0) {
688 if ((err = get_u_int(tp, &arg, &s->restartDelay, 0)))
689 return invalid_value(tp, name, fs_path, option, err);
691 else if (strcasecmp(option, "-init-start-delay") == 0) {
692 if ((err = get_int(tp, &arg, &s->initStartDelay, 0)))
693 return invalid_value(tp, name, fs_path, option, err);
695 else if (strcasecmp(option, "-priority") == 0) {
696 if ((err = get_u_int(tp, &arg, &s->processPriority, 0)))
697 return invalid_value(tp, name, fs_path, option, err);
699 else if (strcasecmp(option, "-listen-queue-depth") == 0) {
700 if ((err = get_u_int(tp, &arg, &s->listenQueueDepth, 1)))
701 return invalid_value(tp, name, fs_path, option, err);
703 else if (strcasecmp(option, "-appConnTimeout") == 0) {
704 if ((err = get_u_int(tp, &arg, &s->appConnectTimeout, 0)))
705 return invalid_value(tp, name, fs_path, option, err);
707 else if (strcasecmp(option, "-idle-timeout") == 0) {
708 if ((err = get_u_int(tp, &arg, &s->idle_timeout, 1)))
709 return invalid_value(tp, name, fs_path, option, err);
711 else if (strcasecmp(option, "-port") == 0) {
712 if ((err = get_u_short(tp, &arg, &s->port, 1)))
713 return invalid_value(tp, name, fs_path, option, err);
715 else if (strcasecmp(option, "-socket") == 0) {
716 s->socket_path = ap_getword_conf(tp, &arg);
717 if (*s->socket_path == '\0')
718 return invalid_value(tp, name, fs_path, option, "\"\"");
720 else if (strcasecmp(option, "-initial-env") == 0) {
721 if ((err = get_env_var(p, &arg, envp, &envc)))
722 return invalid_value(tp, name, fs_path, option, err);
724 else if (strcasecmp(option, "-pass-header") == 0) {
725 if ((err = get_pass_header(p, &arg, &s->pass_headers)))
726 return invalid_value(tp, name, fs_path, option, err);
728 else if (strcasecmp(option, "-flush") == 0) {
729 s->flush = 1;
731 else if (strcasecmp(option, "-user") == 0) {
732 #ifdef WIN32
733 return ap_psprintf(tp,
734 "%s %s: the -user option isn't supported on WIN", name, fs_path);
735 #else
736 s->user = ap_getword_conf(tp, &arg);
737 if (*s->user == '\0')
738 return invalid_value(tp, name, fs_path, option, "\"\"");
739 #endif
741 else if (strcasecmp(option, "-group") == 0) {
742 #ifdef WIN32
743 return ap_psprintf(tp,
744 "%s %s: the -group option isn't supported on WIN", name, fs_path);
745 #else
746 s->group = ap_getword_conf(tp, &arg);
747 if (*s->group == '\0')
748 return invalid_value(tp, name, fs_path, option, "\"\"");
749 #endif
751 else {
752 return ap_psprintf(tp, "%s %s: invalid option: %s", name, fs_path, option);
754 } /* while */
756 #ifndef WIN32
757 if (fcgi_wrapper)
759 if (s->group == NULL)
761 s->group = ap_psprintf(tp, "#%ld", fcgi_util_get_server_gid(cmd->server));
764 if (s->user == NULL)
766 s->user = ap_psprintf(p, "#%ld", fcgi_util_get_server_uid(cmd->server));
769 s->uid = ap_uname2id(s->user);
770 s->gid = ap_gname2id(s->group);
772 else if (s->user || s->group)
774 ap_log_error(FCGI_LOG_WARN, cmd->server, "FastCGI: there is no "
775 "fastcgi wrapper set, user/group options are ignored");
778 if ((err = fcgi_util_fs_set_uid_n_gid(p, s, s->uid, s->gid)))
780 return ap_psprintf(tp,
781 "%s %s: invalid user or group: %s", name, fs_path, err);
783 #endif /* !WIN32 */
785 if (s->socket_path != NULL && s->port != 0) {
786 return ap_psprintf(tp,
787 "%s %s: -port and -socket are mutually exclusive options",
788 name, fs_path);
791 /* Move env array to a surviving pool */
792 s->envp = (char **)ap_pcalloc(p, sizeof(char *) * (envc + 4));
793 memcpy(s->envp, envp, sizeof(char *) * envc);
795 /* Initialize process structs */
796 s->procs = fcgi_util_fs_create_procs(p, s->numProcesses);
798 /* Build the appropriate sockaddr structure */
799 if (s->port != 0) {
800 err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->socket_addr,
801 &s->socket_addr_len, NULL, s->port);
802 if (err != NULL)
803 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
804 #ifdef WIN32
805 err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->dest_addr,
806 &s->socket_addr_len, "localhost", s->port);
807 if (err != NULL)
808 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
809 #endif
810 } else {
811 if (s->socket_path == NULL)
812 s->socket_path = fcgi_util_socket_hash_filename(tp, fs_path, s->user, s->group);
814 if (fcgi_socket_dir == NULL)
816 #ifdef WIN32
817 fcgi_socket_dir = DEFAULT_SOCK_DIR;
818 #else
819 fcgi_socket_dir = ap_server_root_relative(p, DEFAULT_SOCK_DIR);
820 #endif
823 s->socket_path = fcgi_util_socket_make_path_absolute(p, s->socket_path, 0);
824 #ifndef WIN32
825 err = fcgi_util_socket_make_domain_addr(p, (struct sockaddr_un **)&s->socket_addr,
826 &s->socket_addr_len, s->socket_path);
827 if (err != NULL)
828 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
829 #endif
832 /* Add it to the list of FastCGI servers */
833 fcgi_util_fs_add(s);
835 return NULL;
838 /*******************************************************************************
839 * Configure a static FastCGI server that is started/managed elsewhere.
841 const char *fcgi_config_new_external_server(cmd_parms *cmd, void *dummy, const char *arg)
843 fcgi_server *s;
844 pool * const p = cmd->pool, *tp = cmd->temp_pool;
845 const char * const name = cmd->cmd->name;
846 char *fs_path = ap_getword_conf(p, &arg);
847 const char *option, *err;
849 err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
850 if (err)
852 return err;
855 if (!*fs_path) {
856 return ap_pstrcat(tp, name, " requires a path and either a -socket or -host option", NULL);
859 #ifdef APACHE2
860 if (apr_filepath_merge(&fs_path, "", fs_path, 0, p))
861 return ap_psprintf(tp, "%s %s: invalid filepath", name, fs_path);
862 #else
863 fs_path = ap_os_canonical_filename(p, fs_path);
864 #endif
866 fs_path = ap_server_root_relative(p, fs_path);
868 ap_getparents(fs_path);
869 ap_no2slash(fs_path);
871 /* See if we've already got one of these bettys configured */
872 s = fcgi_util_fs_get_by_id(fs_path, fcgi_util_get_server_uid(cmd->server),
873 fcgi_util_get_server_gid(cmd->server));
874 if (s != NULL) {
875 if (fcgi_wrapper) {
876 return ap_psprintf(tp,
877 "%s: redefinition of a previously defined class \"%s\" "
878 "with uid=%ld and gid=%ld",
879 name, fs_path, (long) fcgi_util_get_server_uid(cmd->server),
880 (long) fcgi_util_get_server_gid(cmd->server));
882 else
884 return ap_psprintf(tp,
885 "%s: redefinition of previously defined class \"%s\"", name, fs_path);
889 s = fcgi_util_fs_new(p);
890 s->fs_path = fs_path;
891 s->directive = APP_CLASS_EXTERNAL;
893 /* Parse directive arguments */
894 while (*arg != '\0') {
895 option = ap_getword_conf(tp, &arg);
897 if (strcasecmp(option, "-host") == 0) {
898 if ((err = get_host_n_port(p, &arg, &s->host, &s->port)))
899 return invalid_value(tp, name, fs_path, option, err);
901 else if (strcasecmp(option, "-socket") == 0) {
902 s->socket_path = ap_getword_conf(tp, &arg);
903 if (*s->socket_path == '\0')
904 return invalid_value(tp, name, fs_path, option, "\"\"");
906 else if (strcasecmp(option, "-appConnTimeout") == 0) {
907 if ((err = get_u_int(tp, &arg, &s->appConnectTimeout, 0)))
908 return invalid_value(tp, name, fs_path, option, err);
910 else if (strcasecmp(option, "-idle-timeout") == 0) {
911 if ((err = get_u_int(tp, &arg, &s->idle_timeout, 1)))
912 return invalid_value(tp, name, fs_path, option, err);
914 else if (strcasecmp(option, "-pass-header") == 0) {
915 if ((err = get_pass_header(p, &arg, &s->pass_headers)))
916 return invalid_value(tp, name, fs_path, option, err);
918 else if (strcasecmp(option, "-flush") == 0) {
919 s->flush = 1;
921 else if (strcasecmp(option, "-user") == 0) {
922 #ifdef WIN32
923 return ap_psprintf(tp,
924 "%s %s: the -user option isn't supported on WIN", name, fs_path);
925 #else
926 s->user = ap_getword_conf(tp, &arg);
927 if (*s->user == '\0')
928 return invalid_value(tp, name, fs_path, option, "\"\"");
929 #endif
931 else if (strcasecmp(option, "-group") == 0) {
932 #ifdef WIN32
933 return ap_psprintf(tp,
934 "%s %s: the -group option isn't supported on WIN", name, fs_path);
935 #else
936 s->group = ap_getword_conf(tp, &arg);
937 if (*s->group == '\0')
938 return invalid_value(tp, name, fs_path, option, "\"\"");
939 #endif
941 else {
942 return ap_psprintf(tp, "%s %s: invalid option: %s", name, fs_path, option);
944 } /* while */
947 #ifndef WIN32
948 if (fcgi_wrapper)
950 if (s->group == NULL)
952 s->group = ap_psprintf(tp, "#%ld", fcgi_util_get_server_gid(cmd->server));
955 if (s->user == NULL)
957 s->user = ap_psprintf(p, "#%ld", fcgi_util_get_server_uid(cmd->server));
960 s->uid = ap_uname2id(s->user);
961 s->gid = ap_gname2id(s->group);
963 else if (s->user || s->group)
965 ap_log_error(FCGI_LOG_WARN, cmd->server, "FastCGI: there is no "
966 "fastcgi wrapper set, user/group options are ignored");
969 if ((err = fcgi_util_fs_set_uid_n_gid(p, s, s->uid, s->gid)))
971 return ap_psprintf(tp,
972 "%s %s: invalid user or group: %s", name, fs_path, err);
974 #endif /* !WIN32 */
976 /* Require one of -socket or -host, but not both */
977 if (s->socket_path != NULL && s->port != 0) {
978 return ap_psprintf(tp,
979 "%s %s: -host and -socket are mutually exclusive options",
980 name, fs_path);
982 if (s->socket_path == NULL && s->port == 0) {
983 return ap_psprintf(tp,
984 "%s %s: -socket or -host option missing", name, fs_path);
987 /* Build the appropriate sockaddr structure */
988 if (s->port != 0) {
989 err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->socket_addr,
990 &s->socket_addr_len, s->host, s->port);
991 if (err != NULL)
992 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
993 } else {
995 if (fcgi_socket_dir == NULL)
997 #ifdef WIN32
998 fcgi_socket_dir = DEFAULT_SOCK_DIR;
999 #else
1000 fcgi_socket_dir = ap_server_root_relative(p, DEFAULT_SOCK_DIR);
1001 #endif
1004 s->socket_path = fcgi_util_socket_make_path_absolute(p, s->socket_path, 0);
1005 #ifndef WIN32
1006 err = fcgi_util_socket_make_domain_addr(p, (struct sockaddr_un **)&s->socket_addr,
1007 &s->socket_addr_len, s->socket_path);
1008 if (err != NULL)
1009 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
1010 #endif
1013 /* Add it to the list of FastCGI servers */
1014 fcgi_util_fs_add(s);
1016 return NULL;
1020 *----------------------------------------------------------------------
1022 * fcgi_config_set_config --
1024 * Implements the FastCGI FCGIConfig configuration directive.
1025 * This command adds routines to control the execution of the
1026 * dynamic FastCGI processes.
1029 *----------------------------------------------------------------------
1031 const char *fcgi_config_set_config(cmd_parms *cmd, void *dummy, const char *arg)
1033 pool * const p = cmd->pool;
1034 pool * const tp = cmd->temp_pool;
1035 const char *err, *option;
1036 const char * const name = cmd->cmd->name;
1038 /* Allocate temp storage for an initial environment */
1039 unsigned int envc = 0;
1040 char **envp = (char **)ap_pcalloc(tp, sizeof(char *) * (MAX_INIT_ENV_VARS + 3));
1042 err = ap_check_cmd_context(cmd, GLOBAL_ONLY);
1043 if (err)
1045 return err;
1048 /* Parse the directive arguments */
1049 while (*arg) {
1050 option = ap_getword_conf(tp, &arg);
1052 if (strcasecmp(option, "-maxProcesses") == 0) {
1053 if ((err = get_u_int(tp, &arg, &dynamicMaxProcs, 1)))
1054 return invalid_value(tp, name, NULL, option, err);
1056 else if (strcasecmp(option, "-minProcesses") == 0) {
1057 if ((err = get_int(tp, &arg, &dynamicMinProcs, 0)))
1058 return invalid_value(tp, name, NULL, option, err);
1060 else if (strcasecmp(option, "-maxClassProcesses") == 0) {
1061 if ((err = get_int(tp, &arg, &dynamicMaxClassProcs, 1)))
1062 return invalid_value(tp, name, NULL, option, err);
1064 else if (strcasecmp(option, "-killInterval") == 0) {
1065 if ((err = get_u_int(tp, &arg, &dynamicKillInterval, 1)))
1066 return invalid_value(tp, name, NULL, option, err);
1068 else if (strcasecmp(option, "-updateInterval") == 0) {
1069 if ((err = get_u_int(tp, &arg, &dynamicUpdateInterval, 1)))
1070 return invalid_value(tp, name, NULL, option, err);
1072 else if (strcasecmp(option, "-gainValue") == 0) {
1073 if ((err = get_float(tp, &arg, &dynamicGain, 0.0, 1.0)))
1074 return invalid_value(tp, name, NULL, option, err);
1076 else if ((strcasecmp(option, "-singleThreshold") == 0)
1077 || (strcasecmp(option, "-singleThreshhold") == 0))
1079 if ((err = get_int(tp, &arg, &dynamicThreshold1, 0)))
1080 return invalid_value(tp, name, NULL, option, err);
1082 else if ((strcasecmp(option, "-multiThreshold") == 0)
1083 || (strcasecmp(option, "-multiThreshhold") == 0))
1085 if ((err = get_int(tp, &arg, &dynamicThresholdN, 0)))
1086 return invalid_value(tp, name, NULL, option, err);
1088 else if (strcasecmp(option, "-startDelay") == 0) {
1089 if ((err = get_u_int(tp, &arg, &dynamicPleaseStartDelay, 1)))
1090 return invalid_value(tp, name, NULL, option, err);
1092 else if (strcasecmp(option, "-initial-env") == 0) {
1093 if ((err = get_env_var(p, &arg, envp, &envc)))
1094 return invalid_value(tp, name, NULL, option, err);
1096 else if (strcasecmp(option, "-pass-header") == 0) {
1097 if ((err = get_pass_header(p, &arg, &dynamic_pass_headers)))
1098 return invalid_value(tp, name, NULL, option, err);
1100 else if (strcasecmp(option, "-appConnTimeout") == 0) {
1101 if ((err = get_u_int(tp, &arg, &dynamicAppConnectTimeout, 0)))
1102 return invalid_value(tp, name, NULL, option, err);
1104 else if (strcasecmp(option, "-idle-timeout") == 0) {
1105 if ((err = get_u_int(tp, &arg, &dynamic_idle_timeout, 1)))
1106 return invalid_value(tp, name, NULL, option, err);
1108 else if (strcasecmp(option, "-listen-queue-depth") == 0) {
1109 if ((err = get_u_int(tp, &arg, &dynamicListenQueueDepth, 1)))
1110 return invalid_value(tp, name, NULL, option, err);
1112 else if (strcasecmp(option, "-restart-delay") == 0) {
1113 if ((err = get_u_int(tp, &arg, &dynamicRestartDelay, 0)))
1114 return invalid_value(tp, name, NULL, option, err);
1116 else if (strcasecmp(option, "-init-start-delay") == 0) {
1117 if ((err = get_u_int(tp, &arg, &dynamicInitStartDelay, 0)))
1118 return invalid_value(tp, name, NULL, option, err);
1120 else if (strcasecmp(option, "-processSlack") == 0) {
1121 if ((err = get_u_int(tp, &arg, &dynamicProcessSlack, 1)))
1122 return invalid_value(tp, name, NULL, option, err);
1124 else if (strcasecmp(option, "-restart") == 0) {
1125 dynamicAutoRestart = 1;
1127 else if (strcasecmp(option, "-autoUpdate") == 0) {
1128 dynamicAutoUpdate = 1;
1130 else if (strcasecmp(option, "-flush") == 0) {
1131 dynamicFlush = TRUE;
1133 else {
1134 return ap_psprintf(tp, "%s: invalid option: %s", name, option);
1136 } /* while */
1138 if (dynamicProcessSlack >= dynamicMaxProcs + 1) {
1139 /* the kill policy would work unexpectedly */
1140 return ap_psprintf(tp,
1141 "%s: processSlack (%u) must be less than maxProcesses (%u) + 1",
1142 name, dynamicProcessSlack, dynamicMaxProcs);
1145 /* Move env array to a surviving pool, leave 2 extra slots for
1146 * WIN32 _FCGI_MUTEX_ and _FCGI_SHUTDOWN_EVENT_ */
1147 dynamicEnvp = (char **)ap_pcalloc(p, sizeof(char *) * (envc + 4));
1148 memcpy(dynamicEnvp, envp, sizeof(char *) * envc);
1150 return NULL;
1153 void *fcgi_config_create_dir_config(pool *p, char *dummy)
1155 fcgi_dir_config *dir_config = ap_pcalloc(p, sizeof(fcgi_dir_config));
1157 dir_config->authenticator_options = FCGI_AUTHORITATIVE;
1158 dir_config->authorizer_options = FCGI_AUTHORITATIVE;
1159 dir_config->access_checker_options = FCGI_AUTHORITATIVE;
1161 return dir_config;
1165 const char *fcgi_config_new_auth_server(cmd_parms * cmd,
1166 void * dircfg, const char *fs_path, const char * compat)
1168 fcgi_dir_config * dir_config = (fcgi_dir_config *) dircfg;
1169 pool * const tp = cmd->temp_pool;
1170 char * auth_server;
1172 #ifdef APACHE2
1173 if (apr_filepath_merge(&auth_server, "", fs_path, 0, cmd->pool))
1174 return ap_psprintf(tp, "%s %s: invalid filepath", cmd->cmd->name, fs_path);
1175 #else
1176 auth_server = (char *) ap_os_canonical_filename(cmd->pool, fs_path);
1177 #endif
1179 auth_server = ap_server_root_relative(cmd->pool, auth_server);
1181 /* Make sure its already configured or at least a candidate for dynamic */
1182 if (fcgi_util_fs_get_by_id(auth_server, fcgi_util_get_server_uid(cmd->server),
1183 fcgi_util_get_server_gid(cmd->server)) == NULL)
1185 const char *err = fcgi_util_fs_is_path_ok(tp, auth_server, NULL);
1186 if (err)
1187 return ap_psprintf(tp, "%s: \"%s\" %s", cmd->cmd->name, auth_server, err);
1190 if (compat && strcasecmp(compat, "-compat"))
1191 return ap_psprintf(cmd->temp_pool, "%s: unknown option: \"%s\"", cmd->cmd->name, compat);
1193 switch((int)cmd->info) {
1194 case FCGI_AUTH_TYPE_AUTHENTICATOR:
1195 dir_config->authenticator = auth_server;
1196 dir_config->authenticator_options |= (compat) ? FCGI_COMPAT : 0;
1197 break;
1198 case FCGI_AUTH_TYPE_AUTHORIZER:
1199 dir_config->authorizer = auth_server;
1200 dir_config->authorizer_options |= (compat) ? FCGI_COMPAT : 0;
1201 break;
1202 case FCGI_AUTH_TYPE_ACCESS_CHECKER:
1203 dir_config->access_checker = auth_server;
1204 dir_config->access_checker_options |= (compat) ? FCGI_COMPAT : 0;
1205 break;
1208 return NULL;
1211 const char *fcgi_config_set_authoritative_slot(cmd_parms * cmd,
1212 void * dir_config, int arg)
1214 int offset = (int)(long)cmd->info;
1216 if (arg)
1217 *((u_char *)dir_config + offset) |= FCGI_AUTHORITATIVE;
1218 else
1219 *((u_char *)dir_config + offset) &= ~FCGI_AUTHORITATIVE;
1221 return NULL;