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