Add -user and -group options to FastCgiServer and FastCgiExternalServer.
[mod_fastcgi.git] / fcgi_config.c
blobed9293479af96c567591dd6f70ef6f0c99e81789
1 /*
2 * $Id: fcgi_config.c,v 1.37 2002/08/20 03:01:15 robs Exp $
3 */
5 #include "fcgi.h"
7 #ifdef APACHE2
8 #include <limits.h>
9 #ifdef WIN32
10 #include <direct.h>
11 #else
12 #include <unistd.h>
13 #include "unixd.h"
14 #endif
15 #endif
17 #ifdef WIN32
18 /* warning C4100: unreferenced formal parameter */
19 /* warning C4706: assignment within conditional expression */
20 #pragma warning( disable : 4100 4706 )
21 #endif
23 /*******************************************************************************
24 * Get the next configuration directive argument, & return an in_addr and port.
25 * The arg must be in the form "host:port" where host can be an IP or hostname.
26 * The pool arg should be persistant storage.
28 static const char *get_host_n_port(pool *p, const char **arg,
29 const char **host, u_short *port)
31 char *cvptr, *portStr;
32 long tmp;
34 *host = ap_getword_conf(p, arg);
35 if (**host == '\0')
36 return "\"\"";
38 portStr = strchr(*host, ':');
39 if (portStr == NULL)
40 return "missing port specification";
42 /* Split the host and port portions */
43 *portStr++ = '\0';
45 /* Convert port number */
46 tmp = (u_short) strtol(portStr, &cvptr, 10);
47 if (*cvptr != '\0' || tmp < 1 || tmp > USHRT_MAX)
48 return ap_pstrcat(p, "bad port number \"", portStr, "\"", NULL);
50 *port = (unsigned short) tmp;
52 return NULL;
55 /*******************************************************************************
56 * Get the next configuration directive argument, & return an u_short.
57 * The pool arg should be temporary storage.
59 static const char *get_u_short(pool *p, const char **arg,
60 u_short *num, u_short min)
62 char *ptr;
63 long tmp;
64 const char *txt = ap_getword_conf(p, arg);
66 if (*txt == '\0') {
67 return "\"\"";
70 tmp = strtol(txt, &ptr, 10);
72 if (*ptr != '\0') {
73 return ap_pstrcat(p, "\"", txt, "\" must be a positive integer", NULL);
76 if (tmp < min || tmp > USHRT_MAX) {
77 return ap_psprintf(p, "\"%u\" must be >= %u and < %u", *num, min, USHRT_MAX);
80 *num = (u_short) tmp;
82 return NULL;
85 static const char *get_int(pool *p, const char **arg, int *num, int min)
87 char *cp;
88 const char *val = ap_getword_conf(p, arg);
90 if (*val == '\0')
92 return "\"\"";
95 *num = (int) strtol(val, &cp, 10);
97 if (*cp != '\0')
99 return ap_pstrcat(p, "can't parse ", "\"", val, "\"", NULL);
101 else if (*num < min)
103 return ap_psprintf(p, "\"%d\" must be >= %d", *num, min);
106 return NULL;
109 /*******************************************************************************
110 * Get the next configuration directive argument, & return an u_int.
111 * The pool arg should be temporary storage.
113 static const char *get_u_int(pool *p, const char **arg,
114 u_int *num, u_int min)
116 char *ptr;
117 const char *val = ap_getword_conf(p, arg);
119 if (*val == '\0')
120 return "\"\"";
121 *num = (u_int)strtol(val, &ptr, 10);
123 if (*ptr != '\0')
124 return ap_pstrcat(p, "\"", val, "\" must be a positive integer", NULL);
125 else if (*num < min)
126 return ap_psprintf(p, "\"%u\" must be >= %u", *num, min);
127 return NULL;
130 /*******************************************************************************
131 * Get the next configuration directive argument, & return a float.
132 * The pool arg should be temporary storage.
134 static const char *get_float(pool *p, const char **arg,
135 float *num, float min, float max)
137 char *ptr;
138 const char *val = ap_getword_conf(p, arg);
140 if (*val == '\0')
141 return "\"\"";
142 *num = (float) strtod(val, &ptr);
144 if (*ptr != '\0')
145 return ap_pstrcat(p, "\"", val, "\" is not a floating point number", NULL);
146 if (*num < min || *num > max)
147 return ap_psprintf(p, "\"%f\" is not between %f and %f", *num, min, max);
148 return NULL;
151 const char *fcgi_config_set_env_var(pool *p, char **envp, unsigned int *envc, char * var)
153 if (*envc >= MAX_INIT_ENV_VARS) {
154 return "too many variables, must be <= MAX_INIT_ENV_VARS";
157 if (strchr(var, '=') == NULL) {
158 *(envp + *envc) = ap_pstrcat(p, var, "=", getenv(var), NULL);
160 else {
161 *(envp + *envc) = var;
164 (*envc)++;
166 return NULL;
169 /*******************************************************************************
170 * Get the next configuration directive argument, & add it to an env array.
171 * The pool arg should be permanent storage.
173 static const char *get_env_var(pool *p, const char **arg, char **envp, unsigned int *envc)
175 char * const val = ap_getword_conf(p, arg);
177 if (*val == '\0') {
178 return "\"\"";
181 return fcgi_config_set_env_var(p, envp, envc, val);
184 static const char *get_pass_header(pool *p, const char **arg, array_header **array)
186 const char **header;
188 if (!*array) {
189 *array = ap_make_array(p, 10, sizeof(char*));
192 header = (const char **)ap_push_array(*array);
193 *header = ap_getword_conf(p, arg);
195 return header ? NULL : "\"\"";
198 /*******************************************************************************
199 * Return a "standard" message for common configuration errors.
201 static const char *invalid_value(pool *p, const char *cmd, const char *id,
202 const char *opt, const char *err)
204 return ap_psprintf(p, "%s%s%s: invalid value for %s: %s",
205 cmd, id ? " " : "", id ? id : "", opt, err);
208 /*******************************************************************************
209 * Set/Reset the uid/gid that Apache and the PM will run as. This is ap_user_id
210 * and ap_group_id if we're started as root, and euid/egid otherwise. Also try
211 * to check that the config files don't set the User/Group after a FastCGI
212 * directive is used that depends on it.
214 /*@@@ To be complete, we should save a handle to the server each AppClass is
215 * configured in and at init() check that the user/group is still what we
216 * thought it was. Also the other directives should only be allowed in the
217 * parent Apache server.
219 const char *fcgi_config_set_fcgi_uid_n_gid(int set)
221 static int isSet = 0;
223 #ifndef WIN32
225 uid_t uid = geteuid();
226 gid_t gid = getegid();
228 if (set == 0) {
229 isSet = 0;
230 fcgi_user_id = (uid_t)-1;
231 fcgi_group_id = (gid_t)-1;
232 return NULL;
235 if (uid == 0) {
236 uid = ap_user_id;
239 if (gid == 0) {
240 gid = ap_group_id;
243 if (isSet && (uid != fcgi_user_id || gid != fcgi_group_id)) {
244 return "User/Group commands must preceed FastCGI server definitions";
247 isSet = 1;
248 fcgi_user_id = uid;
249 fcgi_group_id = gid;
251 #endif /* !WIN32 */
253 return NULL;
256 apcb_t fcgi_config_reset_globals(void* dummy)
258 fcgi_config_pool = NULL;
259 fcgi_servers = NULL;
260 fcgi_config_set_fcgi_uid_n_gid(0);
261 fcgi_wrapper = NULL;
262 fcgi_socket_dir = DEFAULT_SOCK_DIR;
264 fcgi_dynamic_total_proc_count = 0;
265 fcgi_dynamic_epoch = 0;
266 fcgi_dynamic_last_analyzed = 0;
268 dynamicMaxProcs = FCGI_DEFAULT_MAX_PROCS;
269 dynamicMinProcs = FCGI_DEFAULT_MIN_PROCS;
270 dynamicMaxClassProcs = FCGI_DEFAULT_MAX_CLASS_PROCS;
271 dynamicKillInterval = FCGI_DEFAULT_KILL_INTERVAL;
272 dynamicUpdateInterval = FCGI_DEFAULT_UPDATE_INTERVAL;
273 dynamicGain = FCGI_DEFAULT_GAIN;
274 dynamicThreshold1 = FCGI_DEFAULT_THRESHOLD_1;
275 dynamicThresholdN = FCGI_DEFAULT_THRESHOLD_N;
276 dynamicPleaseStartDelay = FCGI_DEFAULT_START_PROCESS_DELAY;
277 dynamicAppConnectTimeout = FCGI_DEFAULT_APP_CONN_TIMEOUT;
278 dynamicEnvp = &fcgi_empty_env;
279 dynamicProcessSlack = FCGI_DEFAULT_PROCESS_SLACK;
280 dynamicAutoRestart = FCGI_DEFAULT_RESTART_DYNAMIC;
281 dynamicAutoUpdate = FCGI_DEFAULT_AUTOUPDATE;
282 dynamicListenQueueDepth = FCGI_DEFAULT_LISTEN_Q;
283 dynamicInitStartDelay = DEFAULT_INIT_START_DELAY;
284 dynamicRestartDelay = FCGI_DEFAULT_RESTART_DELAY;
285 dynamic_pass_headers = NULL;
286 dynamic_idle_timeout = FCGI_DEFAULT_IDLE_TIMEOUT;
287 dynamicFlush = FCGI_FLUSH;
289 #ifndef WIN32
290 /* Close any old pipe (HUP/USR1) */
291 if (fcgi_pm_pipe[0] != -1) {
292 close(fcgi_pm_pipe[0]);
293 fcgi_pm_pipe[0] = -1;
295 if (fcgi_pm_pipe[1] != -1) {
296 close(fcgi_pm_pipe[1]);
297 fcgi_pm_pipe[1] = -1;
299 #endif
301 return APCB_OK;
304 /*******************************************************************************
305 * Create a directory to hold Unix/Domain sockets.
307 const char *fcgi_config_make_dir(pool *tp, char *path)
309 struct stat finfo;
310 const char *err = NULL;
312 /* Is the directory spec'd correctly */
313 if (*path != '/') {
314 return "path is not absolute (it must start with a \"/\")";
316 else {
317 int i = strlen(path) - 1;
319 /* Strip trailing "/"s */
320 while(i > 0 && path[i] == '/') path[i--] = '\0';
323 /* Does it exist? */
324 if (stat(path, &finfo) != 0) {
325 /* No, but maybe we can create it */
326 #ifdef WIN32
327 if (mkdir(path) != 0)
328 #else
329 if (mkdir(path, S_IRWXU) != 0)
330 #endif
332 return ap_psprintf(tp,
333 "doesn't exist and can't be created: %s",
334 strerror(errno));
337 #ifndef WIN32
338 /* If we're root, we're gonna setuid/setgid so we need to chown */
339 if (geteuid() == 0 && chown(path, ap_user_id, ap_group_id) != 0) {
340 return ap_psprintf(tp,
341 "can't chown() to the server (uid %ld, gid %ld): %s",
342 (long)ap_user_id, (long)ap_group_id, strerror(errno));
344 #endif
346 else {
347 /* Yes, is it a directory? */
348 if (!S_ISDIR(finfo.st_mode))
349 return "isn't a directory!";
351 /* Can we RWX in there? */
352 #ifdef WIN32
353 err = fcgi_util_check_access(tp, NULL, &finfo, _S_IREAD | _S_IWRITE | _S_IEXEC, fcgi_user_id, fcgi_group_id);
354 #else
355 err = fcgi_util_check_access(tp, NULL, &finfo, R_OK | W_OK | X_OK,
356 fcgi_user_id, fcgi_group_id);
357 #endif
358 if (err != NULL) {
359 return ap_psprintf(tp,
360 "access for server (uid %ld, gid %ld) failed: %s",
361 (long)fcgi_user_id, (long)fcgi_group_id, err);
364 return NULL;
367 /*******************************************************************************
368 * Create a "dynamic" subdirectory. If the directory
369 * already exists we don't mess with it unless 'wax' is set.
371 const char *fcgi_config_make_dynamic_dir(pool *p, const int wax)
373 #ifndef WIN32
374 const char *err;
375 pool *tp;
377 fcgi_dynamic_dir = ap_pstrcat(p, fcgi_socket_dir, "/dynamic", NULL);
379 if ((err = fcgi_config_make_dir(p, fcgi_dynamic_dir)))
380 return ap_psprintf(p, "can't create dynamic directory \"%s\": %s", fcgi_dynamic_dir, err);
382 /* Don't step on a running server unless its OK. */
383 if (!wax)
384 return NULL;
386 #ifdef APACHE2
388 apr_dir_t * dir;
389 apr_finfo_t finfo;
391 if (apr_pool_create(&tp, p))
392 return "apr_pool_create() failed";
394 if (apr_dir_open(&dir, fcgi_dynamic_dir, tp))
395 return "apr_dir_open() failed";
397 /* delete the contents */
399 while (apr_dir_read(&finfo, APR_FINFO_NAME, dir) == APR_SUCCESS)
401 if (strcmp(finfo.name, ".") == 0 || strcmp(finfo.name, "..") == 0)
402 continue;
404 apr_file_remove(finfo.name, tp);
408 #else /* !APACHE2 */
410 DIR *dp;
411 struct dirent *dirp = NULL;
413 tp = ap_make_sub_pool(p);
415 dp = ap_popendir(tp, fcgi_dynamic_dir);
416 if (dp == NULL) {
417 ap_destroy_pool(tp);
418 return ap_psprintf(p, "can't open dynamic directory \"%s\": %s",
419 fcgi_dynamic_dir, strerror(errno));
422 /* delete the contents */
424 while ((dirp = readdir(dp)) != NULL)
426 if (strcmp(dirp->d_name, ".") == 0 || strcmp(dirp->d_name, "..") == 0)
427 continue;
429 unlink(ap_pstrcat(tp, fcgi_dynamic_dir, "/", dirp->d_name, NULL));
433 #endif /* !APACHE2 */
435 ap_destroy_pool(tp);
437 #else
438 fcgi_dynamic_dir = ap_pstrcat(p, fcgi_socket_dir, "dynamic", NULL);
439 #endif
440 return NULL;
444 /*******************************************************************************
445 * Change the directory used for the Unix/Domain sockets from the default.
446 * Create the directory and the "dynamic" subdirectory.
448 const char *fcgi_config_set_socket_dir(cmd_parms *cmd, void *dummy, const char *arg)
450 pool * const tp = cmd->temp_pool;
451 const char * const name = cmd->cmd->name;
452 const char *err;
453 char * arg_nc;
455 if (strcmp(fcgi_socket_dir, DEFAULT_SOCK_DIR) != 0) {
456 return ap_psprintf(tp, "%s %s: already defined as \"%s\"",
457 name, arg, fcgi_socket_dir);
460 err = fcgi_config_set_fcgi_uid_n_gid(1);
461 if (err != NULL)
462 return ap_psprintf(tp, "%s %s: %s", name, arg, err);
464 if (fcgi_servers != NULL) {
465 return ap_psprintf(tp,
466 "The %s command must preceed static FastCGI server definitions",
467 name);
470 arg_nc = ap_pstrdup(cmd->pool, arg);
472 #ifndef WIN32
474 #ifdef APACHE2
475 if (apr_filepath_merge(&arg_nc, "", arg, 0, cmd->pool))
476 return ap_psprintf(tp, "%s %s: invalid filepath", name, arg);
477 #else
478 arg_nc = ap_os_canonical_filename(cmd->pool, arg_nc);
479 #endif
481 arg_nc = ap_server_root_relative(cmd->pool, arg_nc);
483 #else /* WIN32 */
485 if (strncmp(arg_nc, "\\\\.\\pipe\\", 9) != 0)
486 return ap_psprintf(tp, "%s %s is invalid format",name, arg_nc);
488 #endif
490 fcgi_socket_dir = arg_nc;
492 #ifndef WIN32
493 err = fcgi_config_make_dir(tp, fcgi_socket_dir);
494 if (err != NULL)
495 return ap_psprintf(tp, "%s %s: %s", name, arg_nc, err);
497 err = fcgi_config_make_dynamic_dir(cmd->pool, 0);
498 if (err != NULL)
499 return ap_psprintf(tp, "%s %s: %s", name, arg_nc, err);
500 #endif
502 return NULL;
505 /*******************************************************************************
506 * Enable, disable, or specify the path to a wrapper used to invoke all
507 * FastCGI applications.
509 const char *fcgi_config_set_wrapper(cmd_parms *cmd, void *dummy, const char *arg)
511 #ifdef APACHE2
513 * AP2TODO use ap_run_get_suexec_identity() (this will be hard as it
514 * takes a request_rec) and/or mod_userdir_user note
516 return ap_psprintf(cmd->temp_pool, "%s isn't supported yet under Apache2", cmd->cmd->name);
517 #else
518 const char *err = NULL;
519 const char * const name = cmd->cmd->name;
520 pool * const tp = cmd->temp_pool;
521 char * wrapper;
523 if (!ap_suexec_enabled && strcasecmp(arg, "On") == 0) {
524 fprintf(stderr, "Warning: \"%s On\" requires SUEXEC be enabled in Apache", name);
525 return NULL;
528 err = fcgi_config_set_fcgi_uid_n_gid(1);
529 if (err != NULL)
530 return ap_psprintf(tp, "%s %s: %s", name, arg, err);
532 if (fcgi_servers != NULL) {
533 return ap_psprintf(tp,
534 "The %s command must preceed static FastCGI server definitions", name);
537 if (strcasecmp(arg, "On") == 0) {
538 fcgi_wrapper = SUEXEC_BIN;
540 else if (strcasecmp(arg, "Off") == 0) {
541 fcgi_wrapper = NULL;
543 else {
544 wrapper = (char *) ap_os_canonical_filename(cmd->pool, arg);
545 wrapper = ap_server_root_relative(cmd->pool, wrapper);
547 #ifdef WIN32
548 err = fcgi_util_check_access(tp, wrapper, NULL, _S_IEXEC, fcgi_user_id, fcgi_group_id);
549 #else
550 err = fcgi_util_check_access(tp, wrapper, NULL, X_OK, fcgi_user_id, fcgi_group_id);
551 #endif
553 if (err != NULL) {
554 return ap_psprintf(tp,
555 "%s: \"%s\" access for server (uid %ld, gid %ld) failed: %s",
556 name, wrapper, (long)fcgi_user_id, (long)fcgi_group_id, err);
559 fcgi_wrapper = wrapper;
561 return NULL;
562 #endif /* !APACHE2 */
565 /*******************************************************************************
566 * Configure a static FastCGI server.
568 const char *fcgi_config_new_static_server(cmd_parms *cmd, void *dummy, const char *arg)
570 fcgi_server *s;
571 pool *p = cmd->pool, *tp = cmd->temp_pool;
572 const char *name = cmd->cmd->name;
573 char *fs_path = ap_getword_conf(p, &arg);
574 const char *option, *err;
576 /* Allocate temp storage for the array of initial environment variables */
577 char **envp = ap_pcalloc(tp, sizeof(char *) * (MAX_INIT_ENV_VARS + 3));
578 unsigned int envc = 0;
580 #ifdef WIN32
581 HANDLE mutex;
582 #endif
584 if (*fs_path == '\0')
585 return "AppClass requires a pathname!?";
587 if ((err = fcgi_config_set_fcgi_uid_n_gid(1)) != NULL)
588 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
590 #ifdef APACHE2
591 if (apr_filepath_merge(&fs_path, "", fs_path, 0, p))
592 return ap_psprintf(tp, "%s %s: invalid filepath", name, fs_path);
593 #else
594 fs_path = ap_os_canonical_filename(p, fs_path);
595 #endif
596 fs_path = ap_server_root_relative(p, fs_path);
598 ap_getparents(fs_path);
599 ap_no2slash(fs_path);
601 /* See if we've already got one of these configured */
602 s = fcgi_util_fs_get_by_id(fs_path, fcgi_util_get_server_uid(cmd->server),
603 fcgi_util_get_server_gid(cmd->server));
604 if (s != NULL) {
605 if (fcgi_wrapper) {
606 return ap_psprintf(tp,
607 "%s: redefinition of a previously defined FastCGI "
608 "server \"%s\" with uid=%ld and gid=%ld",
609 name, fs_path, (long) fcgi_util_get_server_uid(cmd->server),
610 (long) fcgi_util_get_server_gid(cmd->server));
612 else {
613 return ap_psprintf(tp,
614 "%s: redefinition of a previously defined FastCGI server \"%s\"",
615 name, fs_path);
619 err = fcgi_util_fs_is_path_ok(tp, fs_path, NULL);
620 if (err != NULL) {
621 return ap_psprintf(tp, "%s: \"%s\" %s", name, fs_path, err);
624 s = fcgi_util_fs_new(p);
625 s->fs_path = fs_path;
626 s->directive = APP_CLASS_STANDARD;
627 s->restartOnExit = TRUE;
628 s->numProcesses = 1;
630 #ifdef WIN32
632 // TCP FastCGI applications require SystemRoot be present in the environment
633 // Put it in both for consistency to the application
634 fcgi_config_set_env_var(tp, envp, &envc, "SystemRoot");
636 mutex = CreateMutex(NULL, FALSE, fs_path);
638 if (mutex == NULL)
640 ap_log_error(FCGI_LOG_ALERT, fcgi_apache_main_server,
641 "FastCGI: CreateMutex() failed");
642 return "failed to create FastCGI application accept mutex";
645 SetHandleInformation(mutex, HANDLE_FLAG_INHERIT, TRUE);
647 s->mutex_env_string = ap_psprintf(p, "_FCGI_MUTEX_=%ld", mutex);
649 #elif !defined(APACHE2)
651 if (fcgi_wrapper) {
652 struct passwd *pw;
653 struct group *gr;
655 s->uid = cmd->server->server_uid;
656 pw = getpwuid(s->uid);
657 if (pw == NULL) {
658 return ap_psprintf(tp, "mod_fastcgi: "
659 "getpwuid() couldn't determine the username for uid '%ld', "
660 "you probably need to modify the User directive: %s",
661 (long)s->uid, strerror(errno));
663 s->user = ap_pstrdup(p, pw->pw_name);
664 s->username = s->user;
666 s->gid = cmd->server->server_gid;
667 gr = getgrgid(s->gid);
668 if (gr == NULL) {
669 return ap_psprintf(tp, "mod_fastcgi: "
670 "getgrgid() couldn't determine the group name for gid '%ld', "
671 "you probably need to modify the Group directive: %s\n",
672 (long)s->gid, strerror(errno));
674 s->group = ap_pstrdup(p, gr->gr_name);
676 #endif
678 /* Parse directive arguments */
679 while (*arg) {
680 option = ap_getword_conf(tp, &arg);
682 if (strcasecmp(option, "-processes") == 0) {
683 if ((err = get_u_int(tp, &arg, &s->numProcesses, 1)))
684 return invalid_value(tp, name, fs_path, option, err);
686 else if (strcasecmp(option, "-restart-delay") == 0) {
687 if ((err = get_u_int(tp, &arg, &s->restartDelay, 0)))
688 return invalid_value(tp, name, fs_path, option, err);
690 else if (strcasecmp(option, "-init-start-delay") == 0) {
691 if ((err = get_int(tp, &arg, &s->initStartDelay, 0)))
692 return invalid_value(tp, name, fs_path, option, err);
694 else if (strcasecmp(option, "-priority") == 0) {
695 if ((err = get_u_int(tp, &arg, &s->processPriority, 0)))
696 return invalid_value(tp, name, fs_path, option, err);
698 else if (strcasecmp(option, "-listen-queue-depth") == 0) {
699 if ((err = get_u_int(tp, &arg, &s->listenQueueDepth, 1)))
700 return invalid_value(tp, name, fs_path, option, err);
702 else if (strcasecmp(option, "-appConnTimeout") == 0) {
703 if ((err = get_u_int(tp, &arg, &s->appConnectTimeout, 0)))
704 return invalid_value(tp, name, fs_path, option, err);
706 else if (strcasecmp(option, "-idle-timeout") == 0) {
707 if ((err = get_u_int(tp, &arg, &s->idle_timeout, 1)))
708 return invalid_value(tp, name, fs_path, option, err);
710 else if (strcasecmp(option, "-port") == 0) {
711 if ((err = get_u_short(tp, &arg, &s->port, 1)))
712 return invalid_value(tp, name, fs_path, option, err);
714 else if (strcasecmp(option, "-socket") == 0) {
715 s->socket_path = ap_getword_conf(tp, &arg);
716 if (*s->socket_path == '\0')
717 return invalid_value(tp, name, fs_path, option, "\"\"");
719 else if (strcasecmp(option, "-initial-env") == 0) {
720 if ((err = get_env_var(p, &arg, envp, &envc)))
721 return invalid_value(tp, name, fs_path, option, err);
723 else if (strcasecmp(option, "-pass-header") == 0) {
724 if ((err = get_pass_header(p, &arg, &s->pass_headers)))
725 return invalid_value(tp, name, fs_path, option, err);
727 else if (strcasecmp(option, "-flush") == 0) {
728 s->flush = 1;
730 else {
731 return ap_psprintf(tp, "%s %s: invalid option: %s", name, fs_path, option);
733 } /* while */
735 if (s->socket_path != NULL && s->port != 0) {
736 return ap_psprintf(tp,
737 "%s %s: -port and -socket are mutually exclusive options",
738 name, fs_path);
741 /* Move env array to a surviving pool */
742 s->envp = (char **)ap_pcalloc(p, sizeof(char *) * (envc + 4));
743 memcpy(s->envp, envp, sizeof(char *) * envc);
745 /* Initialize process structs */
746 s->procs = fcgi_util_fs_create_procs(p, s->numProcesses);
748 /* Build the appropriate sockaddr structure */
749 if (s->port != 0) {
750 err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->socket_addr,
751 &s->socket_addr_len, NULL, s->port);
752 if (err != NULL)
753 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
754 #ifdef WIN32
755 err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->dest_addr,
756 &s->socket_addr_len, "localhost", s->port);
757 if (err != NULL)
758 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
759 #endif
760 } else {
761 if (s->socket_path == NULL)
762 s->socket_path = fcgi_util_socket_hash_filename(tp, fs_path, s->user, s->group);
763 s->socket_path = fcgi_util_socket_make_path_absolute(p, s->socket_path, 0);
764 #ifndef WIN32
765 err = fcgi_util_socket_make_domain_addr(p, (struct sockaddr_un **)&s->socket_addr,
766 &s->socket_addr_len, s->socket_path);
767 if (err != NULL)
768 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
769 #endif
772 /* Add it to the list of FastCGI servers */
773 fcgi_util_fs_add(s);
775 return NULL;
778 /*******************************************************************************
779 * Configure a static FastCGI server that is started/managed elsewhere.
781 const char *fcgi_config_new_external_server(cmd_parms *cmd, void *dummy, const char *arg)
783 fcgi_server *s;
784 pool * const p = cmd->pool, *tp = cmd->temp_pool;
785 const char * const name = cmd->cmd->name;
786 char *fs_path = ap_getword_conf(p, &arg);
787 const char *option, *err;
789 if (!*fs_path) {
790 return ap_pstrcat(tp, name, " requires a path and either a -socket or -host option", NULL);
793 #ifdef APACHE2
794 if (apr_filepath_merge(&fs_path, "", fs_path, 0, p))
795 return ap_psprintf(tp, "%s %s: invalid filepath", name, fs_path);
796 #else
797 fs_path = ap_os_canonical_filename(p, fs_path);
798 #endif
800 fs_path = ap_server_root_relative(p, fs_path);
802 ap_getparents(fs_path);
803 ap_no2slash(fs_path);
805 /* See if we've already got one of these bettys configured */
806 s = fcgi_util_fs_get_by_id(fs_path, fcgi_util_get_server_uid(cmd->server),
807 fcgi_util_get_server_gid(cmd->server));
808 if (s != NULL) {
809 if (fcgi_wrapper) {
810 return ap_psprintf(tp,
811 "%s: redefinition of a previously defined class \"%s\" "
812 "with uid=%ld and gid=%ld",
813 name, fs_path, (long) fcgi_util_get_server_uid(cmd->server),
814 (long) fcgi_util_get_server_gid(cmd->server));
816 else
818 return ap_psprintf(tp,
819 "%s: redefinition of previously defined class \"%s\"", name, fs_path);
823 s = fcgi_util_fs_new(p);
824 s->fs_path = fs_path;
825 s->directive = APP_CLASS_EXTERNAL;
827 err = fcgi_util_fs_set_uid_n_gid(p, s, fcgi_util_get_server_uid(cmd->server),
828 fcgi_util_get_server_gid(cmd->server));
829 if (err != NULL)
830 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
832 /* Parse directive arguments */
833 while (*arg != '\0') {
834 option = ap_getword_conf(tp, &arg);
836 if (strcasecmp(option, "-host") == 0) {
837 if ((err = get_host_n_port(p, &arg, &s->host, &s->port)))
838 return invalid_value(tp, name, fs_path, option, err);
840 else if (strcasecmp(option, "-socket") == 0) {
841 s->socket_path = ap_getword_conf(tp, &arg);
842 if (*s->socket_path == '\0')
843 return invalid_value(tp, name, fs_path, option, "\"\"");
845 else if (strcasecmp(option, "-appConnTimeout") == 0) {
846 if ((err = get_u_int(tp, &arg, &s->appConnectTimeout, 0)))
847 return invalid_value(tp, name, fs_path, option, err);
849 else if (strcasecmp(option, "-idle-timeout") == 0) {
850 if ((err = get_u_int(tp, &arg, &s->idle_timeout, 1)))
851 return invalid_value(tp, name, fs_path, option, err);
853 else if (strcasecmp(option, "-pass-header") == 0) {
854 if ((err = get_pass_header(p, &arg, &s->pass_headers)))
855 return invalid_value(tp, name, fs_path, option, err);
857 else if (strcasecmp(option, "-flush") == 0) {
858 s->flush = 1;
860 else {
861 return ap_psprintf(tp, "%s %s: invalid option: %s", name, fs_path, option);
863 } /* while */
865 /* Require one of -socket or -host, but not both */
866 if (s->socket_path != NULL && s->port != 0) {
867 return ap_psprintf(tp,
868 "%s %s: -host and -socket are mutually exclusive options",
869 name, fs_path);
871 if (s->socket_path == NULL && s->port == 0) {
872 return ap_psprintf(tp,
873 "%s %s: -socket or -host option missing", name, fs_path);
876 /* Build the appropriate sockaddr structure */
877 if (s->port != 0) {
878 err = fcgi_util_socket_make_inet_addr(p, (struct sockaddr_in **)&s->socket_addr,
879 &s->socket_addr_len, s->host, s->port);
880 if (err != NULL)
881 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
882 } else {
883 s->socket_path = fcgi_util_socket_make_path_absolute(p, s->socket_path, 0);
884 #ifndef WIN32
885 err = fcgi_util_socket_make_domain_addr(p, (struct sockaddr_un **)&s->socket_addr,
886 &s->socket_addr_len, s->socket_path);
887 if (err != NULL)
888 return ap_psprintf(tp, "%s %s: %s", name, fs_path, err);
889 #endif
892 /* Add it to the list of FastCGI servers */
893 fcgi_util_fs_add(s);
895 return NULL;
899 *----------------------------------------------------------------------
901 * fcgi_config_set_config --
903 * Implements the FastCGI FCGIConfig configuration directive.
904 * This command adds routines to control the execution of the
905 * dynamic FastCGI processes.
908 *----------------------------------------------------------------------
910 const char *fcgi_config_set_config(cmd_parms *cmd, void *dummy, const char *arg)
912 pool * const p = cmd->pool;
913 pool * const tp = cmd->temp_pool;
914 const char *err, *option;
915 const char * const name = cmd->cmd->name;
917 /* Allocate temp storage for an initial environment */
918 unsigned int envc = 0;
919 char **envp = (char **)ap_pcalloc(tp, sizeof(char *) * (MAX_INIT_ENV_VARS + 3));
921 /* Parse the directive arguments */
922 while (*arg) {
923 option = ap_getword_conf(tp, &arg);
925 if (strcasecmp(option, "-maxProcesses") == 0) {
926 if ((err = get_u_int(tp, &arg, &dynamicMaxProcs, 1)))
927 return invalid_value(tp, name, NULL, option, err);
929 else if (strcasecmp(option, "-minProcesses") == 0) {
930 if ((err = get_int(tp, &arg, &dynamicMinProcs, 0)))
931 return invalid_value(tp, name, NULL, option, err);
933 else if (strcasecmp(option, "-maxClassProcesses") == 0) {
934 if ((err = get_int(tp, &arg, &dynamicMaxClassProcs, 1)))
935 return invalid_value(tp, name, NULL, option, err);
937 else if (strcasecmp(option, "-killInterval") == 0) {
938 if ((err = get_u_int(tp, &arg, &dynamicKillInterval, 1)))
939 return invalid_value(tp, name, NULL, option, err);
941 else if (strcasecmp(option, "-updateInterval") == 0) {
942 if ((err = get_u_int(tp, &arg, &dynamicUpdateInterval, 1)))
943 return invalid_value(tp, name, NULL, option, err);
945 else if (strcasecmp(option, "-gainValue") == 0) {
946 if ((err = get_float(tp, &arg, &dynamicGain, 0.0, 1.0)))
947 return invalid_value(tp, name, NULL, option, err);
949 else if ((strcasecmp(option, "-singleThreshold") == 0)
950 || (strcasecmp(option, "-singleThreshhold") == 0))
952 if ((err = get_int(tp, &arg, &dynamicThreshold1, 0)))
953 return invalid_value(tp, name, NULL, option, err);
955 else if ((strcasecmp(option, "-multiThreshold") == 0)
956 || (strcasecmp(option, "-multiThreshhold") == 0))
958 if ((err = get_int(tp, &arg, &dynamicThresholdN, 0)))
959 return invalid_value(tp, name, NULL, option, err);
961 else if (strcasecmp(option, "-startDelay") == 0) {
962 if ((err = get_u_int(tp, &arg, &dynamicPleaseStartDelay, 1)))
963 return invalid_value(tp, name, NULL, option, err);
965 else if (strcasecmp(option, "-initial-env") == 0) {
966 if ((err = get_env_var(p, &arg, envp, &envc)))
967 return invalid_value(tp, name, NULL, option, err);
969 else if (strcasecmp(option, "-pass-header") == 0) {
970 if ((err = get_pass_header(p, &arg, &dynamic_pass_headers)))
971 return invalid_value(tp, name, NULL, option, err);
973 else if (strcasecmp(option, "-appConnTimeout") == 0) {
974 if ((err = get_u_int(tp, &arg, &dynamicAppConnectTimeout, 0)))
975 return invalid_value(tp, name, NULL, option, err);
977 else if (strcasecmp(option, "-idle-timeout") == 0) {
978 if ((err = get_u_int(tp, &arg, &dynamic_idle_timeout, 1)))
979 return invalid_value(tp, name, NULL, option, err);
981 else if (strcasecmp(option, "-listen-queue-depth") == 0) {
982 if ((err = get_u_int(tp, &arg, &dynamicListenQueueDepth, 1)))
983 return invalid_value(tp, name, NULL, option, err);
985 else if (strcasecmp(option, "-restart-delay") == 0) {
986 if ((err = get_u_int(tp, &arg, &dynamicRestartDelay, 0)))
987 return invalid_value(tp, name, NULL, option, err);
989 else if (strcasecmp(option, "-init-start-delay") == 0) {
990 if ((err = get_u_int(tp, &arg, &dynamicInitStartDelay, 0)))
991 return invalid_value(tp, name, NULL, option, err);
993 else if (strcasecmp(option, "-processSlack") == 0) {
994 if ((err = get_u_int(tp, &arg, &dynamicProcessSlack, 1)))
995 return invalid_value(tp, name, NULL, option, err);
997 else if (strcasecmp(option, "-restart") == 0) {
998 dynamicAutoRestart = 1;
1000 else if (strcasecmp(option, "-autoUpdate") == 0) {
1001 dynamicAutoUpdate = 1;
1003 else if (strcasecmp(option, "-flush") == 0) {
1004 dynamicFlush = TRUE;
1006 else {
1007 return ap_psprintf(tp, "%s: invalid option: %s", name, option);
1009 } /* while */
1011 if (dynamicProcessSlack >= dynamicMaxProcs + 1) {
1012 /* the kill policy would work unexpectedly */
1013 return ap_psprintf(tp,
1014 "%s: processSlack (%u) must be less than maxProcesses (%u) + 1",
1015 name, dynamicProcessSlack, dynamicMaxProcs);
1018 /* Move env array to a surviving pool, leave 2 extra slots for
1019 * WIN32 _FCGI_MUTEX_ and _FCGI_SHUTDOWN_EVENT_ */
1020 dynamicEnvp = (char **)ap_pcalloc(p, sizeof(char *) * (envc + 4));
1021 memcpy(dynamicEnvp, envp, sizeof(char *) * envc);
1023 return NULL;
1026 void *fcgi_config_create_dir_config(pool *p, char *dummy)
1028 fcgi_dir_config *dir_config = ap_pcalloc(p, sizeof(fcgi_dir_config));
1030 dir_config->authenticator_options = FCGI_AUTHORITATIVE;
1031 dir_config->authorizer_options = FCGI_AUTHORITATIVE;
1032 dir_config->access_checker_options = FCGI_AUTHORITATIVE;
1034 return dir_config;
1038 const char *fcgi_config_new_auth_server(cmd_parms * cmd,
1039 void * dircfg, const char *fs_path, const char * compat)
1041 fcgi_dir_config * dir_config = (fcgi_dir_config *) dircfg;
1042 pool * const tp = cmd->temp_pool;
1043 char * auth_server;
1045 #ifdef APACHE2
1046 if (apr_filepath_merge(&auth_server, "", fs_path, 0, cmd->pool))
1047 return ap_psprintf(tp, "%s %s: invalid filepath", cmd->cmd->name, fs_path);
1048 #else
1049 auth_server = (char *) ap_os_canonical_filename(cmd->pool, fs_path);
1050 #endif
1052 auth_server = ap_server_root_relative(cmd->pool, auth_server);
1054 /* Make sure its already configured or at least a candidate for dynamic */
1055 if (fcgi_util_fs_get_by_id(auth_server, fcgi_util_get_server_uid(cmd->server),
1056 fcgi_util_get_server_gid(cmd->server)) == NULL)
1058 const char *err = fcgi_util_fs_is_path_ok(tp, auth_server, NULL);
1059 if (err)
1060 return ap_psprintf(tp, "%s: \"%s\" %s", cmd->cmd->name, auth_server, err);
1063 if (compat && strcasecmp(compat, "-compat"))
1064 return ap_psprintf(cmd->temp_pool, "%s: unknown option: \"%s\"", cmd->cmd->name, compat);
1066 switch((int)cmd->info) {
1067 case FCGI_AUTH_TYPE_AUTHENTICATOR:
1068 dir_config->authenticator = auth_server;
1069 dir_config->authenticator_options |= (compat) ? FCGI_COMPAT : 0;
1070 break;
1071 case FCGI_AUTH_TYPE_AUTHORIZER:
1072 dir_config->authorizer = auth_server;
1073 dir_config->authorizer_options |= (compat) ? FCGI_COMPAT : 0;
1074 break;
1075 case FCGI_AUTH_TYPE_ACCESS_CHECKER:
1076 dir_config->access_checker = auth_server;
1077 dir_config->access_checker_options |= (compat) ? FCGI_COMPAT : 0;
1078 break;
1081 return NULL;
1084 const char *fcgi_config_set_authoritative_slot(cmd_parms * cmd,
1085 void * dir_config, int arg)
1087 int offset = (int)(long)cmd->info;
1089 if (arg)
1090 *((u_char *)dir_config + offset) |= FCGI_AUTHORITATIVE;
1091 else
1092 *((u_char *)dir_config + offset) &= ~FCGI_AUTHORITATIVE;
1094 return NULL;