1 /*
2  * mod_mono.c
3  *
4  * Authors:
5  * 	Daniel Lopez Ridruejo
6  * 	Gonzalo Paniagua Javier
7  *      Marek Habersack
8  *
9  * Copyright (c) 2002 Daniel Lopez Ridruejo
10  *           (c) 2002-2009 Novell, Inc.
11  *
12  * Licensed under the Apache License, Version 2.0 (the "License");
13  * you may not use this file except in compliance with the License.
14  * You may obtain a copy of the License at
15  *
16  *     http://www.apache.org/licenses/LICENSE-2.0
17  *
18  * Unless required by applicable law or agreed to in writing, software
19  * distributed under the License is distributed on an "AS IS" BASIS,
20  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
21  * See the License for the specific language governing permissions and
22  * limitations under the License.
23  */
24 #ifdef HAVE_CONFIG_H
25 #include "mod_mono_config.h"
26 #endif
27 
28 /* uncomment this to get tons of messages in the log */
29 /* or use --enable-debug with configure */
30 /* #define DEBUG */
31 #ifndef DEBUG_LEVEL
32 #define DEBUG_LEVEL 0
33 #endif
34 
35 #ifdef DEBUG
36 #define STDOUT_NULL_DEFAULT 0
37 #else
38 #define STDOUT_NULL_DEFAULT 1
39 #endif
40 
41 #include <stdlib.h>
42 #include "mod_mono.h"
43 #include "mono-io-portability.h"
44 
45 DEFINE_MODULE (mono_module);
46 
47 /* Configuration pool. Cleared on restart. */
48 static apr_pool_t *pconf;
49 
50 typedef struct per_dir_config {
51 	char *location;
52 	char *alias;
53 } per_dir_config;
54 
55 enum {
56 	FORK_NONE,
57 	FORK_INPROCESS,
58 	FORK_ATTEMPTED,
59 	FORK_FAILED,
60 	FORK_SUCCEEDED
61 };
62 
63 typedef enum {
64 	AUTORESTART_MODE_INVALID,
65 	AUTORESTART_MODE_NONE,
66 	AUTORESTART_MODE_TIME,
67 	AUTORESTART_MODE_REQUESTS,
68 } auto_restart_mode;
69 
70 #define URI_LIST_ITEM_SIZE 256
71 #define ACTIVE_URI_LIST_ITEM_COUNT 100
72 #define WAITING_URI_LIST_ITEM_COUNT 100
73 
74 typedef struct {
75 	int32_t id;
76 	time_t start_time;
77 	char uri[URI_LIST_ITEM_SIZE];
78 } uri_item;
79 
80 typedef struct {
81 	uint32_t requests_counter;
82 	uint32_t handled_requests;
83 	time_t start_time;
84 	char restart_issued;
85 	char starting;
86 	int active_requests;
87 	int waiting_requests;
88 	int accepting_requests;
89 	uri_item active_uri_list[ACTIVE_URI_LIST_ITEM_COUNT];
90 	uri_item waiting_uri_list[WAITING_URI_LIST_ITEM_COUNT];
91 } dashboard_data;
92 
93 #define INITIAL_DATA_MAX_ALLOCA_SIZE 8192
94 typedef struct {
95 	size_t      method_len;
96 	size_t      server_hostname_len;
97 	size_t      uri_len;
98 	size_t      args_len;
99 	size_t      protocol_len;
100 	size_t      local_ip_len;
101 	size_t      remote_ip_len;
102 	const char *remote_name;
103 	size_t      remote_name_len;
104 	size_t      filename_len;
105 } initial_data_info;
106 
107 typedef struct xsp_data {
108 	char is_default;
109 	char *alias;
110 	char *filename; /* Unix socket path */
111 	char *umask_value;
112 	char *run_xsp;
113 	char *executable_path;
114 	char *path;
115 	char *server_path;
116 	char *target_framework;
117 	char *applications;
118 	char *wapidir;
119 	char *document_root;
120 	char *appconfig_file;
121 	char *appconfig_dir;
122 	char *listen_port;
123 	char *listen_address;
124 	char *listen_backlog;
125 	char *minthreads;
126 	char *max_cpu_time;
127 	char *max_memory;
128 	char *debug;
129 	char *env_vars;
130 	char *iomap;
131 	char *hidden;
132 	char status; /* One of the FORK_* in the enum above.
133 		      * Don't care if run_xsp is "false" */
134 	char is_virtual; /* is the server virtual? */
135 	char *start_attempts;
136 	char *start_wait_time;
137 	char *max_active_requests;
138 	char *max_waiting_requests;
139 
140 	/* auto-restart stuff */
141 	auto_restart_mode restart_mode;
142 	uint32_t restart_requests;
143 	uint32_t restart_time;
144 
145 	/* other settings */
146 	unsigned char no_flush;
147 	int portability_level;
148 
149 	/* The dashboard */
150 	apr_shm_t *dashboard_shm;
151 	dashboard_data *dashboard;
152 	apr_global_mutex_t *dashboard_mutex;
153 	char dashboard_mutex_initialized_in_child;
154 	char *dashboard_file;
155 	char *dashboard_lock_file;
156 } xsp_data;
157 
158 typedef struct {
159 	int nservers;
160 	xsp_data *servers;
161 	char auto_app;
162 	char auto_app_set;
163 } module_cfg;
164 
165 typedef struct {
166 	uint32_t client_block_buffer_size;
167 	char *client_block_buffer;
168 } request_data;
169 
170 typedef struct {
171 	char *name;
172 	apr_lockmech_e sym;
173 	char available;
174 } lock_mechanism;
175 
176 #define LOCK_MECH(name) {#name, APR_LOCK_ ## name, APR_HAS_ ## name ## _SERIALIZE}
177 
178 static lock_mechanism lockMechanisms [] = {
179 	LOCK_MECH (FCNTL),
180 	LOCK_MECH (FLOCK),
181 	LOCK_MECH (SYSVSEM),
182 	LOCK_MECH (PROC_PTHREAD),
183 	LOCK_MECH (POSIXSEM),
184 	{"DEFAULT", APR_LOCK_DEFAULT, 1},
185 	{NULL, 0, 0}
186 };
187 
188 static int send_table (apr_pool_t *pool, apr_table_t *table, apr_socket_t *sock);
189 static void start_xsp (module_cfg *config, int is_restart, char *alias);
190 static apr_status_t terminate_xsp2 (void *data, char *alias, int for_restart, int lock_held);
191 
192 #define IS_MASTER(__conf__) (!strcmp (GLOBAL_SERVER_NAME, (__conf__)->alias))
193 
194 static long
string_to_long(char * str,char * what,long def)195 string_to_long (char *str, char *what, long def)
196 {
197 	long retval;
198 	char *endptr;
199 
200 	if (!str || !*str)
201 		return def;
202 
203 	retval = strtol (str, &endptr, 0);
204 	if ((retval == LONG_MAX && errno == ERANGE) || (str == endptr) || *endptr) {
205 		ap_log_error (APLOG_MARK, APLOG_WARNING, STATUS_AND_SERVER,
206 			      "%s: conversion to integer failed - returning the default value %lu.",
207 			      what ? what : "Configuration", def);
208 		return def;
209 	}
210 
211 	return retval;
212 }
213 
214 static apr_lockmech_e
get_apr_locking_mechanism()215 get_apr_locking_mechanism ()
216 {
217 	char *name = getenv ("MOD_MONO_LOCKING_MECHANISM");
218 	int i = 0;
219 
220 	DEBUG_PRINT (2, "Requested locking mechanism name: %s", name);
221 	if (!name)
222 		return APR_LOCK_DEFAULT;
223 	while (lockMechanisms [i].name) {
224 		if (!strncasecmp (name, lockMechanisms [i].name, strlen (lockMechanisms [i].name))) {
225 			if (lockMechanisms [i].available) {
226 				DEBUG_PRINT (1, "Using configured lock mechanism: %s", lockMechanisms [i].name);
227 				return lockMechanisms [i].sym;
228 			} else {
229 				ap_log_error (APLOG_MARK, APLOG_ALERT, STATUS_AND_SERVER,
230 					      "Locking mechanism '%s' is unavailable for this platform. Using the default one.",
231 					      lockMechanisms [i].name);
232 				return APR_LOCK_DEFAULT;
233 			}
234 		}
235 		i++;
236 	}
237 
238 	ap_log_error (APLOG_MARK, APLOG_ALERT, STATUS_AND_SERVER,
239 		      "No locking mechanism matching '%s' has been found for this platform. Using the default one.",
240 		      name);
241 	return APR_LOCK_DEFAULT;
242 }
243 
244 /* */
245 static int
search_for_alias(const char * alias,module_cfg * config)246 search_for_alias (const char *alias, module_cfg *config)
247 {
248 	/* 'alias' may be NULL to search for the default XSP */
249 	int i;
250 	xsp_data *xsp;
251 
252 	DEBUG_PRINT (0, "Searching for alias %s", alias);
253 	DEBUG_PRINT (0, "%u servers available", config->nservers);
254 	for (i = 0; i < config->nservers; i++) {
255 		xsp = &config->servers [i];
256 		DEBUG_PRINT (0, "Server at index %u is '%s'", i, xsp->alias);
257 		if ((alias == NULL || !strcmp (alias, "default")) && xsp->is_default)
258 			return i;
259 
260 		if (alias != NULL && !strcmp (alias, xsp->alias))
261 			return i;
262 	}
263 
264 	return -1;
265 }
266 
267 static const char *
set_alias(cmd_parms * cmd,void * mconfig,const char * alias)268 set_alias (cmd_parms *cmd, void *mconfig, const char *alias)
269 {
270 	per_dir_config *config = mconfig;
271 	module_cfg *sconfig;
272 
273 	sconfig = ap_get_module_config (cmd->server->module_config, &mono_module);
274 	config->alias = (char *) alias;
275 	if (search_for_alias (alias, sconfig) == -1) {
276 		char *err = apr_pstrcat (cmd->pool, "Server alias '", alias, ", not found.", NULL);
277 		return err;
278 	}
279 
280 	return NULL;
281 }
282 
283 static const char *
set_auto_application(cmd_parms * cmd,void * mconfig,const char * value)284 set_auto_application (cmd_parms *cmd, void *mconfig, const char *value)
285 {
286 	module_cfg *sconfig;
287 
288 	sconfig = ap_get_module_config (cmd->server->module_config, &mono_module);
289 	if (!strcasecmp (value, "disabled")) {
290 		if (sconfig->auto_app_set && sconfig->auto_app != FALSE)
291 			return apr_pstrdup (cmd->pool, "Conflicting values for MonoAutoApplication.");
292 
293 		sconfig->auto_app = FALSE;
294 		/* TODO: Copiar de 'XXGLOBAL' a 'default' */
295 	} else if (!strcasecmp (value, "enabled")) {
296 		if (sconfig->auto_app_set && sconfig->auto_app != TRUE)
297 			return apr_pstrdup (cmd->pool, "Conflicting values for MonoAutoApplication.");
298 
299 		sconfig->auto_app = TRUE;
300 	} else {
301 		return apr_pstrdup (cmd->pool, "Invalid value. Must be 'enabled' or 'disabled'");
302 	}
303 
304 	sconfig->auto_app_set = TRUE;
305 	return NULL;
306 }
307 
308 
309 static unsigned long
parse_restart_time(const char * t)310 parse_restart_time (const char *t)
311 {
312 	uint32_t time_spec [4] = {0, 0, 0, 0};
313 	int parsed;
314 
315 	parsed = sscanf (t, "%u:%u:%u:%u",
316 			 &time_spec [0],
317 			 &time_spec [1],
318 			 &time_spec [2],
319 			 &time_spec [3]);
320 	switch (parsed) {
321 		case 1:
322 			DEBUG_PRINT (1, "Auto-restart will happen after: %u days (%us)",
323 				     time_spec [0],
324 				     time_spec [0] * 86400);
325 			return time_spec [0] * 86400;
326 		case 2:
327 			DEBUG_PRINT (1, "Auto-restart will happen after: %u days, %u hours (%us)",
328 				     time_spec [0], time_spec [1],
329 				     (time_spec [0] * 86400) + (time_spec [1] * 3600));
330 			return (time_spec [0] * 86400) + (time_spec [1] * 3600);
331 		case 3:
332 			DEBUG_PRINT (1, "Auto-restart will happen after: %u days, %u hours, %u minutes (%us)",
333 				     time_spec [0], time_spec [1], time_spec [2],
334 				     (time_spec [0] * 86400) + (time_spec [1] * 3600) + (time_spec [2] * 60));
335 			return (time_spec [0] * 86400) + (time_spec [1] * 3600) + (time_spec [2] * 60);
336 		case 4:
337 			DEBUG_PRINT (1, "Auto-restart will happen: %u days, %u hours, %u minutes, %u seconds (%us)",
338 				     time_spec [0], time_spec [1], time_spec [2], time_spec [3],
339 				     (time_spec [0] * 86400) + (time_spec [1] * 3600) + (time_spec [2] * 60) + time_spec [3]);
340 			return (time_spec [0] * 86400) + (time_spec [1] * 3600) + (time_spec [2] * 60) + time_spec [3];
341 		default:
342 			return 0;
343 	}
344 }
345 
346 static void
get_restart_mode(xsp_data * xsp,const char * value)347 get_restart_mode (xsp_data *xsp, const char *value)
348 {
349 	unsigned long val = 0;
350 	if (xsp == NULL)
351 		return;
352 
353 	switch (xsp->restart_mode) {
354 		case AUTORESTART_MODE_REQUESTS:
355 			ap_log_error (APLOG_MARK, APLOG_NOTICE, STATUS_AND_SERVER,
356 				      "Backend '%s' auto-restart mode %s enabled",
357 				      xsp->alias ? xsp->alias : "default", "REQUESTS");
358 			if (value)
359 				val = (unsigned long)strtol (value, NULL, 0);
360 			if (val == 0 || val > UINT_MAX || (val == ULONG_MAX && errno == ERANGE))
361 				val = DEFAULT_RESTART_REQUESTS;
362 			ap_log_error (APLOG_MARK, APLOG_NOTICE, STATUS_AND_SERVER,
363 				      "Auto-restart will happen after %u requests made to the backend",
364 				      (uint32_t)val);
365 			xsp->restart_requests = (uint32_t)val;
366 			break;
367 
368 		case AUTORESTART_MODE_TIME:
369 			ap_log_error (APLOG_MARK, APLOG_NOTICE, STATUS_AND_SERVER,
370 				      "Backend '%s' auto-restart mode %s enabled",
371 				      xsp->alias ? xsp->alias : "default", "TIME");
372 			if (value)
373 				val = parse_restart_time (value);
374 			if (val == 0 || val > UINT_MAX || (val == ULONG_MAX && errno == ERANGE))
375 				val = DEFAULT_RESTART_TIME;
376 			ap_log_error (APLOG_MARK, APLOG_NOTICE, STATUS_AND_SERVER,
377 				      "Auto-restart will happen after %u seconds of the backend uptime",
378 				      (uint32_t)val);
379 			xsp->restart_time = (uint32_t)val;
380 			break;
381 
382 		default:
383 			break;
384 	}
385 }
386 
387 inline static uid_t
apache_get_userid()388 apache_get_userid ()
389 {
390 #ifdef HAVE_UNIXD
391 #if defined(APACHE24)
392   return ap_unixd_config.user_id;
393 #else
394   return unixd_config.user_id;
395 #endif
396 #else
397 	return ap_user_id;
398 #endif
399 }
400 
401 inline static gid_t
apache_get_groupid()402 apache_get_groupid ()
403 {
404 #ifdef HAVE_UNIXD
405 #if defined(APACHE24)
406 	return ap_unixd_config.group_id;
407 #else
408   return unixd_config.group_id;
409 #endif
410 #else
411 	return ap_group_id;
412 #endif
413 }
414 
415 inline static const char *
apache_get_username()416 apache_get_username ()
417 {
418 #ifdef HAVE_UNIXD
419 #if defined(APACHE24)
420 	return ap_unixd_config.user_name;
421 #else
422 	return unixd_config.user_name;
423 #endif
424 #else
425 	return ap_user_name;
426 #endif
427 }
428 
429 inline static void
initialize_uri_list(uri_item * list,int nitems)430 initialize_uri_list (uri_item* list, int nitems)
431 {
432 	int i;
433 	for (i = 0; i < nitems; i++) {
434 		memset (&list [i], 0, sizeof (uri_item));
435 		list [i].id = -1;
436 		list [i].start_time = -1;
437 	}
438 }
439 
440 static void
ensure_dashboard_initialized(module_cfg * config,xsp_data * xsp,apr_pool_t * p)441 ensure_dashboard_initialized (module_cfg *config, xsp_data *xsp, apr_pool_t *p)
442 {
443 	apr_status_t rv;
444 	mode_t old_umask;
445 #if defined (APR_HAS_USER)
446 	apr_uid_t cur_uid;
447 	apr_gid_t cur_gid;
448 	int switch_back_to_root = 0;
449 #endif
450 	int is_global;
451 
452 	if (IS_MASTER (xsp))
453 		is_global = 1;
454 	else
455 		is_global = 0;
456 
457 	if (!xsp->dashboard_mutex || !xsp->dashboard_shm)
458 		xsp->dashboard = NULL;
459 
460 #if defined (APR_HAS_USER)
461 	if (apache_get_userid () == -1 || apache_get_groupid () == -1) {
462 		ap_log_error (APLOG_MARK, APLOG_CRIT, STATUS_AND_SERVER,
463 			      "The unix daemon module not initialized yet. Please make sure that "
464 			      "your mod_mono module is loaded after the User/Group directives have "
465 			      "been parsed. Not initializing the dashboard.");
466 		return;
467 	}
468 
469 	if (apr_uid_current (&cur_uid, &cur_gid, p) == APR_SUCCESS && cur_uid == 0) {
470 		DEBUG_PRINT (2, "Temporarily switching to target uid/gid");
471 		switch_back_to_root = 1;
472 		if (setegid (apache_get_groupid ()) == -1)
473 			ap_log_error (APLOG_MARK, APLOG_ALERT, STATUS_AND_SERVER,
474 				      "setegid: unable to set effective group id to %u. %s",
475 				      (unsigned)apache_get_groupid (), strerror (errno));
476 
477 		if (seteuid (apache_get_userid ()) == -1)
478 			ap_log_error (APLOG_MARK, APLOG_ALERT, STATUS_AND_SERVER,
479 				      "seteuid: unable to set effective user id to %u. %s",
480 				      (unsigned)apache_get_userid (), strerror (errno));
481 	}
482 #endif
483 
484 	if (!xsp->dashboard_mutex) {
485 		DEBUG_PRINT (1, "creating dashboard mutex = %s", xsp->dashboard_lock_file);
486 
487 		if (unlink (xsp->dashboard_lock_file) == -1 && errno != ENOENT) {
488 			ap_log_error (APLOG_MARK, APLOG_CRIT, STATUS_AND_SERVER,
489 				      "Failed to remove dashboard mutex file '%s'; will attempt to continue. %s",
490 				      xsp->dashboard_lock_file, strerror (errno));
491 		}
492 		rv = apr_global_mutex_create (&xsp->dashboard_mutex, xsp->dashboard_lock_file,
493 					      get_apr_locking_mechanism (), p);
494 		if (rv != APR_SUCCESS) {
495 			ap_log_error (APLOG_MARK, APLOG_CRIT, STATCODE_AND_SERVER (rv),
496 				      "Failed to create mutex '%s'", xsp->dashboard_lock_file);
497 			goto restore_creds;
498 		}
499 
500 #if defined (AP_NEED_SET_MUTEX_PERMS) && defined (HAVE_UNIXD)
501 		DEBUG_PRINT (1, "Setting mutex permissions for %s", xsp->dashboard_lock_file);
502 #if defined(APACHE24)
503 		rv = ap_unixd_set_global_mutex_perms (xsp->dashboard_mutex);
504 #else
505 		rv = unixd_set_global_mutex_perms (xsp->dashboard_mutex);
506 #endif
507     if (rv != APR_SUCCESS) {
508 			ap_log_error (APLOG_MARK, APLOG_CRIT, STATCODE_AND_SERVER (rv),
509 				      "Failed to set mutex permissions for %s",
510 				      xsp->dashboard_lock_file);
511 			goto restore_creds;
512 		}
513 #endif
514 	}
515 
516 	if (!xsp->dashboard_shm)
517 		rv = apr_shm_attach (&xsp->dashboard_shm, xsp->dashboard_file, p);
518 	else
519 		rv = APR_SUCCESS;
520 
521 	if (rv == APR_SUCCESS) {
522 		DEBUG_PRINT (1, "Attaching to dashboard (file '%s')", xsp->dashboard_file);
523 		xsp->dashboard = apr_shm_baseaddr_get (xsp->dashboard_shm);
524 	} else {
525 		DEBUG_PRINT (1, "removing dashboard file '%s'", xsp->dashboard_file);
526 		if (unlink (xsp->dashboard_file) == -1 && errno != ENOENT) {
527 			if (!is_global)
528 				ap_log_error (APLOG_MARK, APLOG_CRIT, STATCODE_AND_SERVER (rv),
529 					      "Failed to attach to existing dashboard, and removing dashboard file '%s' failed (%s). Further action impossible.",
530 					       xsp->dashboard_file, strerror (errno));
531 			xsp->dashboard = NULL;
532 			goto restore_creds;
533 		}
534 
535 		DEBUG_PRINT (1, "creating dashboard '%s'", xsp->dashboard_file);
536 		old_umask = umask (0077);
537 		rv = apr_shm_create (&xsp->dashboard_shm, sizeof (dashboard_data), xsp->dashboard_file, p);
538 		umask (old_umask);
539 		if (rv != APR_SUCCESS) {
540 			xsp->dashboard = NULL;
541 			ap_log_error (APLOG_MARK, APLOG_CRIT, STATCODE_AND_SERVER (rv),
542 				      "Failed to create shared memory segment for backend '%s' at '%s'.",
543 				      xsp->alias, xsp->dashboard_file);
544 		} else {
545 			rv = apr_shm_attach (&xsp->dashboard_shm, xsp->dashboard_file, p);
546 			if (rv != APR_SUCCESS) {
547 				xsp->dashboard = NULL;
548 				ap_log_error (APLOG_MARK, APLOG_CRIT, STATCODE_AND_SERVER (rv),
549 					      "Failed to attach to the dashboard '%s'",
550 					      xsp->dashboard_file);
551 				goto restore_creds;
552 			}
553 
554 			xsp->dashboard = apr_shm_baseaddr_get (xsp->dashboard_shm);
555 			xsp->dashboard->start_time = time (NULL);
556 			xsp->dashboard->requests_counter = 0;
557 			xsp->dashboard->handled_requests = 0;
558 			xsp->dashboard->restart_issued = 0;
559 			xsp->dashboard->active_requests = 0;
560 			xsp->dashboard->waiting_requests = 0;
561 			xsp->dashboard->starting = 0;
562 			xsp->dashboard->accepting_requests = 1;
563 
564 			initialize_uri_list (xsp->dashboard->active_uri_list, ACTIVE_URI_LIST_ITEM_COUNT);
565 			initialize_uri_list (xsp->dashboard->waiting_uri_list, WAITING_URI_LIST_ITEM_COUNT);
566 		}
567 	}
568 
569   restore_creds:
570 #if defined (APR_HAS_USER) && !defined (WIN32)
571 	if (switch_back_to_root) {
572 		DEBUG_PRINT (2, "Switching back to root");
573 		if (seteuid (0) == -1)
574 			ap_log_error (APLOG_MARK, APLOG_ALERT, STATUS_AND_SERVER,
575 				      "seteuid: cannot switch the effective user id back to root. %s",
576 				      strerror (errno));
577 		if (setegid (0) == -1)
578 			ap_log_error (APLOG_MARK, APLOG_ALERT, STATUS_AND_SERVER,
579 				      "setegid: cannot switch the effective group id back to root. %s",
580 				      strerror (errno));
581 	}
582 #endif
583 }
584 
585 static int
add_xsp_server(apr_pool_t * pool,const char * alias,module_cfg * config,int is_default,int is_virtual)586 add_xsp_server (apr_pool_t *pool, const char *alias, module_cfg *config, int is_default, int is_virtual)
587 {
588 	xsp_data *server;
589 	xsp_data *servers;
590 	int nservers;
591 	int i;
592 	char num [8];
593 
594 	DEBUG_PRINT (0, "add_xsp_server for alias %s", alias);
595 	i = search_for_alias (alias, config);
596 	DEBUG_PRINT (0, "index for this server is %d", i);
597 	if (i >= 0)
598 		return i;
599 
600 	DEBUG_PRINT (0, "alias not found, continuing");
601 	server = apr_pcalloc (pool, sizeof (xsp_data));
602 
603 	server->is_default = is_default;
604 	server->alias = apr_pstrdup (pool, alias);
605 	server->filename = NULL;
606 	server->umask_value = NULL;
607 	server->run_xsp = "True";
608 	/* (Obsolete) server->executable_path = EXECUTABLE_PATH; */
609 	server->path = NULL;
610 	server->server_path = NULL;
611 	server->target_framework = NULL;
612 	server->applications = NULL;
613 	server->wapidir = WAPIDIR;
614 	server->document_root = DOCUMENT_ROOT;
615 	server->appconfig_file = APPCONFIG_FILE;
616 	if (is_default)
617 		server->appconfig_dir = APPCONFIG_DIR;
618 
619 	server->listen_port = NULL;
620 	server->listen_address = NULL;
621 	server->listen_backlog = NULL;
622 	server->minthreads = NULL;
623 	server->max_cpu_time = NULL;
624 	server->max_memory = NULL;
625 	server->debug = NULL;
626 	server->env_vars = NULL;
627 	server->iomap = NULL;
628 	server->portability_level = PORTABILITY_UNKNOWN;
629 	server->status = FORK_NONE;
630 	server->is_virtual = is_virtual;
631 	server->start_attempts = NULL;
632 	server->start_wait_time = NULL;
633 	server->no_flush = 1;
634 	server->max_active_requests = NULL;
635 	server->max_waiting_requests = NULL;
636 
637 	apr_snprintf (num, sizeof (num), "%u", (unsigned)config->nservers + 1);
638 	server->dashboard_file = apr_pstrcat (pool,
639 					      DASHBOARD_FILE,
640 					      "_",
641 					      alias == NULL ? "default" : alias,
642 					      "_",
643 					      num,
644 					      NULL);
645 	server->dashboard_lock_file = apr_pstrcat (pool, server->dashboard_file, ".lock", NULL);
646 	server->dashboard_shm = NULL;
647 	server->dashboard = NULL;
648 	server->dashboard_mutex = NULL;
649 	server->dashboard_mutex_initialized_in_child = 0;
650 	server->restart_mode = AUTORESTART_MODE_INVALID;
651 	server->restart_requests = 0;
652 	server->restart_time = 0;
653 
654 	ensure_dashboard_initialized (config, server, pool);
655 	/* This is needed, because we're being called from the main process and dashboard must NOT */
656 	/* be set to any value when start_xsp is called from child init or handler init. */
657 	server->dashboard = NULL;
658 
659 	nservers = config->nservers + 1;
660 	servers = config->servers;
661 	config->servers = apr_pcalloc (pool, sizeof (xsp_data) * nservers);
662 	if (config->nservers > 0)
663 		memcpy (config->servers, servers, sizeof (xsp_data) * config->nservers);
664 
665 	memcpy (&config->servers [config->nservers], server, sizeof (xsp_data));
666 	config->nservers = nservers;
667 
668 	return config->nservers - 1;
669 }
670 
671 static int
handle_restart_config(char * ptr,unsigned long offset,const char * value)672 handle_restart_config (char *ptr, unsigned long offset, const char *value)
673 {
674 	xsp_data *xsp = (xsp_data*)ptr;
675 
676 	if (offset == APR_OFFSETOF (xsp_data, restart_mode)) {
677 		if (!strncasecmp (value, "REQUESTS", 8)) {
678 			xsp->restart_mode = AUTORESTART_MODE_REQUESTS;
679 			xsp->restart_requests = DEFAULT_RESTART_REQUESTS;
680 		} else if (!strncasecmp (value, "TIME", 4)) {
681 			xsp->restart_mode = AUTORESTART_MODE_TIME;
682 			xsp->restart_time = DEFAULT_RESTART_TIME;
683 		} else if (!strncasecmp (value, "NONE", 4))
684 			xsp->restart_mode = AUTORESTART_MODE_NONE;
685 		else
686 			xsp->restart_mode = AUTORESTART_MODE_INVALID;
687 		return 1;
688 	}
689 
690 	if ((offset == APR_OFFSETOF (xsp_data, restart_requests)) ||
691 	    (offset == APR_OFFSETOF (xsp_data, restart_time))) {
692 		get_restart_mode (xsp, value);
693 		return 1;
694 	}
695 
696 	return 0;
697 }
698 
699 static const char *
store_config_xsp(cmd_parms * cmd,void * notused,const char * first,const char * second)700 store_config_xsp (cmd_parms *cmd, void *notused, const char *first, const char *second)
701 {
702 	const char *alias;
703 	const char *value;
704 	char *prev_value = NULL;
705 	char *new_value;
706 	int idx;
707 	module_cfg *config;
708 	char *ptr;
709 	unsigned long offset;
710 	int is_default;
711 
712 	offset = (unsigned long) cmd->info;
713 	DEBUG_PRINT (0, "store_config %lu '%s' '%s'", offset, first, second);
714 	config = ap_get_module_config (cmd->server->module_config, &mono_module);
715 	if (second == NULL) {
716 		if (config->auto_app) {
717 			idx = search_for_alias (GLOBAL_SERVER_NAME, config);
718 			value = first;
719 			ptr = (char *) &config->servers [idx];
720 
721 			/* special handling for restart fields */
722 			if (handle_restart_config (ptr, offset, value))
723 				return NULL;
724 			ptr += offset;
725 
726 			/* MonoApplications/AddMonoApplications are accumulative */
727 			if (offset == APR_OFFSETOF (xsp_data, applications))
728 				prev_value = *((char **) ptr);
729 
730 			if (prev_value != NULL) {
731 				new_value = apr_pstrcat (cmd->pool, prev_value, ",", value, NULL);
732 			} else {
733 				new_value = apr_pstrdup (cmd->pool, value);
734 			}
735 
736 			*((char **) ptr) = new_value;
737 			return NULL;
738 		}
739 		alias = "default";
740 		if (cmd->server->is_virtual && cmd->server->server_hostname)
741 			alias = cmd->server->server_hostname;
742 
743 		value = first;
744 		is_default = 1;
745 	} else {
746 		if (!strcmp (first, GLOBAL_SERVER_NAME))
747 			return apr_pstrdup (cmd->pool, "XXGLOBAL is a reserved application identifier.");
748 		alias = first;
749 		value = second;
750 		is_default = (!strcmp (alias, "default"));
751 	}
752 
753 	/* Disable autoapp if there's any other application. MonoDebug is excluded. */
754 	if (!config->auto_app_set)
755 		config->auto_app = FALSE;
756 
757 	idx = search_for_alias (alias, config);
758 	if (idx == -1) {
759 		DEBUG_PRINT (0, "Calling add_xsp from %s", __PRETTY_FUNCTION__);
760 		idx = add_xsp_server (cmd->pool, alias, config, is_default, cmd->server->is_virtual);
761 	}
762 	ptr = (char *) &config->servers [idx];
763 
764 	/* special handling for restart fields */
765 	if (handle_restart_config (ptr, offset, value))
766 		return NULL;
767 
768 	ptr += offset;
769 	/* MonoApplications/AddMonoApplications are accumulative */
770 	if (offset == APR_OFFSETOF (xsp_data, applications))
771 		prev_value = *((char **) ptr);
772 
773 	if (prev_value != NULL) {
774 		new_value = apr_pstrcat (cmd->pool, prev_value, ",", value, NULL);
775 	} else {
776 		new_value = apr_pstrdup (cmd->pool, value);
777 	}
778 
779 	*((char **) ptr) = new_value;
780 	DEBUG_PRINT (0, "store_config end: %s", new_value);
781 	return NULL;
782 }
783 
784 static void *
merge_config(apr_pool_t * p,void * base_conf,void * new_conf)785 merge_config (apr_pool_t *p, void *base_conf, void *new_conf)
786 {
787 	module_cfg *base_module = (module_cfg *) base_conf;
788 	module_cfg *new_module = (module_cfg *) new_conf;
789 	xsp_data *base_config;
790 	xsp_data *new_config;
791 	int nservers;
792 
793 	if (new_module->nservers == 0)
794 		return new_module;
795 
796 	base_config = base_module->servers;
797 	new_config = new_module->servers;
798 	nservers = base_module->nservers + new_module->nservers;
799 
800 	/* FIXME: error out on duplicate aliases. */
801 	base_module->servers = apr_pcalloc (p, sizeof (xsp_data) * nservers);
802 	memcpy (base_module->servers, base_config, sizeof (xsp_data) * base_module->nservers);
803 	memcpy (&base_module->servers [base_module->nservers], new_config, new_module->nservers * sizeof (xsp_data));
804 	base_module->nservers = nservers;
805 	DEBUG_PRINT (0, "Total mod-mono-servers to spawn so far: %d", nservers);
806 	return new_module;
807 }
808 
809 static void *
create_dir_config(apr_pool_t * p,char * dirspec)810 create_dir_config (apr_pool_t *p, char *dirspec)
811 {
812 	per_dir_config *cfg;
813 
814 	DEBUG_PRINT (0, "creating dir config for %s", dirspec);
815 
816 	cfg = apr_pcalloc (p, sizeof (per_dir_config));
817 	if (dirspec != NULL)
818 		cfg->location = apr_pstrdup (p, dirspec);
819 
820 	return cfg;
821 }
822 
823 static char *
get_default_global_socket_name(apr_pool_t * pool,const char * base)824 get_default_global_socket_name (apr_pool_t *pool, const char *base)
825 {
826 	return apr_pstrcat (pool, base, "_", "global", NULL);
827 }
828 
829 static void *
create_mono_server_config(apr_pool_t * p,server_rec * s)830 create_mono_server_config (apr_pool_t *p, server_rec *s)
831 {
832 	module_cfg *server;
833 
834 	DEBUG_PRINT (0, "creating mono server config");
835 	server = apr_pcalloc (p, sizeof (module_cfg));
836 	server->auto_app = TRUE;
837 	server->auto_app_set = FALSE;
838 	add_xsp_server (p, GLOBAL_SERVER_NAME, server, FALSE, FALSE);
839 	return server;
840 }
841 
842 static void
request_send_response_from_memory(request_rec * r,char * byteArray,int size,int noFlush)843 request_send_response_from_memory (request_rec *r, char *byteArray, int size, int noFlush)
844 {
845 	DEBUG_PRINT (0, "sending from memory with%s flush", noFlush ? "out" : "");
846 
847 	ap_rwrite (byteArray, size, r);
848 	if (!noFlush) {
849 		DEBUG_PRINT (0, "flushing");
850 		ap_rflush (r);
851 	}
852 }
853 
854 static void
request_send_response_string(request_rec * r,char * byteArray)855 request_send_response_string (request_rec *r, char *byteArray)
856 {
857 	request_send_response_from_memory (r, byteArray, strlen (byteArray), 0);
858 }
859 
860 /* Not connection because actual port will vary depending on Apache configuration */
861 static int
request_get_server_port(request_rec * r)862 request_get_server_port (request_rec *r)
863 {
864 	return ap_get_server_port (r);
865 }
866 
867 static int
connection_get_remote_port(conn_rec * c)868 connection_get_remote_port (conn_rec *c)
869 {
870 #if defined(APACHE22)
871 	return c->remote_addr->port;
872 #else
873 #if defined(APACHE24)
874   return c->client_addr->port;
875 #else
876 	apr_port_t port;
877 	apr_sockaddr_port_get (&port, c->remote_addr);
878 	return port;
879 #endif
880 #endif
881 
882 }
883 
884 static int
connection_get_local_port(request_rec * r)885 connection_get_local_port (request_rec *r)
886 {
887 #if defined(APACHE22)
888 	return r->connection->local_addr->port;
889 #else
890 #if defined(APACHE24)
891 	return r->connection->local_addr->port;
892 #else
893 	apr_port_t port;
894 	apr_sockaddr_port_get (&port, r->connection->local_addr);
895 	return port;
896 #endif
897 #endif
898 }
899 
900 static const char *
connection_get_remote_name(request_rec * r)901 connection_get_remote_name (request_rec *r)
902 {
903 	return ap_get_remote_host (r->connection, r->per_dir_config, REMOTE_NAME, NULL);
904 }
905 
906 static void
set_response_header(request_rec * r,const char * name,const char * value)907 set_response_header (request_rec *r,
908 		     const char *name,
909 		     const char *value)
910 {
911 	if (!strcasecmp (name,"Content-Type")) {
912 		r->content_type = value;
913 	} else {
914 		apr_table_addn (r->headers_out, name, value);
915 	}
916 }
917 
918 static int
setup_client_block(request_rec * r)919 setup_client_block (request_rec *r)
920 {
921 	if (r->read_length)
922 		return APR_SUCCESS;
923 
924 	return ap_setup_client_block (r, REQUEST_CHUNKED_DECHUNK);
925 }
926 
927 static int
write_data(apr_socket_t * sock,const void * str,apr_size_t size)928 write_data (apr_socket_t *sock, const void *str, apr_size_t size)
929 {
930 	apr_size_t prevsize = size;
931 	apr_status_t statcode;
932 
933 	if ((statcode = apr_socket_send (sock, str, &size)) != APR_SUCCESS) {
934 		ap_log_error (APLOG_MARK, APLOG_ERR, STATCODE_AND_SERVER (statcode), "write_data failed");
935 		return -1;
936 	}
937 
938 	return (prevsize == size) ? size : -1;
939 }
940 
941 static int
read_data(apr_socket_t * sock,void * ptr,apr_size_t size)942 read_data (apr_socket_t *sock, void *ptr, apr_size_t size)
943 {
944 	apr_status_t statcode;
945 
946 	if ((statcode = apr_socket_recv (sock, ptr, &size)) != APR_SUCCESS) {
947 		ap_log_error (APLOG_MARK, APLOG_ERR, STATCODE_AND_SERVER (statcode), "read_data failed");
948 		return -1;
949 	}
950 
951 	return size;
952 }
953 
954 static char *
read_data_string(apr_pool_t * pool,apr_socket_t * sock,char ** ptr,int32_t * size)955 read_data_string (apr_pool_t *pool, apr_socket_t *sock, char **ptr, int32_t *size)
956 {
957 	int l, count;
958 	char *buf;
959 	apr_status_t result;
960 
961 	if (read_data (sock, &l, sizeof (int32_t)) == -1)
962 		return NULL;
963 
964 	l = INT_FROM_LE (l);
965 	buf = apr_pcalloc (pool, l + 1);
966 	count = l;
967 	while (count > 0) {
968 		result = read_data (sock, buf + l - count, count);
969 		if (result == -1)
970 			return NULL;
971 
972 		count -= result;
973 	}
974 
975 	if (ptr)
976 		*ptr = buf;
977 
978 	if (size)
979 		*size = l;
980 
981 	return buf;
982 }
983 
984 static int
send_entire_file(request_rec * r,const char * filename,int * result,xsp_data * xsp)985 send_entire_file (request_rec *r, const char *filename, int *result, xsp_data *xsp)
986 {
987 #ifdef APR_LARGEFILE
988 # define MODMONO_LARGE APR_LARGEFILE
989 #else
990 # define MODMONO_LARGE 0
991 #endif
992 	apr_file_t *file;
993 	apr_status_t st;
994 	apr_finfo_t info;
995 	apr_size_t nbytes;
996 	const apr_int32_t flags = APR_READ | APR_SENDFILE_ENABLED | MODMONO_LARGE;
997 	int retval = 0;
998 	gchar *file_path = mono_portability_find_file (xsp->portability_level, filename, TRUE);
999 
1000 	st = apr_file_open (&file, file_path ? file_path : filename, flags, APR_OS_DEFAULT, r->pool);
1001 	if (st != APR_SUCCESS) {
1002 		DEBUG_PRINT (2, "file_open FAILED (path: %s)", file_path ? file_path : filename);
1003 		*result = HTTP_FORBIDDEN;
1004 		retval = -1;
1005 		goto finish;
1006 	}
1007 
1008 	st = apr_file_info_get (&info, APR_FINFO_SIZE, file);
1009 	if (st != APR_SUCCESS) {
1010 		DEBUG_PRINT (2, "info_get FAILED");
1011 		*result = HTTP_FORBIDDEN;
1012 		retval = -1;
1013 		goto finish;
1014 	}
1015 
1016 	st = ap_send_fd (file, r, 0, info.size, &nbytes);
1017 	apr_file_close (file);
1018 	if (nbytes < 0 || st != APR_SUCCESS) {
1019 		DEBUG_PRINT (2, "SEND FAILED");
1020 		*result = HTTP_INTERNAL_SERVER_ERROR;
1021 		retval = -1;
1022 		goto finish;
1023 	}
1024 
1025   finish:
1026 	if (file_path)
1027 		g_free (file_path);
1028 
1029 	return retval;
1030 }
1031 
1032 static int
send_response_headers(request_rec * r,apr_socket_t * sock)1033 send_response_headers (request_rec *r, apr_socket_t *sock)
1034 {
1035 	char *str;
1036 	int32_t size;
1037 	int pos, len;
1038 	char *name;
1039 	char *value;
1040 
1041 	if (read_data_string (r->pool, sock, &str, &size) == NULL) {
1042 		ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER, "failed to read data string");
1043 		return -1;
1044 	}
1045 
1046 	DEBUG_PRINT (0, "Headers length: %d", size);
1047 	pos = 0;
1048 	while (size > 0) {
1049 		name = &str [pos];
1050 		len = strlen (name);
1051 		pos += len + 1;
1052 		size -= len + 1;
1053 		value = &str [pos];
1054 		len = strlen (value);
1055 		pos += len + 1;
1056 		size -= len + 1;
1057 		set_response_header (r, name, value);
1058 	}
1059 
1060 	return 0;
1061 }
1062 
1063 static void
remove_http_vars(apr_table_t * table)1064 remove_http_vars (apr_table_t *table)
1065 {
1066 	const apr_array_header_t *elts;
1067 	const apr_table_entry_t *t_elt;
1068 	const apr_table_entry_t *t_end;
1069 
1070 	elts = apr_table_elts (table);
1071 	if (elts->nelts == 0)
1072 		return;
1073 
1074 	t_elt = (const apr_table_entry_t *) (elts->elts);
1075 	t_end = t_elt + elts->nelts;
1076 
1077 	do {
1078 		if (!strncmp (t_elt->key, "HTTP_", 5)) {
1079 			apr_table_setn (table, t_elt->key, NULL);
1080 		}
1081 		t_elt++;
1082 	} while (t_elt < t_end);
1083 }
1084 
1085 static char *
get_client_block_buffer(request_rec * r,uint32_t requested_size,uint32_t * actual_size)1086 get_client_block_buffer (request_rec *r, uint32_t requested_size, uint32_t *actual_size)
1087 {
1088 	request_data *rd = ap_get_module_config (r->request_config, &mono_module);
1089 
1090 	if (rd == NULL)	{
1091 		rd = apr_pcalloc (r->pool, sizeof (request_data));
1092 		ap_set_module_config (r->request_config, &mono_module, rd);
1093 	}
1094 
1095 	if (requested_size > 1024 * 1024)
1096 		requested_size = 1024 * 1024;
1097 
1098 	if (requested_size > rd->client_block_buffer_size) {
1099 		rd->client_block_buffer = apr_pcalloc (r->pool, requested_size);
1100 		rd->client_block_buffer_size = requested_size;
1101 	}
1102 
1103 	*actual_size = requested_size;
1104 	return rd->client_block_buffer;
1105 }
1106 
1107 static int
do_command(int command,apr_socket_t * sock,request_rec * r,int * result,xsp_data * xsp)1108 do_command (int command, apr_socket_t *sock, request_rec *r, int *result, xsp_data *xsp)
1109 {
1110 	int32_t size;
1111 	char *str;
1112 	const char *cstr;
1113 	int32_t i;
1114 	uint32_t actual_size;
1115 	int status = 0;
1116 	apr_pool_t *temp_pool;
1117 	char *error_message = NULL;
1118 
1119 	if (command < 0 || command >= LAST_COMMAND) {
1120 		ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1121 			      "Unknown command: %d", command);
1122 		*result = HTTP_INTERNAL_SERVER_ERROR;
1123 		return FALSE;
1124 	}
1125 
1126 	DEBUG_PRINT (2, "Command received: %s (%u)", cmdNames [command], command);
1127 	*result = OK;
1128 	switch (command) {
1129 		case SEND_FROM_MEMORY:
1130 			apr_pool_create (&temp_pool, r->pool);
1131 			if (read_data_string (temp_pool, sock, &str, &size) == NULL) {
1132 				error_message = "failed to read data for SEND_FROM_MEMORY command";
1133 				status = -1;
1134 				apr_pool_destroy (temp_pool);
1135 				break;
1136 			}
1137 			request_send_response_from_memory (r, str, size, xsp->no_flush);
1138 			apr_pool_destroy (temp_pool);
1139 			break;
1140 		case GET_SERVER_VARIABLES:
1141 			ap_add_cgi_vars (r);
1142 			ap_add_common_vars (r);
1143 			remove_http_vars (r->subprocess_env);
1144 			cstr = apr_table_get (r->subprocess_env, "HTTPS");
1145 			if (cstr != NULL && !strcmp (cstr, "on"))
1146 				apr_table_add (r->subprocess_env, "SERVER_PORT_SECURE", "True");
1147 			if (!send_table (r->pool, r->subprocess_env, sock)) {
1148 				error_message = "failed to send server variables";
1149 				status = -1;
1150 			} else
1151 				status = 0;
1152 			break;
1153 		case SET_RESPONSE_HEADERS:
1154 			status = send_response_headers (r, sock);
1155 			if (status < 0)
1156 				error_message = "failed to send response headers";
1157 			break;
1158 		case GET_LOCAL_PORT:
1159 			i = connection_get_local_port (r);
1160 			i = LE_FROM_INT (i);
1161 			status = write_data (sock, &i, sizeof (int32_t));
1162 			if (status < 0)
1163 				error_message = "failed to get local port";
1164 			break;
1165 		case CLOSE:
1166 			return FALSE;
1167 			break;
1168 		case SHOULD_CLIENT_BLOCK:
1169 			size = ap_should_client_block (r);
1170 			size = LE_FROM_INT (size);
1171 			status = write_data (sock, &size, sizeof (int32_t));
1172 			if (status < 0)
1173 				error_message = "failed to send the 'should block' flag";
1174 			break;
1175 		case SETUP_CLIENT_BLOCK:
1176 			if (setup_client_block (r) != APR_SUCCESS) {
1177 				size = LE_FROM_INT (-1);
1178 				status = write_data (sock, &size, sizeof (int32_t));
1179 				if (status < 0)
1180 					error_message = "failed to setup client block (data size)";
1181 				break;
1182 			}
1183 
1184 			size = LE_FROM_INT (0);
1185 			status = write_data (sock, &size, sizeof (int32_t));
1186 			if (status < 0)
1187 				error_message = "failed to setup client block (data)";
1188 			break;
1189 		case GET_CLIENT_BLOCK:
1190 			status = read_data (sock, &i, sizeof (int32_t));
1191 			if (status == -1)
1192 				break;
1193 
1194 			i = INT_FROM_LE (i);
1195 			if (i < 0) {
1196 				DEBUG_PRINT (2, "xsp sent us size == %d: not processing", i);
1197 				abort();
1198 			}
1199 			str = get_client_block_buffer (r, (uint32_t) i, &actual_size);
1200 			DEBUG_PRINT (2, "requested size == %u, actual size == %u", i, actual_size);
1201 			i = ap_get_client_block (r, str, actual_size);
1202 			i = LE_FROM_INT (i);
1203 			status = write_data (sock, &i, sizeof (int32_t));
1204 			if (status < 0)
1205 				error_message = "failed to get client block (data size)";
1206 			i = INT_FROM_LE (i);
1207 			if (i == -1)
1208 				break;
1209 			status = write_data (sock, str, i);
1210 			if (status < 0)
1211 				error_message = "failed to get client block (data)";
1212 			break;
1213 		case SET_STATUS:
1214 			status = read_data (sock, &i, sizeof (int32_t));
1215 			if (status == -1) {
1216 				error_message = "failed to set status (data size)";
1217 				break;
1218 			}
1219 
1220 			if (read_data_string (r->pool, sock, &str, NULL) == NULL) {
1221 				error_message = "failed to set status (data)";
1222 				status = -1;
1223 				break;
1224 			}
1225 			r->status = INT_FROM_LE (i);
1226 			r->status_line = str;
1227 			break;
1228 		case DECLINE_REQUEST:
1229 			*result = DECLINED;
1230 			return FALSE;
1231 		case IS_CONNECTED:
1232 			*result = (r->connection->aborted ? 0 : 1);
1233 			status = write_data (sock, result, sizeof (int32_t));
1234 			if (status < 0)
1235 				error_message = "failed to check if the backend is connected";
1236 			break;
1237 		case MYNOT_FOUND:
1238 			ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1239 				      "No application found for %s", r->uri);
1240 
1241 			ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1242 				      "Host header was %s",
1243 				      apr_table_get (r->headers_in, "host"));
1244 
1245 			*result = HTTP_NOT_FOUND;
1246 			return FALSE;
1247 		case SEND_FILE:
1248 			if (read_data_string (r->pool, sock, &str, NULL) == NULL) {
1249 				error_message = "failed to send file (file name)";
1250 				status = -1;
1251 				break;
1252 			}
1253 			status = send_entire_file (r, str, result, xsp);
1254 			if (status == -1)
1255 				error_message = "failed to send file (file data)";
1256 			break;
1257 
1258 		case SET_CONFIGURATION: {
1259 			if (read_data (sock, &xsp->no_flush, sizeof (xsp->no_flush)) == -1) {
1260 				error_message = "failed to set configuration (output buffering)";
1261 				status = -1;
1262 				break;
1263 			}
1264 			break;
1265 		}
1266 
1267 		default:
1268 			error_message = "unknown command";
1269 			status = -1;
1270 			break;
1271 	}
1272 
1273 	if (status == -1) {
1274 		ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1275 			      "command failed: %s",
1276 			      error_message ? error_message : "unknown error");
1277 		*result = HTTP_INTERNAL_SERVER_ERROR;
1278 		return FALSE;
1279 	}
1280 
1281 	return TRUE;
1282 }
1283 
1284 #if !defined (HAVE_APR_SOCKET_CONNECT)
1285 /* libapr-0 <= 0.9.3 (or 0.9.2?) */
1286 #	define apr_socket_connect apr_connect
1287 #endif
1288 
1289 static char *
get_default_socket_name(apr_pool_t * pool,const char * alias,const char * base)1290 get_default_socket_name (apr_pool_t *pool, const char *alias, const char *base)
1291 {
1292 	return apr_pstrcat (pool, base, "_", !alias || !alias [0] ? "default" : alias, NULL);
1293 }
1294 
1295 static char *
get_base_socket_path(apr_pool_t * pool,xsp_data * conf)1296 get_base_socket_path (apr_pool_t *pool, xsp_data *conf)
1297 {
1298 	if (conf->filename && conf->filename [0])
1299 		return conf->filename;
1300 	else
1301 		return get_default_socket_name (pool, conf->alias, SOCKET_FILE);
1302 }
1303 
1304 static char *
get_unix_socket_path(apr_pool_t * pool,xsp_data * conf)1305 get_unix_socket_path (apr_pool_t *pool, xsp_data *conf)
1306 {
1307 	DEBUG_PRINT (0, "getting unix socket path");
1308 	if (IS_MASTER (conf)) {
1309 		return get_default_global_socket_name (pool, SOCKET_FILE);
1310 	} else {
1311 		return get_base_socket_path (pool, conf);
1312 	}
1313 }
1314 
1315 static apr_status_t
try_connect(xsp_data * conf,apr_socket_t ** sock,apr_int32_t family,apr_pool_t * pool)1316 try_connect (xsp_data *conf, apr_socket_t **sock, apr_int32_t family, apr_pool_t *pool)
1317 {
1318 	char *error;
1319 	struct sockaddr_un unix_address;
1320 	struct sockaddr *ptradd;
1321 	char *fn = NULL;
1322 	char *la = NULL;
1323 	int err;
1324 
1325 	if (conf->listen_port == NULL) {
1326 		apr_os_sock_t sock_fd;
1327 
1328 		apr_os_sock_get (&sock_fd, *sock);
1329 		unix_address.sun_family = PF_UNIX;
1330 		fn = get_unix_socket_path (pool, conf);
1331 		if (!fn)
1332 			return -2;
1333 
1334 		DEBUG_PRINT (1, "Socket file name %s", fn);
1335 		memcpy (unix_address.sun_path, fn, strlen (fn) + 1);
1336 		ptradd = (struct sockaddr *) &unix_address;
1337 		if (connect (sock_fd, ptradd, sizeof (unix_address)) != -1)
1338 			return APR_SUCCESS;
1339 	} else {
1340 		apr_status_t rv;
1341 		apr_sockaddr_t *sa;
1342 
1343 		la = conf->listen_address ? conf->listen_address : LISTEN_ADDRESS;
1344 		rv = apr_sockaddr_info_get (&sa, la, family,
1345 					    (apr_port_t)string_to_long (conf->listen_port, "MonoListenPort", 0), 0, pool);
1346 
1347 		if (rv != APR_SUCCESS) {
1348 			ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1349 				      "mod_mono: error in address ('%s') or port ('%s').",
1350 				      la, conf->listen_port);
1351 			return -2;
1352 		}
1353 
1354 		rv = apr_socket_connect (*sock, sa);
1355 		if (rv == APR_SUCCESS)
1356 			return APR_SUCCESS;
1357 		errno = rv;
1358 	}
1359 
1360 	err = errno;
1361 	switch (err) {
1362 		case ENOENT:
1363 		case ECONNREFUSED:
1364 			return -1; /* Can try to launch mod-mono-server */
1365 		case EPERM:
1366 			error = strerror (err);
1367 			if (conf->listen_port == NULL)
1368 				ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1369 					      "mod_mono: file %s exists, but wrong permissions. %s", fn, error);
1370 			else
1371 				ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1372 					      "mod_mono: no permission to listen on %s. %s",
1373 					      conf->listen_port, error);
1374 
1375 
1376 			apr_socket_close (*sock);
1377 			return -2; /* Unrecoverable */
1378 		default:
1379 			error = strerror (err);
1380 			if (conf->listen_port == NULL)
1381 				ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1382 					      "mod_mono: connect error (%s). File: %s",
1383 					      error, fn);
1384 			else
1385 				ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1386 					      "mod_mono: connect error (%s). Address: %s Port: %s",
1387 					      error, la, conf->listen_port);
1388 
1389 
1390 			apr_socket_close (*sock);
1391 			return -2; /* Unrecoverable */
1392 	}
1393 }
1394 
1395 static char *
get_directory(apr_pool_t * pool,const char * filepath)1396 get_directory (apr_pool_t *pool, const char *filepath)
1397 {
1398 	char *sep;
1399 	char *result;
1400 
1401 	sep = strrchr ((char *) filepath, '/');
1402 	if (sep == NULL || sep == filepath)
1403 		return "/";
1404 
1405 	result = apr_pcalloc (pool, sep - filepath + 1);
1406 	strncpy (result, filepath, sep - filepath);
1407 	return result;
1408 }
1409 
1410 #ifdef HAVE_SETENV
1411 #	define SETENV(pool, name, value) setenv (name, value, 1)
1412 #else
1413 #	ifdef HAVE_PUTENV
1414 #	define SETENV(pool, name, value) setenv_to_putenv (pool, name, value)
1415 static int
setenv_to_putenv(apr_pool_t * pool,char * name,char * value)1416 setenv_to_putenv (apr_pool_t *pool, char *name, char *value)
1417 {
1418 	char *arg;
1419 
1420 	arg = apr_pcalloc (pool, strlen (name) + strlen (value) + 2);
1421 	sprintf (arg, "%s=%s", name, value);
1422 	return putenv (arg);
1423 }
1424 
1425 #	else
1426 #	error No setenv or putenv found!
1427 #endif
1428 #endif
1429 
1430 static void
set_environment_variables(apr_pool_t * pool,char * environment_variables)1431 set_environment_variables (apr_pool_t *pool, char *environment_variables)
1432 {
1433 	char *tmp;
1434 	char *name;
1435 	char *value;
1436 
1437 	/* were any environment_variables specified? */
1438 	if (environment_variables == NULL)
1439 		return;
1440 
1441 	name = environment_variables;
1442 	tmp = strchr (environment_variables, '=');
1443 	while (tmp != NULL) {
1444 		*tmp = '\0';
1445 		value = tmp + 1;
1446 		tmp = strchr (value, ';');
1447 		if (tmp != NULL)
1448 			*tmp = '\0';
1449 
1450 		SETENV (pool, name, value);
1451 		if (tmp == NULL)
1452 			break;
1453 
1454 		name = tmp + 1;
1455 		tmp = strchr (name, '=');
1456 	}
1457 }
1458 
1459 static void
set_process_limits2(int resource,int max,char * name)1460 set_process_limits2 (int resource, int max, char *name) {
1461 	struct rlimit limit;
1462 
1463 	if (max > 0) {
1464 #ifdef HAVE_SETRLIMIT
1465 		/* We don't want SIGXCPU */
1466 		limit.rlim_cur = max;
1467 		limit.rlim_max = max;
1468 		DEBUG_PRINT (1, "Setting %s limit to %d", name, max);
1469 		if (setrlimit (resource, &limit) == -1) {
1470 			if (errno == EPERM)
1471 				ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1472 					      "Failed to set %s process limit on mod-mono-server to %d: The value is greater than an existing hard limit", name, max);
1473 			else
1474 				ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1475 					      "Failed to set %s process limit on mod-mono-server to %d.", name, max);
1476 		}
1477 #else
1478 		ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1479 			      "Setting %s process limit is not supported on this platform.", name);
1480 #endif
1481 	}
1482 }
1483 
1484 static void
set_process_limits(int max_cpu_time,int max_memory)1485 set_process_limits (int max_cpu_time, int max_memory)
1486 {
1487 #ifndef HAVE_SETRLIMIT
1488 #define RLIMIT_CPU 0
1489 #define RLIMIT_DATA 0
1490 #endif
1491 	set_process_limits2 (RLIMIT_CPU, max_cpu_time, "CPU time");
1492 	set_process_limits2 (RLIMIT_DATA, max_memory, "memory (data segment)");
1493 #ifdef RLIMIT_AS
1494 	// RLIMIT_AS may not be defined on some systems (e.g. BSD)
1495 	// If it is defined, it is almost certainly more correct than RLIMIT_DATA, as it
1496 	// will cause mmap to return null.
1497 	set_process_limits2 (RLIMIT_AS, max_memory, "memory (virtual memory)");
1498 #endif
1499 
1500 }
1501 
1502 static void
configure_stdout(char null_stdout)1503 configure_stdout (char null_stdout)
1504 {
1505 #ifndef WIN32
1506 	if (null_stdout) {
1507 
1508 		int fd;
1509 
1510 		fd = open ("/dev/null", O_WRONLY);
1511 		if (fd >= 0) {
1512 			dup2 (fd, 1);
1513 		}
1514 	} else
1515 #endif
1516 		dup2 (2, 1);
1517 }
1518 
1519 static void
fork_mod_mono_server(apr_pool_t * pool,xsp_data * config)1520 fork_mod_mono_server (apr_pool_t *pool, xsp_data *config)
1521 {
1522 	pid_t pid;
1523 	int i;
1524 	const int MAXARGS = 21;
1525 	char *argv [MAXARGS];
1526 	int argi;
1527 	char *path;
1528 	char *tmp;
1529 	char *serverdir;
1530 	char *server_path;
1531 	char *wapidir;
1532 	int max_memory = 0;
1533 	int max_cpu_time = 0;
1534 	int status;
1535 	char is_master;
1536 	sigset_t sigset;
1537 #if defined (APR_HAS_USER)
1538 	apr_uid_t cur_uid;
1539 	apr_gid_t cur_gid;
1540 
1541 	if (apache_get_userid () == -1 || apache_get_groupid () == -1) {
1542 		ap_log_error (APLOG_MARK, APLOG_CRIT, STATUS_AND_SERVER,
1543 			      "The unix daemon module not initialized yet. Please make sure that "
1544 			      "your mod_mono module is loaded after the User/Group directives have "
1545 			      "been parsed. Not forking the backend.");
1546 		return;
1547 	}
1548 #endif
1549 	is_master = IS_MASTER (config);
1550 	if (is_master && config->listen_port == NULL && config->filename == NULL)
1551 		config->filename = get_default_global_socket_name (pool, SOCKET_FILE);
1552 
1553 	/* Running mod-mono-server not requested */
1554 	if (!strcasecmp (config->run_xsp, "false")) {
1555 		DEBUG_PRINT (1, "Not running mod-mono-server: %s", config->run_xsp);
1556 		ap_log_error (APLOG_MARK, APLOG_DEBUG, STATUS_AND_SERVER,
1557 			      "Not running mod-mono-server.exe");
1558 		return;
1559 	}
1560 
1561 	/*
1562 	 * At least one of MonoApplications, MonoApplicationsConfigFile or
1563 	 * MonoApplicationsConfigDir must be specified, except for the 'global'
1564 	 * instance that will be used to create applications on demand.
1565 	 */
1566 	DEBUG_PRINT (1, "Applications: %s", config->applications);
1567 	DEBUG_PRINT (1, "Config file: %s", config->appconfig_file);
1568 	DEBUG_PRINT (1, "Config dir.: %s", config->appconfig_dir);
1569 	if (!is_master && config->applications == NULL && config->appconfig_file == NULL &&
1570 	    config->appconfig_dir == NULL) {
1571 		ap_log_error (APLOG_MARK, APLOG_ERR,
1572 			      STATUS_AND_SERVER,
1573 			      "Not running mod-mono-server.exe because no MonoApplications, "
1574 			      "MonoApplicationsConfigFile or MonoApplicationConfigDir specified.");
1575 		return;
1576 	}
1577 
1578 	/* Only one of MonoUnixSocket and MonoListenPort. */
1579 	DEBUG_PRINT (1, "Listen port: %s", config->listen_port);
1580 	if (config->listen_port != NULL && config->filename != NULL) {
1581 		ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1582 			      "Not running mod-mono-server.exe because both MonoUnixSocket and "
1583 			      "MonoListenPort specified.");
1584 		return;
1585 	}
1586 
1587 	/* MonoListenAddress must be used together with MonoListenPort */
1588 	DEBUG_PRINT (1, "Listen address: %s", config->listen_address);
1589 	if (config->listen_port == NULL && config->listen_address != NULL) {
1590 		ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1591 			      "Not running mod-mono-server.exe because MonoListenAddress "
1592 			      "is present and there is no MonoListenPort.");
1593 		return;
1594 	}
1595 
1596 
1597 	if (config->max_memory != NULL)
1598 		max_memory = (int)string_to_long (config->max_memory, "MonoMaxMemory", -1);
1599 
1600 	if (config->max_cpu_time != NULL)
1601 		max_cpu_time = (int)string_to_long (config->max_cpu_time, "MonoMaxCPUTime", -1);
1602 
1603 	pid = fork ();
1604 	if (pid > 0) {
1605 		waitpid (pid, &status, 0);
1606 		return;
1607 	}
1608 
1609 	/* Double fork to prevent defunct/zombie processes */
1610 	pid = fork ();
1611 	if (pid > 0)
1612 		exit (0);
1613 
1614 	setsid ();
1615 	set_environment_variables (pool, config->env_vars);
1616 
1617 	if (config->iomap && *config->iomap)
1618 		SETENV (pool, "MONO_IOMAP", config->iomap);
1619 
1620 	status = chdir ("/");
1621 
1622 #if defined (APR_HAS_USER)
1623 	/*
1624 	 * Make sure the backend runs with proper uid/gid if we're forking
1625 	 * from the module postconfig handler.
1626 	 */
1627 	if (apr_uid_current (&cur_uid, &cur_gid, pool) == APR_SUCCESS && cur_uid == 0) {
1628 		DEBUG_PRINT (2, "switching forked process group to %u", (unsigned)apache_get_groupid ());
1629 		if (setgid (apache_get_groupid ()) == -1)
1630 			ap_log_error (APLOG_MARK, APLOG_ALERT, STATUS_AND_SERVER,
1631 				      "setgid: unable to set group id to %u. %s",
1632 				      (unsigned)apache_get_groupid (), strerror (errno));
1633 
1634 		DEBUG_PRINT (2, "initializing groups for forked process user %s", apache_get_username ());
1635 		if (initgroups (apache_get_username (), apache_get_groupid ()) == -1)
1636 			ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1637 				      "initgroups: unable to initialize supplementary group list for user %s: %s",
1638 				      apache_get_username (),
1639 				      strerror (errno));
1640 
1641 		DEBUG_PRINT (2, "switching forked process user to %s", apache_get_username ());
1642 		if (setuid (apache_get_userid ()) == -1)
1643 			ap_log_error (APLOG_MARK, APLOG_ALERT, STATUS_AND_SERVER,
1644 				      "setuid: unable to set user id to %u. %s",
1645 				      (unsigned)apache_get_userid (), strerror (errno));
1646 	}
1647 #endif
1648 
1649 	if (config->umask_value == NULL)
1650 		umask (0077);
1651 	else {
1652 		unsigned int uval;
1653 		if (sscanf(config->umask_value, "%o", &uval) != 1) {
1654 			DEBUG_PRINT (1, "umask conversion to octal failed");
1655 			uval = 0077;
1656 		}
1657 		DEBUG_PRINT (1, "setting umask to %o", uval);
1658 		umask (uval);
1659 	}
1660 	DEBUG_PRINT (1, "child started");
1661 
1662 	if (config->debug && !strcasecmp (config->debug, "True")) {
1663 		SETENV (pool, "MONO_OPTIONS", "--debug");
1664 		configure_stdout (0);
1665 	} else
1666 		configure_stdout (STDOUT_NULL_DEFAULT);
1667 
1668 	for (i = getdtablesize () - 1; i >= 3; i--)
1669 		close (i);
1670 
1671 	set_process_limits (max_cpu_time, max_memory);
1672 	tmp = getenv ("PATH");
1673 	DEBUG_PRINT (1, "PATH: %s", tmp);
1674 	if (tmp == NULL)
1675 		tmp = "";
1676 
1677 	if (config->server_path && config->server_path [0])
1678 		server_path = config->server_path;
1679 	else if (config->target_framework && config->target_framework [0]) {
1680 		switch (config->target_framework [0]) {
1681 			case '2':
1682 			case '3':
1683 				server_path = MODMONO_SERVER_PATH;
1684 			break;
1685 
1686 			case '4':
1687 				server_path = MODMONO_SERVER_BASEPATH "4";
1688 				break;
1689 
1690 			default:
1691 				ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1692 					      "Unsupported target framework version: %s",
1693 					      config->target_framework);
1694 				exit (1);
1695 		}
1696 
1697 	} else
1698 		server_path = MODMONO_SERVER_PATH;
1699 
1700 	serverdir = get_directory (pool, server_path);
1701 	DEBUG_PRINT (1, "serverdir: %s", serverdir);
1702 	path = apr_pcalloc (pool, strlen (tmp) + strlen (serverdir) + 2);
1703 	sprintf (path, "%s:%s", serverdir, tmp);
1704 
1705 	DEBUG_PRINT (1, "PATH after: %s", path);
1706 	SETENV (pool, "PATH", path);
1707 	if (config->path != NULL)
1708 		SETENV (pool, "MONO_PATH", config->path);
1709 	wapidir = apr_pcalloc (pool, strlen (config->wapidir) + 5 + 2);
1710 	sprintf (wapidir, "%s/%s", config->wapidir, ".wapi");
1711 	mkdir (wapidir, 0700);
1712 	if (chmod (wapidir, 0700) != 0 && (errno == EPERM || errno == EACCES)) {
1713 		ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1714 			      "%s: %s", wapidir, strerror (errno));
1715 		exit (1);
1716 	}
1717 
1718 	SETENV (pool, "MONO_SHARED_DIR", config->wapidir);
1719 
1720 #ifdef REMOVE_DISPLAY
1721 #warning mod_mono is compiled with support for removing the DISPLAY variable (Bug 464225)
1722 #ifdef HAVE_UNSETENV
1723 	unsetenv ("DISPLAY");
1724 #else
1725 #warning unsetenv (2) is not available!
1726 #endif
1727 #endif
1728 	memset (argv, 0, sizeof (char *) * MAXARGS);
1729 	argi = 0;
1730 	argv [argi++] = server_path;
1731 	if (config->listen_port != NULL) {
1732 		char *la;
1733 
1734 		la = config->listen_address;
1735 		la = la ? la : LISTEN_ADDRESS;
1736 		argv [argi++] = "--address";
1737 		argv [argi++] = la;
1738 		argv [argi++] = "--port";
1739 		argv [argi++] = config->listen_port;
1740 	} else {
1741 		char *fn = get_unix_socket_path (pool, config);
1742 		DEBUG_PRINT (0, "Backend socket path: %s", fn);
1743 		argv [argi++] = "--filename";
1744 		argv [argi++] = fn;
1745 	}
1746 
1747 	if (config->listen_backlog != NULL) {
1748 		argv [argi++] = "--backlog";
1749 		argv [argi++] = config->listen_backlog;
1750 	}
1751 
1752 	if (config->minthreads != NULL) {
1753 		argv [argi++] = "--minThreads";
1754 		argv [argi++] = config->minthreads;
1755 	}
1756 
1757 	if (config->applications != NULL) {
1758 		argv [argi++] = "--applications";
1759 		argv [argi++] = config->applications;
1760 	}
1761 
1762 	if (config->hidden != NULL) {
1763 		if (strcasecmp (config->hidden, "false"))
1764 			argv [argi++] = "--no-hidden";
1765 	}
1766 
1767 	argv [argi++] = "--nonstop";
1768 	if (config->document_root != NULL) {
1769 		argv [argi++] = "--root";
1770 		argv [argi++] = config->document_root;
1771 	}
1772 
1773 	if (config->appconfig_file != NULL) {
1774 		argv [argi++] = "--appconfigfile";
1775 		argv [argi++] = config->appconfig_file;
1776 	}
1777 
1778 	if (config->appconfig_dir != NULL) {
1779 		argv [argi++] = "--appconfigdir";
1780 		argv [argi++] = config->appconfig_dir;
1781 	}
1782 
1783 	if (is_master)
1784 		argv [argi++] = "--master";
1785 	/*
1786 	 * The last element in the argv array must always be NULL
1787 	 * to terminate the array for execv().
1788 	 *
1789 	 * Any new argi++'s that are added here must also increase
1790 	 * the maxargs argument at the top of this method to prevent
1791 	 * array out-of-bounds.
1792 	 */
1793 
1794 	ap_log_error (APLOG_MARK, APLOG_DEBUG, STATUS_AND_SERVER,
1795 		      "running '%s %s %s %s %s %s %s %s %s %s %s %s %s'",
1796 		      argv [0], argv [1], argv [2], argv [3], argv [4],
1797 		      argv [5], argv [6], argv [7], argv [8],
1798 		      argv [9], argv [10], argv [11], argv [12]);
1799 
1800 	/* Unblock signals Mono uses: see bug #472732 */
1801 	/* TODO: are PWR and XCPU used in non-linux systems too? What about 33 and 35? */
1802 	sigemptyset (&sigset);
1803 #ifdef SIGPWR
1804 	sigaddset (&sigset, SIGPWR);
1805 #endif
1806 #ifdef SIGXCPU
1807 	sigaddset (&sigset, SIGXCPU);
1808 #endif
1809 	sigaddset (&sigset, 33);
1810 	sigaddset (&sigset, 35);
1811 	sigprocmask (SIG_UNBLOCK, &sigset, NULL);
1812 
1813 	execv (argv [0], argv);
1814 	ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1815 		      "Failed running '%s %s %s %s %s %s %s %s %s %s %s %s %s'. Reason: %s",
1816 		      argv [0], argv [1], argv [2], argv [3], argv [4],
1817 		      argv [5], argv [6], argv [7], argv [8],
1818 		      argv [9], argv [10], argv [11], argv [12],
1819 		      strerror (errno));
1820 	exit (1);
1821 }
1822 
1823 static apr_status_t
setup_socket(apr_socket_t ** sock,xsp_data * conf,apr_pool_t * pool)1824 setup_socket (apr_socket_t **sock, xsp_data *conf, apr_pool_t *pool)
1825 {
1826 	apr_status_t rv;
1827 	int family, proto;
1828 
1829 	if (conf->listen_port != NULL) {
1830 #if APR_HAVE_IPV6
1831 		apr_status_t rv;
1832 		apr_sockaddr_t *sa;
1833 		char *la = NULL;
1834 #endif
1835 		family = AF_UNSPEC;
1836 
1837 #if APR_HAVE_IPV6
1838 		la = conf->listen_address ? conf->listen_address : LISTEN_ADDRESS;
1839 		rv = apr_sockaddr_info_get (&sa, la, family,
1840 					    (apr_port_t)string_to_long (conf->listen_port, "MonoListenPort", 0),
1841 					    APR_IPV6_ADDR_OK,
1842 					    pool);
1843 
1844 		if (rv != APR_SUCCESS) {
1845 			ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1846 				      "mod_mono: error in address ('%s') or port ('%s'). Assuming AF_UNSPEC.",
1847 				      la, conf->listen_port);
1848 		} else {
1849 			family = sa->family;
1850 		}
1851 
1852 #endif
1853 	} else {
1854 		family = PF_UNIX;
1855 	}
1856 	/* APR_PROTO_TCP = 6 */
1857 	proto = (family == AF_UNSPEC) ? 6 : 0;
1858 	rv = APR_SOCKET_CREATE (sock, family, SOCK_STREAM, proto, pool);
1859 
1860 	if (rv != APR_SUCCESS) {
1861 		int err= errno;
1862 		ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
1863 			      "mod_mono: error creating socket: %d %s", err, strerror (err));
1864 		return rv;
1865 	}
1866 
1867 	rv = try_connect (conf, sock, family, pool);
1868 	DEBUG_PRINT (2, "try_connect: %d", (int) rv);
1869 	return rv;
1870 }
1871 
1872 static int
write_string_to_buffer(char * buffer,int offset,const char * str,size_t str_length)1873 write_string_to_buffer (char *buffer, int offset, const char *str, size_t str_length)
1874 {
1875 	uint32_t le;
1876 	uint32_t tmp;
1877 
1878 	if (!str && str_length > 0)
1879 		str_length = 0;
1880 
1881 	buffer += offset;
1882 	if (str && !str_length) {
1883 		tmp = strlen (str);
1884 		le = LE_FROM_INT (tmp);
1885 	} else
1886 		tmp = (uint32_t)str_length;
1887 
1888 	le = LE_FROM_INT (tmp);
1889 
1890 	memcpy (buffer, &le, sizeof (uint32_t));
1891 	if (tmp > 0) {
1892 		buffer += sizeof (uint32_t);
1893 		memcpy (buffer, str, tmp);
1894 	}
1895 
1896 	return tmp + sizeof (uint32_t);
1897 }
1898 
1899 static int32_t
get_table_send_size(apr_table_t * table)1900 get_table_send_size (apr_table_t *table)
1901 {
1902 	const apr_array_header_t *elts;
1903 	const apr_table_entry_t *t_elt;
1904 	const apr_table_entry_t *t_end;
1905 	int32_t size;
1906 
1907 	elts = apr_table_elts (table);
1908 	if (elts->nelts == 0)
1909 		return sizeof (int32_t);
1910 
1911 	size = sizeof (int32_t) * 2;
1912 	t_elt = (const apr_table_entry_t *) (elts->elts);
1913 	t_end = t_elt + elts->nelts;
1914 
1915 	do {
1916 		if (t_elt->val != NULL) {
1917 			size += sizeof (int32_t) * 2;
1918 			size += strlen (t_elt->key);
1919 			size += strlen (t_elt->val);
1920 		}
1921 		t_elt++;
1922 	} while (t_elt < t_end);
1923 
1924 	return size;
1925 }
1926 
1927 static int32_t
write_table_to_buffer(char * buffer,apr_table_t * table)1928 write_table_to_buffer (char *buffer, apr_table_t *table)
1929 {
1930 	const apr_array_header_t *elts;
1931 	const apr_table_entry_t *t_elt;
1932 	const apr_table_entry_t *t_end;
1933 	char *ptr;
1934 	int32_t count = 0, size;
1935 	char *countLocation = buffer + sizeof (int32_t);
1936 	char *sizeLocation = buffer;
1937 
1938 	elts = apr_table_elts (table);
1939 	if (elts->nelts == 0) { /* size is sizeof (int32_t) */
1940 		int32_t *i32 = (int32_t *) buffer;
1941 		*i32 = 0;
1942 		return sizeof (int32_t);
1943 	}
1944 
1945 	ptr = buffer;
1946 	/* the size is set after the loop */
1947 	/* the count is set after the loop */
1948 	ptr += sizeof (int32_t) * 2;
1949 	t_elt = (const apr_table_entry_t *) (elts->elts);
1950 	t_end = t_elt + elts->nelts;
1951 
1952 	do {
1953 		if (t_elt->val != NULL) {
1954 			DEBUG_PRINT (0, "%s: %s", t_elt->key, t_elt->val);
1955 			ptr += write_string_to_buffer (ptr, 0, t_elt->key, 0);
1956 			ptr += write_string_to_buffer (ptr, 0, t_elt->val, 0);
1957 			count++;
1958 		}
1959 
1960 		t_elt++;
1961 	} while (t_elt < t_end);
1962 
1963 	count = LE_FROM_INT (count);
1964 	memcpy (countLocation, &count, sizeof (int32_t));
1965 	size = (ptr - buffer) - sizeof (int32_t);
1966 	size = LE_FROM_INT (size);
1967 	memcpy (sizeLocation, &size, sizeof (int32_t));
1968 
1969 	return (ptr - buffer);
1970 }
1971 
1972 static int
send_table(apr_pool_t * pool,apr_table_t * table,apr_socket_t * sock)1973 send_table (apr_pool_t *pool, apr_table_t *table, apr_socket_t *sock)
1974 {
1975 	char *buffer;
1976 	int32_t size;
1977 
1978 	size = get_table_send_size (table);
1979 	buffer = apr_pcalloc (pool, size);
1980 	write_table_to_buffer (buffer, table);
1981 	return (write_data (sock, buffer, size) == size);
1982 }
1983 
1984 static int
send_initial_data(request_rec * r,apr_socket_t * sock,char auto_app)1985 send_initial_data (request_rec *r, apr_socket_t *sock, char auto_app)
1986 {
1987 	uint32_t           i;
1988 	char              *str, *ptr;
1989 	uint32_t           size;
1990 	server_rec        *s = r->server;
1991 	initial_data_info  info;
1992 
1993 	DEBUG_PRINT (1, "Send init");
1994 	size = 1 + sizeof (size);
1995 
1996 	info.method_len = ((r->method != NULL) ? strlen (r->method) : 0);
1997 	size += info.method_len + sizeof (int32_t);
1998 	if (s != NULL) {
1999 		info.server_hostname_len = ((s->is_virtual && s->server_hostname != NULL) ? strlen (s->server_hostname) : 0);
2000 		size += info.server_hostname_len + sizeof (int32_t);
2001 	} else {
2002 		info.server_hostname_len = 0;
2003 		size += sizeof (int32_t);
2004 	}
2005 
2006 	info.uri_len = ((r->uri != NULL) ? strlen (r->uri) : 0);
2007 	size += info.uri_len + sizeof (int32_t);
2008 
2009 	info.args_len = ((r->args != NULL) ? strlen (r->args) : 0);
2010 	size += info.args_len + sizeof (int32_t);
2011 
2012 	info.protocol_len = ((r->protocol != NULL) ? strlen (r->protocol) : 0);
2013 	size += info.protocol_len + sizeof (int32_t);
2014 
2015 	info.local_ip_len = strlen (r->connection->local_ip);
2016 	size += info.local_ip_len + sizeof (int32_t);
2017 
2018 	size += sizeof (int32_t);
2019 #if defined(APACHE24)
2020   info.remote_ip_len = strlen (r->connection->client_ip);
2021 #else
2022   info.remote_ip_len = strlen (r->connection->remote_ip);
2023 #endif
2024   size += info.remote_ip_len + sizeof (int32_t);
2025 
2026 	size += sizeof (int32_t);
2027 
2028 	info.remote_name = connection_get_remote_name (r);
2029 	info.remote_name_len = strlen (info.remote_name);
2030 
2031 	size += info.remote_name_len + sizeof (int32_t);
2032 
2033 	size += get_table_send_size (r->headers_in);
2034 
2035 	size++; /* byte. TRUE->auto_app, FALSE: configured application */
2036 	if (auto_app != FALSE) {
2037 		if (r->filename != NULL) {
2038 			info.filename_len = strlen (r->filename);
2039 			size += info.filename_len + sizeof (int32_t);
2040 		} else {
2041 			info.filename_len = 0;
2042 			auto_app = FALSE;
2043 		}
2044 	} else
2045 		info.filename_len = 0;
2046 
2047 	DEBUG_PRINT (1, "Initial data size: %u", size);
2048 
2049 	if (size <= INITIAL_DATA_MAX_ALLOCA_SIZE)
2050 		ptr = str = alloca (size);
2051 	else
2052 		ptr = str = apr_pcalloc (r->pool, size);
2053 	*ptr++ = (char)PROTOCOL_VERSION; /* version. Keep in sync with ModMonoRequest. */
2054 	i = LE_FROM_INT (size) - (1 + sizeof (size)); /* Subtract the command the data size from the buffer size */
2055 	memcpy (ptr, &i, sizeof (i));
2056 	ptr += sizeof (int32_t);
2057 	ptr += write_string_to_buffer (ptr, 0, r->method, info.method_len);
2058 	if (s != NULL)
2059 		ptr += write_string_to_buffer (ptr, 0, (s->is_virtual ? s->server_hostname : NULL), info.server_hostname_len);
2060 	else
2061 		ptr += write_string_to_buffer (ptr, 0, NULL, 0);
2062 	ptr += write_string_to_buffer (ptr, 0, r->uri, info.uri_len);
2063 	ptr += write_string_to_buffer (ptr, 0, r->args, info.args_len);
2064 	ptr += write_string_to_buffer (ptr, 0, r->protocol, info.protocol_len);
2065 
2066 	ptr += write_string_to_buffer (ptr, 0, r->connection->local_ip, info.local_ip_len);
2067 	i = request_get_server_port (r);
2068 	i = LE_FROM_INT (i);
2069 	memcpy (ptr, &i, sizeof (i));
2070 	ptr += sizeof (int32_t);
2071 #if defined(APACHE24)
2072 	ptr += write_string_to_buffer (ptr, 0, r->connection->client_ip, info.remote_ip_len);
2073 #else
2074 	ptr += write_string_to_buffer (ptr, 0, r->connection->remote_ip, info.remote_ip_len);
2075 #endif
2076 	i = connection_get_remote_port (r->connection);
2077 	i = LE_FROM_INT (i);
2078 	memcpy (ptr, &i, sizeof (i));
2079 	ptr += sizeof (int32_t);
2080 	ptr += write_string_to_buffer (ptr, 0, info.remote_name, info.remote_name_len);
2081 	ptr += write_table_to_buffer (ptr, r->headers_in);
2082 	*ptr++ = auto_app;
2083 	if (auto_app != FALSE)
2084 		ptr += write_string_to_buffer (ptr, 0, r->filename, info.filename_len);
2085 
2086 	if (write_data (sock, str, size) != size)
2087 		return -1;
2088 
2089 	return 0;
2090 }
2091 
clear_uri_item(uri_item * list,int nitems,int32_t id)2092 inline static void clear_uri_item (uri_item* list, int nitems, int32_t id)
2093 {
2094 	int i;
2095 
2096 	for (i = 0; i < nitems; i++) {
2097 		if (list [i].id != id)
2098 			continue;
2099 		list [i].id = -1;
2100 		list [i].start_time = -1;
2101 		return;
2102 	}
2103 }
2104 
set_uri_item(uri_item * list,int nitems,request_rec * r,int32_t id)2105 inline static void set_uri_item (uri_item* list, int nitems, request_rec* r, int32_t id)
2106 {
2107 	int i;
2108 	int uri_len = 0;
2109 	int args_len;
2110 
2111 	for (i = 0; i < nitems; i++) {
2112 		if (list [i].id != -1)
2113 			continue;
2114 		list [i].id = id;
2115 		list [i].start_time = id == -1 ? -1 : time (NULL);
2116 		if (r->uri) {
2117 			uri_len = strlen (r->uri);
2118 			if (uri_len > URI_LIST_ITEM_SIZE)
2119 				uri_len = URI_LIST_ITEM_SIZE;
2120 			memcpy (list [i].uri, r->uri, uri_len);
2121 			list [i].uri [uri_len] = '\0';
2122 		}
2123 
2124 		if (r->args) {
2125 			args_len = strlen (r->args);
2126 			if (args_len + uri_len + 1 > URI_LIST_ITEM_SIZE)
2127 				args_len = URI_LIST_ITEM_SIZE - uri_len - 1;
2128 			if (args_len > 0) {
2129 				list [i].uri [uri_len] = '?';
2130 				memcpy (&list [i].uri [uri_len + 1], r->args, args_len);
2131 				list [i].uri [args_len + uri_len + 1] = '\0';
2132 			}
2133 		}
2134 		break;
2135 	}
2136 }
2137 
2138 static int
increment_active_requests(xsp_data * conf,request_rec * r,int32_t id)2139 increment_active_requests (xsp_data *conf, request_rec *r, int32_t id)
2140 {
2141 	/* This function tries to increment the counters that limit
2142 	 * the number of simultaenous requests being processed. It
2143 	 * assumes that the mutex is held when the function is called
2144 	 * and returns with the mutex still held, although it may
2145 	 * unlock and lock the mutex itself.
2146 	 */
2147 	apr_status_t rv;
2148 	int max_active_requests;
2149 	int max_waiting_requests;
2150 
2151 	/* Limit the number of concurrent requests. If no
2152 	 * limiting is in effect (or can't be done because
2153 	 * there is no dashboard), return the OK status.
2154 	 * Same test as in the decrement function and the
2155 	 * control panel. */
2156 
2157 	max_active_requests = (int)string_to_long (conf->max_active_requests, "MonoMaxActiveRequests", MAX_ACTIVE_REQUESTS);
2158 	max_waiting_requests = (int)string_to_long (conf->max_waiting_requests, "MonoMaxWaitingRequests", MAX_WAITING_REQUESTS);
2159 	// From here on, rv holds onto whether we still have
2160 	// the lock acquired, just in case some error ocurrs
2161 	// acquiring it during the loop.
2162 	rv = APR_SUCCESS;
2163 
2164 	/* If any limiting is in effect, and if there are the maximum
2165 	 * allowed concurrent requests, then we have to hold the request
2166 	 * for a bit of time. */
2167 	if (max_active_requests > 0 && conf->dashboard->active_requests >= max_active_requests) {
2168 		int retries = 20;
2169 
2170 		/* We need to wait until the active requests
2171 		 * go below the maximum. */
2172 
2173 		/* However, we won't keep more than max_waiting_req requests
2174 		 * waiting, which means the max number of active Apache
2175 		 * connections associated with this mod-mono-server
2176 		 * is max_active_req+max_waiting_req.
2177 		 */
2178 		if (conf->dashboard->waiting_requests >= max_waiting_requests) {
2179 			ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
2180 				      "Maximum number of concurrent mod_mono requests to %s reached (%d active, %d waiting). Request dropped.",
2181 				      conf->dashboard_lock_file, max_active_requests, max_waiting_requests);
2182 			return 0;
2183 		}
2184 
2185 		ap_log_error (APLOG_MARK, APLOG_INFO, STATUS_AND_SERVER,
2186 			      "Maximum number of concurrent mod_mono requests to %s reached (%d). Will wait and retry.",
2187 			      conf->dashboard_lock_file, max_active_requests);
2188 
2189 		conf->dashboard->waiting_requests++;
2190 		set_uri_item (conf->dashboard->waiting_uri_list, WAITING_URI_LIST_ITEM_COUNT, r, id);
2191 
2192 		while (retries-- > 0) {
2193 			// Release the lock, wait some time, and then re-acquire.
2194 			apr_global_mutex_unlock (conf->dashboard_mutex);
2195 			apr_sleep (500000); // 0.5 seconds
2196 			rv = apr_global_mutex_lock (conf->dashboard_mutex);
2197 			if (rv != APR_SUCCESS)
2198 				break;
2199 			// If the number of requests is low enough, we
2200 			// can stop waiting.
2201 			if (conf->dashboard->active_requests < max_active_requests)
2202 				break;
2203 		}
2204 
2205 		// Hopefully we haven't lost the lock, but if we have we still
2206 		// want to at least attempt to decrement the counter.
2207 		conf->dashboard->waiting_requests--;
2208 		clear_uri_item (conf->dashboard->waiting_uri_list, WAITING_URI_LIST_ITEM_COUNT, id);
2209 
2210 		// If we got to the end of the loop and still too
2211 		// many requests are going, stop processing the
2212 		// request.
2213 		if (rv == APR_SUCCESS && conf->dashboard->active_requests >= max_active_requests) {
2214 			ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
2215 				      "Maximum number (%d) of concurrent mod_mono requests to %s reached. Dropping request.",
2216 				      max_active_requests, conf->dashboard_lock_file);
2217 			return 0;
2218 		}
2219 	}
2220 
2221 	/* If we lost the lock during the loop, drop the request
2222 	 * because we don't want to decrement the counter later
2223 	 * since we couldn't increment it here. */
2224 	if (rv != APR_SUCCESS)
2225 		return 0;
2226 
2227 	conf->dashboard->active_requests++;
2228 	set_uri_item (conf->dashboard->active_uri_list, ACTIVE_URI_LIST_ITEM_COUNT, r, id);
2229 
2230 	return 1;
2231 }
2232 
2233 static void
decrement_active_requests(xsp_data * conf,int32_t id)2234 decrement_active_requests (xsp_data *conf, int32_t id)
2235 {
2236 	apr_status_t rv;
2237 
2238 	/* Check if limiting is in effect. Same test as in the
2239 	 * increment function and the control panel. */
2240 
2241 	rv = apr_global_mutex_lock (conf->dashboard_mutex);
2242 	// Since we incremented the counter, even if we can't
2243 	// get a lock, we had better attempt to decrement it.
2244 	conf->dashboard->active_requests--;
2245 
2246 	clear_uri_item (conf->dashboard->active_uri_list, ACTIVE_URI_LIST_ITEM_COUNT, id);
2247 	if (rv == APR_SUCCESS)
2248 		apr_global_mutex_unlock (conf->dashboard_mutex);
2249 }
2250 
2251 static int
mono_execute_request(request_rec * r,char auto_app)2252 mono_execute_request (request_rec *r, char auto_app)
2253 {
2254 	apr_socket_t *sock;
2255 	apr_status_t rv;
2256 	apr_status_t rv2;
2257 	int command = -1;
2258 	int result = FALSE;
2259 	apr_status_t input;
2260 	int status = 0;
2261 	module_cfg *config;
2262 	per_dir_config *dir_config = NULL;
2263 	int idx;
2264 	xsp_data *conf;
2265 	uint32_t connect_attempts;
2266 	uint32_t start_wait_time;
2267 	int retrying, was_starting;
2268 	int32_t id = -1;
2269 
2270 	config = ap_get_module_config (r->server->module_config, &mono_module);
2271 	DEBUG_PRINT (1, "config = 0x%lx", (unsigned long)config);
2272 	if (r->per_dir_config != NULL)
2273 		dir_config = ap_get_module_config (r->per_dir_config, &mono_module);
2274 
2275 	DEBUG_PRINT (1, "dir_config = 0x%lx", (unsigned long)dir_config);
2276 	if (dir_config != NULL && dir_config->alias != NULL)
2277 		idx = search_for_alias (dir_config->alias, config);
2278 	else
2279 		idx = search_for_alias (NULL, config);
2280 
2281 	DEBUG_PRINT (0, "idx = %d", idx);
2282 	if (idx < 0) {
2283 		DEBUG_PRINT (2, "Alias not found. Checking for auto-applications.");
2284 		if (config->auto_app)
2285 			idx = search_for_alias (GLOBAL_SERVER_NAME, config);
2286 
2287 		if (idx == -1) {
2288 			DEBUG_PRINT (2, "Global config not found. Finishing request.");
2289 			return HTTP_INTERNAL_SERVER_ERROR;
2290 		}
2291 	}
2292 
2293 	conf = &config->servers [idx];
2294 	ensure_dashboard_initialized (config, conf, pconf);
2295 	connect_attempts = (uint32_t)string_to_long (conf->start_attempts, "MonoXSPStartAttempts", START_ATTEMPTS);
2296 	start_wait_time = (uint32_t)string_to_long (conf->start_wait_time, "MonoXSPStartWaitTime", START_WAIT_TIME);
2297 	if (start_wait_time < 2)
2298 		start_wait_time = 2;
2299 
2300 	if (conf->dashboard_mutex && !conf->dashboard_mutex_initialized_in_child) {
2301 		/* Avoiding to call apr_global_mutex_child_init is a hack since in certain
2302 		 * conditions it may lead to apache deadlock. Since we don't know the exact cause
2303 		 * and usually it is not necessary to use the environment variable to work around
2304 		 * the apr's default locking mechanism, we skip the call in case the envvar is
2305 		 * used.
2306 		 */
2307 		if (!getenv ("MOD_MONO_LOCKING_MECHANISM")) {
2308 			rv = apr_global_mutex_child_init (&conf->dashboard_mutex, conf->dashboard_lock_file, pconf);
2309 		} else {
2310 			DEBUG_PRINT (1, "Skipping apr_global_mutex_child_init on '%s'", conf->dashboard_lock_file);
2311 			rv = APR_SUCCESS;
2312 		}
2313 		if (rv != APR_SUCCESS) {
2314 			ap_log_error (APLOG_MARK, APLOG_CRIT, STATCODE_AND_SERVER (rv),
2315 				      "Failed to initialize the dashboard mutex '%s' in child process",
2316 				      conf->dashboard_lock_file);
2317 			/* continue despite the error, the init doesn't have to be necessary on this platform */
2318 		} else
2319 			conf->dashboard_mutex_initialized_in_child = 1;
2320 	}
2321 
2322 	if (conf->dashboard_mutex && conf->dashboard) {
2323 		if (apr_global_mutex_lock (conf->dashboard_mutex) == APR_SUCCESS) {
2324 			int ok = conf->dashboard->accepting_requests;
2325 			if (ok) {
2326 				id = (int32_t)conf->dashboard->requests_counter++;
2327 				ok = increment_active_requests (conf, r, id);
2328 			}
2329 
2330 			apr_global_mutex_unlock (conf->dashboard_mutex);
2331 
2332 			if (!ok)
2333 				return HTTP_SERVICE_UNAVAILABLE;
2334 		}
2335 	}
2336 
2337 	if (conf->portability_level > PORTABILITY_MAX)
2338 		conf->portability_level = PORTABILITY_UNKNOWN;
2339 
2340 	mono_portability_helpers_init (&conf->portability_level, conf->iomap);
2341 
2342 	rv = -1; /* avoid a warning about uninitialized value */
2343 	retrying = connect_attempts;
2344 	was_starting = 0;
2345 	while (connect_attempts--) {
2346 		rv = setup_socket (&sock, conf, r->pool);
2347 		DEBUG_PRINT (2, "After setup_socket");
2348 		// Note that rv's value after the loop ends is important.
2349 		if (rv != APR_SUCCESS) {
2350 			if (rv != -1) {
2351 				decrement_active_requests (conf, id);
2352 				return HTTP_SERVICE_UNAVAILABLE;
2353 			}
2354 			DEBUG_PRINT (2, "No backend found, will start a new copy.");
2355 
2356 			if (conf->dashboard_mutex)
2357 				rv2 = apr_global_mutex_lock (conf->dashboard_mutex);
2358 			else
2359 				rv2 = APR_SUCCESS;
2360 			DEBUG_PRINT (1, "Acquiring the %s lock for backend start", conf->dashboard_lock_file);
2361 			if (rv2 != APR_SUCCESS) {
2362 				ap_log_error (APLOG_MARK, APLOG_CRIT, STATCODE_AND_SERVER (rv2),
2363 					      "Failed to acquire %s lock, cannot continue starting new process",
2364 					      conf->dashboard_lock_file);
2365 				decrement_active_requests (conf, id);
2366 				return HTTP_SERVICE_UNAVAILABLE;
2367 			}
2368 
2369 			if (conf->dashboard && conf->dashboard->starting) {
2370 				retrying--;
2371 				was_starting = 1;
2372 
2373 				rv2 = apr_global_mutex_unlock (conf->dashboard_mutex);
2374 				if (rv2 != APR_SUCCESS)
2375 					ap_log_error (APLOG_MARK, APLOG_ALERT, STATCODE_AND_SERVER (rv2),
2376 						      "Failed to release %s lock, the process may deadlock!",
2377 						      conf->dashboard_lock_file);
2378 				if (retrying < 0) {
2379 					ap_log_error (APLOG_MARK, APLOG_CRIT, STATUS_AND_SERVER,
2380 						      "Another process is attempting to start the backend. This request will fail.");
2381 					decrement_active_requests (conf, id);
2382 					return HTTP_SERVICE_UNAVAILABLE;
2383 				}
2384 				connect_attempts++; /* keep trying */
2385 				apr_sleep (apr_time_from_sec (start_wait_time));
2386 				continue;
2387 			}
2388 
2389 			if (was_starting) {
2390 				was_starting = 0;
2391 				connect_attempts++;
2392 
2393 				/* let's try one more time here */
2394 				apr_sleep (apr_time_from_sec (start_wait_time));
2395 				continue;
2396 			}
2397 
2398 			start_xsp (config, 0, conf->alias);
2399 
2400 			/* give some time for warm-up */
2401 			DEBUG_PRINT (2, "Started new backend, sleeping %us to let it configure", (unsigned)start_wait_time);
2402 			apr_sleep (apr_time_from_sec (start_wait_time));
2403 			if (conf->dashboard_mutex) {
2404 				rv2 = apr_global_mutex_unlock (conf->dashboard_mutex);
2405 				if (rv2 != APR_SUCCESS)
2406 					ap_log_error (APLOG_MARK, APLOG_ALERT, STATCODE_AND_SERVER (rv2),
2407 						      "Failed to release %s lock, the process may deadlock!",
2408 						      conf->dashboard_lock_file);
2409 
2410 			}
2411 		} else
2412 			break; /* connected */
2413 	}
2414 
2415 	if (rv != APR_SUCCESS) {
2416 		/* Failed to connect to mod-mono-server after several attempts. */
2417 		ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
2418 			      "Failed to connect to mod-mono-server after several attempts to spawn the process.");
2419 		decrement_active_requests (conf, id);
2420 		return HTTP_SERVICE_UNAVAILABLE;
2421 	}
2422 
2423 	DEBUG_PRINT (1, "Sending initial data");
2424 	if (send_initial_data (r, sock, auto_app) != 0) {
2425 		ap_log_error (APLOG_MARK, APLOG_ALERT, STATUS_AND_SERVER,
2426 			      "Failed to send initial data. %s", strerror (errno));
2427 		apr_socket_close (sock);
2428 		decrement_active_requests (conf, id);
2429 		return HTTP_SERVICE_UNAVAILABLE;
2430 	}
2431 
2432 	DEBUG_PRINT (1, "Loop");
2433 	do {
2434 		input = read_data (sock, (char *) &command, sizeof (int32_t));
2435 		if (input == sizeof (int32_t)) {
2436 			command = INT_FROM_LE (command);
2437 			result = do_command (command, sock, r, &status, conf);
2438 		}
2439 	} while (input == sizeof (int32_t) && result == TRUE);
2440 
2441 	apr_socket_close (sock);
2442 	if (input != sizeof (int32_t)) {
2443 		ap_log_error (APLOG_MARK, APLOG_ERR, STATUS_AND_SERVER,
2444 			      "Command stream corrupted, last command was %d", command);
2445 		status = HTTP_INTERNAL_SERVER_ERROR;
2446 	}
2447 
2448 	decrement_active_requests (conf, id);
2449 
2450 	if (conf->restart_mode > AUTORESTART_MODE_NONE) {
2451 		int do_restart = 0;
2452 
2453 		DEBUG_PRINT (2, "Auto-restart enabled for '%s', checking if restart required", conf->alias);
2454 		ensure_dashboard_initialized (config, conf, pconf);
2455 		if (!conf->dashboard_mutex || !conf->dashboard)
2456 			return status;
2457 
2458 		rv = apr_global_mutex_lock (conf->dashboard_mutex);
2459 
2460 		DEBUG_PRINT (1, "Acquired the %s lock for backend auto-restart check", conf->dashboard_lock_file);
2461 		if (rv != APR_SUCCESS) {
2462 			ap_log_error (APLOG_MARK, APLOG_CRIT, STATCODE_AND_SERVER (rv),
2463 				      "Failed to acquire %s lock, cannot continue auto-restart check",
2464 				      conf->dashboard_lock_file);
2465 			return status;
2466 		}
2467 
2468 		if (conf->restart_mode == AUTORESTART_MODE_REQUESTS) {
2469 			conf->dashboard->handled_requests++;
2470 			if (conf->dashboard->handled_requests > conf->restart_requests) {
2471 				DEBUG_PRINT (2, "More than %u requests served (%u), restart required",
2472 					     conf->restart_requests, conf->dashboard->handled_requests);
2473 				do_restart = 1;
2474 			} else {
2475 				DEBUG_PRINT (2, "Backend %s has %u requests left before auto-restart",
2476 					     conf->alias, conf->restart_requests - conf->dashboard->handled_requests);
2477 			}
2478 		} else if (conf->restart_mode == AUTORESTART_MODE_TIME) {
2479 			time_t now = time (NULL);
2480 			if (now - conf->dashboard->start_time > conf->restart_time) {
2481 				DEBUG_PRINT (2, "Backend uptime exceeded %us, restart required", conf->restart_time);
2482 				do_restart = 1;
2483 			} else {
2484 				DEBUG_PRINT (2, "Backend %s has %us left before auto-restart",
2485 					     conf->alias, (uint32_t)(conf->restart_time - (now - conf->dashboard->start_time)));
2486 			}
2487 		}
2488 
2489 		if (do_restart && !conf->dashboard->restart_issued) {
2490 			/* we just need to stop it, it will be started at the next request */
2491 			DEBUG_PRINT (2, "Stopping the backend '%s', it will be started at the next request",
2492 				     conf->alias);
2493 			ap_log_error (APLOG_MARK, APLOG_ALERT, STATCODE_AND_SERVER (rv),
2494 				      "Requesting termination of %s mod-mono-server for restart...",
2495 				      conf->alias);
2496 			conf->dashboard->restart_issued = 1;
2497 			terminate_xsp2 (r->server, conf->alias, 1, 1);
2498 		}
2499 
2500 		rv = apr_global_mutex_unlock (conf->dashboard_mutex);
2501 		if (rv != APR_SUCCESS)
2502 			ap_log_error (APLOG_MARK, APLOG_ALERT, STATCODE_AND_SERVER (rv),
2503 				      "Failed to release %s lock after auto-restart check, the process may deadlock!",
2504 				      conf->dashboard_lock_file);
2505 	}
2506 
2507 	DEBUG_PRINT (2, "Done. Status: %d", status);
2508 	return status;
2509 }
2510 
2511 /*
2512  * Compute real path directory and all the directories above that up to the first filesystem
2513  * change */
2514 
2515 static int
mono_handler(request_rec * r)2516 mono_handler (request_rec *r)
2517 {
2518 	module_cfg *config;
2519 	int retval;
2520 
2521 	if (r->handler != NULL && !strcmp (r->handler, "mono")) {
2522 		DEBUG_PRINT (0, "handler: %s", r->handler);
2523 		retval = mono_execute_request (r, FALSE);
2524 
2525 		return retval;
2526 	}
2527 
2528 	if (!r->content_type || strcmp (r->content_type, "application/x-asp-net"))
2529 		return DECLINED;
2530 
2531 	config = ap_get_module_config (r->server->module_config, &mono_module);
2532 	if (!config->auto_app)
2533 		return DECLINED;
2534 
2535 	/*
2536 	  if (FALSE == check_file_extension (r->filename))
2537 	  return DECLINED;
2538 	*/
2539 
2540 	/* Handle on-demand created applications */
2541 	return mono_execute_request (r, TRUE);
2542 }
2543 
2544 static apr_status_t
connect_to_backend(xsp_data * conf,int is_restart)2545 connect_to_backend (xsp_data *conf, int is_restart)
2546 {
2547 	apr_status_t rv;
2548 	apr_socket_t *socket;
2549 	char *termstr = "";
2550 
2551 	rv = setup_socket (&socket, conf, pconf);
2552 	if (rv == APR_SUCCESS) {
2553 		/* connected */
2554 		DEBUG_PRINT (2, "connected to backend of %s", conf->alias);
2555 		if (is_restart) {
2556 			write_data (socket, termstr, 1);
2557 			apr_socket_close (socket);
2558 			apr_sleep (apr_time_from_sec (2));
2559 			return APR_SUCCESS;
2560 		}
2561 		apr_socket_close (socket);
2562 		conf->status = FORK_SUCCEEDED;
2563 		return APR_SUCCESS;
2564 	} else {
2565 		apr_socket_close (socket);
2566 		return -1;
2567 	}
2568 }
2569 
2570 /*
2571  * It is assumed that this function is called with the dashboard mutex held. This is not required when calling it from the module
2572  * init handler, as there's only one process running at that time.
2573  */
2574 static void
start_xsp(module_cfg * config,int is_restart,char * alias)2575 start_xsp (module_cfg *config, int is_restart, char *alias)
2576 {
2577 	/* 'alias' may be NULL to start all XSPs */
2578 	xsp_data *xsp;
2579 	int i;
2580 	char do_fork = 0;
2581 
2582 	for (i = 0; i < config->nservers; i++) {
2583 		xsp = &config->servers [i];
2584 		DEBUG_PRINT (0, "config->servers [%u]->dashboard == 0x%lX", i, (unsigned long)xsp->dashboard);
2585 		if (xsp->run_xsp && !strcasecmp (xsp->run_xsp, "false"))
2586 			continue;
2587 
2588 		/* If alias isn't null, skip XSPs that don't have that alias. */
2589 		if (alias != NULL && strcmp (xsp->alias, alias))
2590 			continue;
2591 
2592 		if (IS_MASTER (xsp) && config->auto_app == FALSE)
2593 			continue;
2594 
2595 		DEBUG_PRINT (1, "xsp address 0x%lx, dashboard 0x%lx", (unsigned long)xsp, (unsigned long)xsp->dashboard);
2596 		if (!xsp->dashboard)
2597 			ensure_dashboard_initialized (config, xsp, pconf);
2598 
2599 		if (xsp->dashboard)
2600 			xsp->dashboard->starting = 1;
2601 		do_fork = 0;
2602 		if (connect_to_backend (xsp, is_restart) == APR_SUCCESS) {
2603 			if (is_restart) {
2604 				i--;
2605 				continue;
2606 			}
2607 		} else {
2608 			DEBUG_PRINT (2, "backend cannot be connected to.");
2609 			do_fork = 1;
2610 		}
2611 
2612 		if (!do_fork)
2613 			goto reset_starting;
2614 
2615 		DEBUG_PRINT (2, "Starting backend for alias %s", xsp->alias);
2616 		xsp->status = FORK_INPROCESS;
2617 		fork_mod_mono_server (pconf, xsp);
2618 		if (xsp->dashboard) {
2619 			xsp->dashboard->start_time = time (NULL);
2620 			xsp->dashboard->handled_requests = 0;
2621 			xsp->dashboard->restart_issued = 0;
2622 		}
2623 		xsp->status = FORK_SUCCEEDED;
2624 
2625 	  reset_starting:
2626 		if (xsp->dashboard)
2627 			xsp->dashboard->starting = 0;
2628 	}
2629 }
2630 
2631 static void
stop_xsp(xsp_data * conf)2632 stop_xsp (xsp_data *conf)
2633 {
2634 	apr_socket_t *sock;
2635 	apr_status_t rv;
2636 	char *termstr = "";
2637 
2638 	rv = setup_socket (&sock, conf, pconf);
2639 	if (rv == APR_SUCCESS) {
2640 		write_data (sock, termstr, 1);
2641 		apr_socket_close (sock);
2642 	}
2643 
2644 	if (conf->listen_port == NULL) {
2645 		char *fn = get_unix_socket_path (pconf, conf);
2646 		if (fn)
2647 			remove (fn); /* Don't bother checking error */
2648 	}
2649 }
2650 
2651 static apr_status_t
terminate_xsp2(void * data,char * alias,int for_restart,int lock_held)2652 terminate_xsp2 (void *data, char *alias, int for_restart, int lock_held)
2653 {
2654 	/* alias may be NULL to terminate all XSPs */
2655 	server_rec *server;
2656 	module_cfg *config;
2657 	apr_status_t rv;
2658 	xsp_data *xsp;
2659 	int i;
2660 	int release_lock = 0;
2661 
2662 	DEBUG_PRINT (0, "Terminate XSP");
2663 	server = (server_rec *) data;
2664 	config = ap_get_module_config (server->module_config, &mono_module);
2665 
2666 	for (i = 0; i < config->nservers; i++) {
2667 		xsp = &config->servers [i];
2668 		if (xsp->run_xsp && !strcasecmp (xsp->run_xsp, "false"))
2669 			continue;
2670 
2671 		/* If alias isn't null, skip XSPs that don't have that alias. */
2672 		if (alias != NULL && strcmp(xsp->alias, alias))
2673 			continue;
2674 
2675 		stop_xsp (xsp);
2676 
2677 		/* destroy the dashboard */
2678 		if (!for_restart && xsp->dashboard_shm) {
2679 			DEBUG_PRINT (0, "Destroying dashboard for %s", xsp->alias);
2680 			if (!lock_held && xsp->dashboard_mutex) {
2681 				rv = apr_global_mutex_lock (xsp->dashboard_mutex);
2682 				if (rv != APR_SUCCESS)
2683 					ap_log_error (APLOG_MARK, APLOG_ALERT, STATCODE_AND_SERVER (rv),
2684 						      "Failed to acquire dashboard lock before destroying the dashboard");
2685 				else
2686 					release_lock = 1;
2687 			}
2688 
2689 			// No need to detach before destroying, and in that case we must not.
2690 			// But should we detach instead of destroy if we weren't the creating
2691 			// process?
2692 			rv = apr_shm_destroy (xsp->dashboard_shm);
2693 			if (rv != APR_SUCCESS)
2694 				ap_log_error (APLOG_MARK, APLOG_WARNING, STATCODE_AND_SERVER (rv),
2695 					      "Failed to destroy the '%s' shared memory dashboard",
2696 					      xsp->dashboard_file);
2697 
2698 			if (release_lock) {
2699 				rv = apr_global_mutex_unlock (xsp->dashboard_mutex);
2700 				if (rv != APR_SUCCESS)
2701 					ap_log_error (APLOG_MARK, APLOG_WARNING, STATCODE_AND_SERVER (rv),
2702 						      "Failed to release dashboard lock after destroying the dashboard");
2703 			}
2704 
2705 			xsp->dashboard_shm = NULL;
2706 			xsp->dashboard = NULL;
2707 		}
2708 
2709 		if (!for_restart && xsp->dashboard_mutex) {
2710 			DEBUG_PRINT (0, "Destroying dasboard mutex %s", xsp->dashboard_lock_file);
2711 			rv = apr_global_mutex_destroy (xsp->dashboard_mutex);
2712 			if (rv != APR_SUCCESS)
2713 				ap_log_error (APLOG_MARK, APLOG_WARNING, STATCODE_AND_SERVER (rv),
2714 					      "Failed to destroy the dashboard mutex '%s'",
2715 					      xsp->dashboard_lock_file);
2716 			else
2717 				xsp->dashboard_mutex = NULL;
2718 		}
2719 
2720 		xsp->status = FORK_NONE;
2721 	}
2722 
2723 	apr_sleep (apr_time_from_sec (1));
2724 	return APR_SUCCESS;
2725 }
2726 
2727 static apr_status_t
terminate_xsp(void * data)2728 terminate_xsp (void *data)
2729 {
2730 	DEBUG_PRINT (0, "Cleaning up for shutdown");
2731 	return terminate_xsp2(data, NULL, 0, 0);
2732 }
2733 
2734 static void
set_accepting_requests(void * data,char * alias,int accepting_requests)2735 set_accepting_requests (void *data, char *alias, int accepting_requests)
2736 {
2737 	server_rec *server;
2738 	module_cfg *config;
2739 	xsp_data *xsp;
2740 	int i;
2741 
2742 	server = (server_rec *) data;
2743 	config = ap_get_module_config (server->module_config, &mono_module);
2744 
2745 	for (i = 0; i < config->nservers; i++) {
2746 		xsp = &config->servers [i];
2747 
2748 		/* If alias isn't null, skip XSPs that don't have that alias. */
2749 		if (alias != NULL && strcmp(xsp->alias, alias))
2750 			continue;
2751 
2752 		if (xsp->dashboard)
2753 			xsp->dashboard->accepting_requests = accepting_requests;
2754 	}
2755 }
2756 
2757 inline static void
send_uri_list(uri_item * list,int nitems,request_rec * r)2758 send_uri_list (uri_item* list, int nitems, request_rec *r)
2759 {
2760 	int i;
2761 	char *buffer;
2762 
2763 	request_send_response_string (r, "<dl>\n");
2764 	for (i = 0; i < nitems; i++) {
2765 		if (list [i].id != -1) {
2766 			buffer = apr_psprintf (r->pool, "<dd>%d %ds %s</dd>\n", list [i].id, (int)(time (NULL) - list [i].start_time), list [i].uri);
2767 			request_send_response_string (r, buffer);
2768 		}
2769 	}
2770 	request_send_response_string (r, "</dl></li>");
2771 }
2772 
2773 static int
mono_control_panel_handler(request_rec * r)2774 mono_control_panel_handler (request_rec *r)
2775 {
2776 	module_cfg *config;
2777 	apr_uri_t *uri;
2778 	xsp_data *xsp;
2779 	int i;
2780 	char *buffer;
2781 	apr_status_t rv;
2782 
2783 	if (strcmp (r->handler, "mono-ctrl"))
2784 		return DECLINED;
2785 
2786 	DEBUG_PRINT (2, "control panel handler: %s", r->handler);
2787 
2788 	config = ap_get_module_config (r->server->module_config, &mono_module);
2789 
2790 	set_response_header (r, "Content-Type", "text/html");
2791 
2792 	request_send_response_string (r, "<html><body>\n");
2793 	request_send_response_string (r, "<h1 style=\"text-align: center;\">mod_mono Control Panel</h1>\n");
2794 
2795 	uri = &r->parsed_uri;
2796 	if (!uri->query || !strcmp (uri->query, "")) {
2797 		/* No query string -> Emit links for configuration commands. */
2798 		request_send_response_string (r, "<ul>\n");
2799 		request_send_response_string (r, "<li><div>All Backends</div>\n<ul>\n");
2800 		request_send_response_string (r, "<li><a href=\"?restart=ALL\">Restart all mod-mono-server processes</a></li>\n");
2801 		request_send_response_string (r, "<li><a href=\"?pause=ALL\">Stop Accepting Requests</a></li>\n");
2802 		request_send_response_string (r, "<li><a href=\"?resume=ALL\">Resume Accepting Requests</a></li>\n");
2803 		request_send_response_string (r, "</ul></li>\n");
2804 
2805 		for (i = 0; i < config->nservers; i++) {
2806 			xsp = &config->servers [i];
2807 			if (xsp->run_xsp && !strcasecmp (xsp->run_xsp, "false"))
2808 				continue;
2809 
2810 			buffer = apr_psprintf (r->pool, "<li><div>%s</div><ul>\n", xsp->alias);
2811 			request_send_response_string(r, buffer);
2812 
2813 			buffer = apr_psprintf (r->pool, "<li><a href=\"?restart=%s\">Restart Server</a></li>\n", xsp->alias);
2814 			request_send_response_string(r, buffer);
2815 
2816 			ensure_dashboard_initialized (config, xsp, pconf);
2817 			if (xsp->dashboard_mutex && xsp->dashboard
2818 			    && apr_global_mutex_lock (xsp->dashboard_mutex) == APR_SUCCESS) {
2819 
2820 				if (xsp->dashboard->accepting_requests)
2821 					buffer = apr_psprintf (r->pool, "<li><a href=\"?pause=%s\">Stop Accepting Requests</a></li>\n", xsp->alias);
2822 				else
2823 					buffer = apr_psprintf (r->pool, "<li><a href=\"?resume=%s\">Resume Accepting Requests</a></li>\n", xsp->alias);
2824 				request_send_response_string(r, buffer);
2825 
2826 				if (xsp->restart_mode == AUTORESTART_MODE_REQUESTS) {
2827 					buffer = apr_psprintf (r->pool, "<li>%d requests served; limit: %d</li>\n",
2828 							       xsp->dashboard->handled_requests, xsp->restart_requests);
2829 					request_send_response_string(r, buffer);
2830 				} else if (xsp->restart_mode == AUTORESTART_MODE_TIME) {
2831 					buffer = apr_psprintf (r->pool, "<li>%ds time running; limit: %ds</li>\n",
2832 							       (int)(time(NULL) - xsp->dashboard->start_time), (int)xsp->restart_time);
2833 					request_send_response_string(r, buffer);
2834 				}
2835 
2836 				buffer = apr_psprintf (r->pool, "<li>%d requests currently being processed; limit: %s; total: %d\n",
2837 						       xsp->dashboard->active_requests,
2838 						       xsp->max_active_requests ? xsp->max_active_requests : "unlimited",
2839 						       xsp->dashboard->requests_counter);
2840 				request_send_response_string (r, buffer);
2841 				send_uri_list (xsp->dashboard->active_uri_list, ACTIVE_URI_LIST_ITEM_COUNT, r);
2842 
2843 				buffer = apr_psprintf (r->pool, "<li>%d requests currently waiting to be processed; limit: %s\n",
2844 						       xsp->dashboard->waiting_requests,
2845 						       xsp->max_waiting_requests ? xsp->max_waiting_requests : "unlimited");
2846 				request_send_response_string(r, buffer);
2847 				send_uri_list (xsp->dashboard->waiting_uri_list, WAITING_URI_LIST_ITEM_COUNT, r);
2848 
2849 				rv = apr_global_mutex_unlock (xsp->dashboard_mutex);
2850 				if (rv != APR_SUCCESS)
2851 					ap_log_error (APLOG_MARK, APLOG_ALERT, STATCODE_AND_SERVER (rv),
2852 						      "Failed to release %s lock after mono-ctrl output, the process may deadlock!",
2853 						      xsp->dashboard_lock_file);
2854 			}
2855 			request_send_response_string(r, "</ul></li>\n");
2856 		}
2857 		request_send_response_string (r, "</ul>\n");
2858 	} else {
2859 		if (uri->query && !strncmp (uri->query, "restart=", 8)) {
2860 			/* Restart the mod-mono-server processes */
2861 			char *alias = uri->query + 8; /* +8 == .Substring(8) */
2862 			if (!strcmp (alias, "ALL"))
2863 				alias = NULL;
2864 			set_accepting_requests (r->server, alias, 0);
2865 			terminate_xsp2 (r->server, alias, 1, 0);
2866 			start_xsp (config, 1, alias);
2867 			set_accepting_requests (r->server, alias, 1);
2868 			request_send_response_string (r, "<div style=\"text-align: center;\">mod-mono-server processes restarted.</div><br>\n");
2869 		} else if (uri->query && !strncmp (uri->query, "pause=", 6)) {
2870 			/* Stop accepting requests for this server */
2871 			char *alias = uri->query + 6; /* +6 == .Substring(6) */
2872 			if (!strcmp (alias, "ALL"))
2873 				alias = NULL;
2874 			set_accepting_requests (r->server, alias, 0);
2875 			request_send_response_string (r, "<div style=\"text-align: center;\">no longer accepting requests</div><br>\n");
2876 		} else if (uri->query && !strncmp (uri->query, "resume=", 7)) {
2877 			/* Resume accepting requests for this server */
2878 			char *alias = uri->query + 7; /* +7 == .Substring(7) */
2879 			if (!strcmp (alias, "ALL"))
2880 				alias = NULL;
2881 			set_accepting_requests (r->server, alias, 1);
2882 			request_send_response_string (r, "<div style=\"text-align: center;\">resumed accepting requests</div><br>\n");
2883 		} else {
2884 			/* Invalid command. */
2885 			request_send_response_string (r, "<div style=\"text-align: center;\">Invalid query string command.</div>\n");
2886 		}
2887 		request_send_response_string (r, "<div style=\"text-align: center;\"><a href=\"?\">Return to Control Panel</a></div>\n");
2888 	}
2889 
2890 	request_send_response_string(r, "</body></html>\n");
2891 
2892 	DEBUG_PRINT (2, "Done.");
2893 	return OK;
2894 }
2895 
2896 static int
mono_init_handler(apr_pool_t * p,apr_pool_t * plog,apr_pool_t * ptemp,server_rec * s)2897 mono_init_handler (apr_pool_t *p,
2898 		   apr_pool_t *plog,
2899 		   apr_pool_t *ptemp,
2900 		   server_rec *s)
2901 {
2902 	void *data;
2903 	const char *userdata_key = "mono_module_init";
2904 #if defined (APR_HAS_USER) && !defined (WIN32)
2905 	module_cfg *config;
2906 #endif
2907 
2908 	/*
2909 	 * mono_init_handler() will be called twice, and if it's a DSO then all
2910 	 * static data from the first call will be lost. Only set up our static
2911 	 * data on the second call.
2912 	 */
2913 	apr_pool_userdata_get (&data, userdata_key, s->process->pool);
2914 	if (!data) {
2915 		apr_pool_userdata_set ((const void *) 1, userdata_key,
2916 				       apr_pool_cleanup_null, s->process->pool);
2917 		return OK;
2918 	}
2919 
2920 	DEBUG_PRINT (0, "Initializing handler");
2921 	ap_add_version_component (p, "mod_mono/" VERSION);
2922 	pconf = s->process->pconf;
2923 	apr_pool_cleanup_register (pconf, s, terminate_xsp, apr_pool_cleanup_null);
2924 
2925 #if defined (APR_HAS_USER) && !defined (WIN32)
2926 	config = ap_get_module_config (s->module_config, &mono_module);
2927 	start_xsp (config, 0, NULL);
2928 #endif
2929 
2930 	return OK;
2931 }
2932 
2933 #if !defined (APR_HAS_USER) || defined (WIN32)
2934 void
mono_child_init(apr_pool_t * p,server_rec * s)2935 mono_child_init (apr_pool_t *p, server_rec *s)
2936 {
2937 	module_cfg *config;
2938 
2939 	DEBUG_PRINT (0, "Mono Child Init");
2940 	config = ap_get_module_config (s->module_config, &mono_module);
2941 	start_xsp (config, 0, NULL);
2942 }
2943 #endif
2944 
2945 static void
mono_register_hooks(apr_pool_t * p)2946 mono_register_hooks (apr_pool_t * p)
2947 {
2948 	ap_hook_handler (mono_handler, NULL, NULL, APR_HOOK_FIRST);
2949 	ap_hook_handler (mono_control_panel_handler, NULL, NULL, APR_HOOK_FIRST);
2950 	ap_hook_post_config (mono_init_handler, NULL, NULL, APR_HOOK_MIDDLE);
2951 #if !defined (APR_HAS_USER) || defined (WIN32)
2952 	ap_hook_child_init (mono_child_init, NULL, NULL, APR_HOOK_MIDDLE);
2953 #endif
2954 }
2955 
2956 static const command_rec mono_cmds [] = {
2957 	MAKE_CMD12 (MonoUnixUmask, umask_value,
2958 		    "Value of the file mode creation mask (see umask(2)) "
2959 		    "Default: 0077"
2960 	),
2961 	MAKE_CMD12 (MonoUnixSocket, filename,
2962 		    "Named pipe file name. Mutually exclusive with MonoListenPort. "
2963 		    "Default: /tmp/mod_mono_server"
2964 	),
2965 	MAKE_CMD12 (MonoListenPort, listen_port,
2966 		    "TCP port on which mod-mono-server should listen/is listening on. Mutually "
2967 		    "exclusive with MonoUnixSocket. "
2968 		    "When this options is specified, "
2969 		    "mod-mono-server and mod_mono will use a TCP socket for communication. "
2970 		    "Default: none"
2971 	),
2972 
2973 	MAKE_CMD12 (MonoListenAddress, listen_address,
2974 		    "IP address where mod-mono-server should listen/is listening on. Can "
2975 		    "only be used when MonoListenPort is specified."
2976 		    "Default: \"127.0.0.1\""
2977 	),
2978 	MAKE_CMD12 (MonoListenBacklog, listen_backlog,
2979 		    "The socket listen backlog to set on mod-mono-server."
2980 		    "Default: 500"
2981 	),
2982 	MAKE_CMD12 (MonoMinThreads, minthreads,
2983 		    "The minimum number of threads the thread pool allocates in mod-mono-server."
2984 		    "Default: (mono runtime default)"
2985 	),
2986 
2987 	MAKE_CMD12 (MonoRunXSP, run_xsp,
2988 		    "It can be False or True. If it is True, asks the module to "
2989 		    "start mod-mono-server.exe if it's not already there. Default: True"
2990 	),
2991 
2992 	MAKE_CMD12 (MonoXSPStartAttempts, start_attempts,
2993 		    "Number of attempts to make when a backend is found to be dead. "
2994 		    "Cannot be less than 0. Default: 3"),
2995 
2996 	MAKE_CMD12 (MonoXSPStartWaitTime, start_wait_time,
2997 		    "Number of seconds to wait for the backend to come up. Cannot be less "
2998 		    "than 2. Default: 2"),
2999 
3000 	MAKE_CMD12 (MonoExecutablePath, executable_path,
3001 		    "(Obsolete) If MonoRunXSP is True, this is the full path where mono is located. "
3002 		    "Default: /usr/bin/mono"
3003 	),
3004 
3005 	MAKE_CMD12 (MonoPath, path,
3006 		    "If MonoRunXSP is True, this will be the content of MONO_PATH "
3007 		    "environment variable. Default: \"\""
3008 	),
3009 
3010 	MAKE_CMD12 (MonoServerPath, server_path,
3011 		    "If MonoRunXSP is True, this is the full path to the mod-mono-server script. "
3012 		    "Default: " MODMONO_SERVER_PATH
3013 	),
3014 
3015 	MAKE_CMD12 (MonoTargetFramework, target_framework,
3016 		    "If MonoRunXSP is True, this option selects the .NET framework version to use. This "
3017 		    "affects the backend that is started to service the requests. The MonoServerPath option "
3018 		    "takes precedence over this setting. Default: " MONO_DEFAULT_FRAMEWORK
3019 	),
3020 
3021 	MAKE_CMD12 (MonoApplications, applications,
3022 		    "Comma separated list with virtual directories and real directories. "
3023 		    "One ASP.NET application will be created for each pair. Default: \"\" "
3024 	),
3025 
3026 	MAKE_CMD12 (MonoWapiDir, wapidir,
3027 		    "The directory where mono runtime will create the '.wapi' directory "
3028 		    "used to emulate windows I/O. It's used to set MONO_SHARED_DIR. "
3029 		    "Default value: \"/tmp\""
3030 	),
3031 
3032 	MAKE_CMD12 (MonoDocumentRootDir, document_root,
3033 		    "The argument passed in --root argument to mod-mono-server. "
3034 		    "This tells mod-mono-server to change the directory to the "
3035 		    "value specified before doing anything else. Default: /"
3036 	),
3037 
3038 	MAKE_CMD12 (MonoApplicationsConfigFile, appconfig_file,
3039 		    "Adds application definitions from the  XML configuration file. "
3040 		    "See Appendix C for details on the file format. "
3041 		    "Default value: \"\""
3042 	),
3043 
3044 	MAKE_CMD12 (MonoApplicationsConfigDir, appconfig_dir,
3045 		    "Adds application definitions from all XML files found in the "
3046 		    "specified directory DIR. Files must have '.webapp' extension. "
3047 		    "Default value: \"\""
3048 	),
3049 
3050 #ifndef HAVE_SETRLIMIT
3051 	MAKE_CMD12 (MonoMaxMemory, max_memory,
3052 		    "If MonoRunXSP is True, the maximum size of the process's data segment "
3053 		    "(data size) in bytes allowed for the spawned mono process. It will "
3054 		    "be restarted when the limit is reached.  .. but your system doesn't "
3055 		    "support setrlimit. Sorry, this feature will not be available. "
3056 		    "Default value: system default"
3057 	),
3058 #else
3059 	MAKE_CMD12 (MonoMaxMemory, max_memory,
3060 		    "If MonoRunXSP is True, the maximum size of the process's data "
3061 		    "segment (data size) in bytes allowed "
3062 		    "for the spawned mono process. It will be restarted when the limit "
3063 		    "is reached."
3064 		    " Default value: system default"
3065 	),
3066 #endif
3067 
3068 #ifndef HAVE_SETRLIMIT
3069 	MAKE_CMD12 (MonoMaxCPUTime, max_cpu_time,
3070 		    "If MonoRunXSP is True, CPU time limit in seconds allowed for "
3071 		    "the spawned mono process. Beyond that, it will be restarted."
3072 		    ".. but your system doesn't support setrlimit. Sorry, this feature "
3073 		    "will not be available."
3074 		    " Default value: system default"
3075 	),
3076 #else
3077 	MAKE_CMD12 (MonoMaxCPUTime, max_cpu_time,
3078 		    "If MonoRunXSP is True, CPU time limit in seconds allowed for "
3079 		    "the spawned mono process. Beyond that, it will be restarted."
3080 		    " Default value: system default"
3081 	),
3082 #endif
3083 	MAKE_CMD12 (MonoDebug, debug,
3084 		    "If MonoDebug is true, mono will be run in debug mode."
3085 		    " Default value: False"
3086 	),
3087 
3088 	MAKE_CMD12 (MonoSetEnv, env_vars,
3089 		    "A string of name=value pairs separated by semicolons."
3090 		    "For each pair, setenv(name, value) is called before running "
3091 		    "mod-mono-server."
3092 		    " Default value: Default: \"\""
3093 	),
3094 
3095 	MAKE_CMD12 (MonoIOMAP, iomap,
3096 		    "A string with format the same as the MONO_IOMAP variable (see mod_mono (8))."
3097 		    " Default value: none"),
3098 
3099 	MAKE_CMD_ITERATE2 (AddMonoApplications, applications,
3100 			   "Appends an application."
3101 	),
3102 
3103 	MAKE_CMD_ACCESS (MonoSetServerAlias, set_alias,
3104 			 "Uses the server named by this alias inside this Directory/Location."
3105 	),
3106 	MAKE_CMD1 (MonoAutoApplication, set_auto_application,
3107 		   "Disables automatic creation of applications. "
3108 		   "Default value: 'Disabled' if there's any other application for the server. "
3109 		   "'Enabled' otherwise."
3110 	),
3111 	MAKE_CMD12 (MonoAutoRestartMode, restart_mode,
3112 		    "Set the auto-restart mode for the backend(s). Three modes are available: "
3113 		    "None - do not auto-restart, Requests - restart after a configured number of "
3114 		    "requests served, Time - restart after the backend has been up for the specified "
3115 		    "period of time. Default value: None"),
3116 	MAKE_CMD12 (MonoAutoRestartRequests, restart_requests,
3117 		    "Number of requests for a backend to serve before auto-restarting. The value here "
3118 		    "is taken into account only when MonoAutoRestartMode is set to Requests. "
3119 		    "Default value: 10000"),
3120 	MAKE_CMD12 (MonoAutoRestartTime, restart_time,
3121 		    "Time after which the backend should be auto-restarted. The time format is: "
3122 		    "DD[:HH[:MM[:SS]]]. Default value: 00:12:00:00"),
3123 
3124 	MAKE_CMD12 (MonoMaxActiveRequests, max_active_requests,
3125 		    "The maximum number of concurrent requests mod_mono will pass off to the ASP.NET backend. "
3126 		    "Set to zero to turn off the limit. Default value: 150"),
3127 	MAKE_CMD12 (MonoMaxWaitingRequests, max_waiting_requests,
3128 		    "The maximum number of concurrent requests mod_mono will hold while the ASP.NET backend is busy "
3129 		    "with the maximum number of requests specified by MonoMaxActiveRequests. "
3130 		    "Requests that can't be processed or held are dropped with Service Unavailable."
3131 		    "Default value: 150"),
3132 	MAKE_CMD12 (MonoCheckHiddenFiles, hidden,
3133 		    "Do not protect hidden files/directories from being accessed by clients. Hidden files/directories are those with "
3134 		    "Hidden attribute on Windows and whose name starts with a dot on Unix. Any file/directory below a hidden directory "
3135 		    "is inacessible. This option turns the default behavior of protecting such locations off. If your application "
3136 		    "does not contain any hidden files/directories, you might want to use this option as the checking process has a "
3137 		    "per-request cost. Accepts a boolean value - 'true' or 'false'"
3138 		    "Default value: true. "
3139 		    "AppSettings key name: MonoServerCheckHiddenFiles. "),
3140 	{ NULL }
3141 };
3142 
3143 module AP_MODULE_DECLARE_DATA mono_module = {
3144 	STANDARD20_MODULE_STUFF,
3145 	create_dir_config,		/* dir config creater */
3146 	NULL,				/* dir merger --- default is to override */
3147 	create_mono_server_config,	/* server config */
3148 	merge_config,			/* merge server configs */
3149 	mono_cmds,			/* command apr_table_t */
3150 	mono_register_hooks		/* register hooks */
3151 };
3152