1 #include <uwsgi.h>
2 #include <mono/jit/jit.h>
3 #include <mono/metadata/mono-config.h>
4 #include <mono/metadata/assembly.h>
5 #include <mono/metadata/threads.h>
6 #include <mono/metadata/debug-helpers.h>
7 #include <mono/metadata/mono-gc.h>
8 #include <mono/metadata/exception.h>
9 
10 /*
11 
12 	Mono ASP.NET plugin
13 
14 	there are various mode of operation (based on security needs)
15 
16 	(the --mono-key maps to DOCUMENT_ROOT by default)
17 
18 	1) static application
19 
20 		--mono-app <directory>
21 
22 		will create an ApplicationHost on the specified <directory>
23 
24 		the --mono-key will be searched for that directory
25 
26 		the application runs on the main domain
27 
28 	2) dynamic applications
29 
30 		the app is created on demand using the specified key as the physicalDirectory
31 
32 
33 	TODO:
34 		allows mounting apps under subpaths (currently all is mapped to "/")
35 
36 	Thanks to:
37 		Robert Jordan for helping me understanding the ApplicationHost internals
38 
39 */
40 
41 extern struct uwsgi_server uwsgi;
42 struct uwsgi_plugin mono_plugin;
43 
44 struct uwsgi_mono {
45 
46 	char *config;
47 	char *version;
48 	char *assembly_name ;
49 
50 	struct uwsgi_string_list *key;
51 	struct uwsgi_string_list *index;
52 
53 	// GC frequency
54 	uint64_t gc_freq;
55 
56 	// a lock for dynamic apps
57         pthread_mutex_t lock_loader;
58 
59 	MonoDomain *main_domain;
60 	MonoMethod *create_application_host;
61 
62 	MonoClass *application_class;
63 	MonoClass *api_class;
64 
65 	MonoClass *byte_class;
66 
67 	MonoClassField *filepath;
68 
69 	// thunk
70 	void (*process_request)(MonoObject *, MonoException **);
71 
72 	struct uwsgi_string_list *app;
73 	struct uwsgi_string_list *exec;
74 
75 } umono;
76 
77 struct uwsgi_option uwsgi_mono_options[] = {
78 
79         {"mono-app", required_argument, 0, "load a Mono asp.net app from the specified directory", uwsgi_opt_add_string_list, &umono.app, 0},
80         {"mono-gc-freq", required_argument, 0, "run the Mono GC every <n> requests (default: run after every request)", uwsgi_opt_set_64bit, &umono.gc_freq, 0},
81         {"mono-key", required_argument, 0, "select the ApplicationHost based on the specified CGI var", uwsgi_opt_add_string_list, &umono.key, 0},
82         {"mono-version", required_argument, 0, "set the Mono jit version", uwsgi_opt_set_str, &umono.version, 0},
83         {"mono-config", required_argument, 0, "set the Mono config file", uwsgi_opt_set_str, &umono.config, 0},
84         {"mono-assembly", required_argument, 0, "load the specified main assembly (default: uwsgi.dll)", uwsgi_opt_set_str, &umono.assembly_name, 0},
85         {"mono-exec", required_argument, 0, "exec the specified assembly just before app loading", uwsgi_opt_add_string_list, &umono.exec, 0},
86         {"mono-index", required_argument, 0, "add an asp.net index file", uwsgi_opt_add_string_list, &umono.index, 0},
87 	{0, 0, 0, 0, 0, 0, 0},
88 };
89 
uwsgi_mono_method_GetFilePath(MonoObject * this)90 static MonoString *uwsgi_mono_method_GetFilePath(MonoObject *this) {
91 	MonoString *ret = NULL;
92 	// cache it !!!
93 	MonoObject *filepath = mono_field_get_value_object(mono_domain_get(), umono.filepath, this);
94 	if (filepath) {
95 		return (MonoString *) filepath;
96 	}
97 	struct wsgi_request *wsgi_req = current_wsgi_req();
98 	struct uwsgi_app *app = &uwsgi_apps[wsgi_req->app_id];
99 	char *path = uwsgi_concat3n(app->interpreter, strlen(app->interpreter), "/", 1, wsgi_req->path_info, wsgi_req->path_info_len);
100 	size_t path_len = strlen(app->interpreter) + 1 + wsgi_req->path_info_len;
101 
102 	if (!uwsgi_file_exists(path)) {
103 		free(path);
104 		goto simple;
105 	}
106 
107 	if (uwsgi_is_dir(path)) {
108 		struct uwsgi_string_list *usl = umono.index;
109 		while(usl) {
110 			char *index = uwsgi_concat3n(path, path_len, "/", 1 , usl->value, usl->len);
111 			if (uwsgi_file_exists(index)) {
112 				ret = mono_string_new(mono_domain_get(), index + strlen(app->interpreter));
113 				free(path);
114 				free(index);
115 				mono_field_set_value(this, umono.filepath, ret);
116 				return ret;
117 			}
118 			free(index);
119 			usl = usl->next;
120 		}
121 	}
122 	free(path);
123 simple:
124 	ret = mono_string_new_len(mono_domain_get(), wsgi_req->path_info, wsgi_req->path_info_len);
125 	mono_field_set_value(this, umono.filepath, ret);
126 	return ret;
127 }
128 
uwsgi_mono_method_GetUriPath(MonoObject * this)129 static MonoString *uwsgi_mono_method_GetUriPath(MonoObject *this) {
130 	return uwsgi_mono_method_GetFilePath(this);
131 }
132 
uwsgi_mono_method_MapPath(MonoObject * this,MonoString * virtualPath)133 static MonoString *uwsgi_mono_method_MapPath(MonoObject *this, MonoString *virtualPath) {
134 	struct wsgi_request *wsgi_req = current_wsgi_req();
135 	struct uwsgi_app *app = &uwsgi_apps[wsgi_req->app_id];
136 	char *path = uwsgi_concat3n(app->interpreter, strlen(app->interpreter), "/", 1, mono_string_to_utf8(virtualPath), mono_string_length(virtualPath));
137 	MonoString *ret = mono_string_new_len(mono_domain_get(), path, strlen(path));
138 	free(path);
139 	return ret;
140 }
141 
uwsgi_mono_method_GetQueryString(MonoObject * this)142 static MonoString *uwsgi_mono_method_GetQueryString(MonoObject *this) {
143 	struct wsgi_request *wsgi_req = current_wsgi_req();
144 	return mono_string_new_len(mono_domain_get(), wsgi_req->query_string, wsgi_req->query_string_len);
145 }
146 
uwsgi_mono_method_GetHttpVerbName(MonoObject * this)147 static MonoString *uwsgi_mono_method_GetHttpVerbName(MonoObject *this) {
148 	struct wsgi_request *wsgi_req = current_wsgi_req();
149 	return mono_string_new_len(mono_domain_get(), wsgi_req->method, wsgi_req->method_len);
150 }
151 
uwsgi_mono_method_GetRawUrl(MonoObject * this)152 static MonoString *uwsgi_mono_method_GetRawUrl(MonoObject *this) {
153 	struct wsgi_request *wsgi_req = current_wsgi_req();
154 	return mono_string_new_len(mono_domain_get(), wsgi_req->uri, wsgi_req->uri_len);
155 }
156 
uwsgi_mono_method_GetHttpVersion(MonoObject * this)157 static MonoString *uwsgi_mono_method_GetHttpVersion(MonoObject *this) {
158 	struct wsgi_request *wsgi_req = current_wsgi_req();
159 	return mono_string_new_len(mono_domain_get(), wsgi_req->protocol, wsgi_req->protocol_len);
160 }
161 
uwsgi_mono_method_GetRemoteAddress(MonoObject * this)162 static MonoString *uwsgi_mono_method_GetRemoteAddress(MonoObject *this) {
163 	struct wsgi_request *wsgi_req = current_wsgi_req();
164 	return mono_string_new_len(mono_domain_get(), wsgi_req->remote_addr, wsgi_req->remote_addr_len);
165 }
166 
uwsgi_mono_method_SendStatus(MonoObject * this,int code,MonoString * msg)167 static void uwsgi_mono_method_SendStatus(MonoObject *this, int code, MonoString *msg) {
168 	struct wsgi_request *wsgi_req = current_wsgi_req();
169 	char status_code[4];
170 	uwsgi_num2str2n(code, status_code, 4);
171 	char *status_line = uwsgi_concat3n(status_code, 3, " ", 1, mono_string_to_utf8(msg), mono_string_length(msg));
172 	uwsgi_response_prepare_headers(wsgi_req, status_line, 4 + mono_string_length(msg));
173 	free(status_line);
174 }
175 
uwsgi_mono_method_SendUnknownResponseHeader(MonoObject * this,MonoString * key,MonoString * value)176 static void uwsgi_mono_method_SendUnknownResponseHeader(MonoObject *this, MonoString *key, MonoString *value) {
177 	struct wsgi_request *wsgi_req = current_wsgi_req();
178 	uwsgi_response_add_header(wsgi_req, mono_string_to_utf8(key), mono_string_length(key), mono_string_to_utf8(value), mono_string_length(value));
179 }
180 
uwsgi_mono_method_SendResponseFromMemory(MonoObject * this,MonoArray * byteArray,int len)181 static void uwsgi_mono_method_SendResponseFromMemory(MonoObject *this, MonoArray *byteArray, int len) {
182 	struct wsgi_request *wsgi_req = current_wsgi_req();
183 	uwsgi_response_write_body_do(wsgi_req, mono_array_addr(byteArray, char, 0), len);
184 }
185 
uwsgi_mono_method_FlushResponse(MonoObject * this,int is_final)186 static void uwsgi_mono_method_FlushResponse(MonoObject *this, int is_final) {
187 	struct wsgi_request *wsgi_req = current_wsgi_req();
188 	uwsgi_response_write_body_do(wsgi_req, "", 0);
189 }
190 
uwsgi_mono_method_SendResponseFromFd(MonoObject * this,int fd,long offset,long len)191 static void uwsgi_mono_method_SendResponseFromFd(MonoObject *this, int fd, long offset, long len) {
192 	struct wsgi_request *wsgi_req = current_wsgi_req();
193 	wsgi_req->sendfile_fd = fd;
194 	if (fd >= 0) {
195         	uwsgi_response_sendfile_do(wsgi_req, fd, offset, len);
196 	}
197 	wsgi_req->sendfile_fd = -1;
198 }
199 
uwsgi_mono_method_SendResponseFromFile(MonoObject * this,MonoString * filename,long offset,long len)200 static void uwsgi_mono_method_SendResponseFromFile(MonoObject *this, MonoString *filename, long offset, long len) {
201 	struct wsgi_request *wsgi_req = current_wsgi_req();
202 	int fd = open(mono_string_to_utf8(filename), O_RDONLY);
203 	if (fd >= 0) {
204         	uwsgi_response_sendfile_do(wsgi_req, fd, offset, len);
205 	}
206 }
207 
uwsgi_mono_method_GetHeaderByName(MonoObject * this,MonoString * key)208 static MonoString *uwsgi_mono_method_GetHeaderByName(MonoObject *this, MonoString *key) {
209 	struct wsgi_request *wsgi_req = current_wsgi_req();
210 	uint16_t rlen = 0;
211 	char *value = uwsgi_get_header(wsgi_req, mono_string_to_utf8(key), mono_string_length(key), &rlen);
212 	if (value) {
213 		return mono_string_new_len(mono_domain_get(), value, rlen);
214 	}
215 	return mono_string_new(mono_domain_get(), "");
216 }
217 
uwsgi_mono_method_GetServerVariable(MonoObject * this,MonoString * key)218 static MonoString *uwsgi_mono_method_GetServerVariable(MonoObject *this, MonoString *key) {
219 	struct wsgi_request *wsgi_req = current_wsgi_req();
220 	uint16_t rlen = 0;
221 	char *value = uwsgi_get_var(wsgi_req, mono_string_to_utf8(key), mono_string_length(key), &rlen);
222 	if (value) {
223 		return mono_string_new_len(mono_domain_get(), value, rlen);
224 	}
225 	return mono_string_new(mono_domain_get(), "");
226 }
227 
uwsgi_mono_method_ReadEntityBody(MonoObject * this,MonoArray * byteArray,int len)228 static int uwsgi_mono_method_ReadEntityBody(MonoObject *this, MonoArray *byteArray, int len) {
229 	struct wsgi_request *wsgi_req = current_wsgi_req();
230 	char *buf = mono_array_addr(byteArray, char, 0);
231 	ssize_t rlen = 0;
232 	char *chunk = uwsgi_request_body_read(wsgi_req, len, &rlen);
233 	if (chunk == uwsgi.empty) {
234 		return 0;
235 	}
236 	if (chunk) {
237 		memcpy(buf, chunk, rlen);
238 		return rlen;
239 	}
240 	return -1;
241 }
242 
uwsgi_mono_method_GetTotalEntityBodyLength(MonoObject * this)243 static int uwsgi_mono_method_GetTotalEntityBodyLength(MonoObject *this) {
244 	struct wsgi_request *wsgi_req = current_wsgi_req();
245 	return wsgi_req->post_cl;
246 }
247 
uwsgi_mono_method_api_RegisterSignal(int signum,MonoString * target,MonoObject * func)248 static void uwsgi_mono_method_api_RegisterSignal(int signum, MonoString *target, MonoObject *func) {
249 	mono_gchandle_new(func, 1);
250 	if (uwsgi_register_signal(signum, mono_string_to_utf8(target), func, mono_plugin.modifier1)) {
251 		mono_raise_exception(mono_get_exception_invalid_operation("unable to register signal handler"));
252 	}
253 }
254 
uwsgi_mono_method_api_Signal(int signum)255 static void uwsgi_mono_method_api_Signal(int signum) {
256 	uwsgi_signal_send(uwsgi.signal_socket, signum);
257 }
258 
uwsgi_mono_method_api_WorkerId()259 static int uwsgi_mono_method_api_WorkerId() {
260 	return uwsgi.mywid;
261 }
262 
uwsgi_mono_method_api_CacheGet(MonoString * key,MonoString * cache)263 static MonoArray *uwsgi_mono_method_api_CacheGet(MonoString *key, MonoString *cache) {
264 	char *c_key = mono_string_to_utf8(key);
265 	uint16_t c_keylen = mono_string_length(key);
266 	char *c_cache = NULL;
267 	if (cache) {
268 		c_cache = mono_string_to_utf8(cache);
269 	}
270 	uint64_t vallen = 0 ;
271 	char *value = uwsgi_cache_magic_get(c_key, c_keylen, &vallen, NULL, c_cache);
272         if (value) {
273 		MonoArray *ret = mono_array_new(mono_domain_get(), umono.byte_class, vallen);
274 		char *buf = mono_array_addr(ret, char, 0);
275 		memcpy(buf, value, vallen);
276 		free(value);
277                 return ret;
278         }
279 
280 	return NULL;
281 }
282 
uwsgi_mono_add_internal_calls()283 static void uwsgi_mono_add_internal_calls() {
284 	// uWSGIRequest
285 	mono_add_internal_call("uwsgi.uWSGIRequest::SendResponseFromMemory", uwsgi_mono_method_SendResponseFromMemory);
286 	mono_add_internal_call("uwsgi.uWSGIRequest::SendStatus", uwsgi_mono_method_SendStatus);
287 	mono_add_internal_call("uwsgi.uWSGIRequest::SendUnknownResponseHeader", uwsgi_mono_method_SendUnknownResponseHeader);
288 	mono_add_internal_call("uwsgi.uWSGIRequest::FlushResponse", uwsgi_mono_method_FlushResponse);
289 	mono_add_internal_call("uwsgi.uWSGIRequest::GetQueryString", uwsgi_mono_method_GetQueryString);
290 	mono_add_internal_call("uwsgi.uWSGIRequest::MapPath", uwsgi_mono_method_MapPath);
291 	mono_add_internal_call("uwsgi.uWSGIRequest::GetHttpVerbName", uwsgi_mono_method_GetHttpVerbName);
292 	mono_add_internal_call("uwsgi.uWSGIRequest::GetRawUrl", uwsgi_mono_method_GetRawUrl);
293 	mono_add_internal_call("uwsgi.uWSGIRequest::GetFilePath", uwsgi_mono_method_GetFilePath);
294 	mono_add_internal_call("uwsgi.uWSGIRequest::GetUriPath", uwsgi_mono_method_GetUriPath);
295 	mono_add_internal_call("uwsgi.uWSGIRequest::SendResponseFromFile", uwsgi_mono_method_SendResponseFromFile);
296 	mono_add_internal_call("uwsgi.uWSGIRequest::SendResponseFromFd", uwsgi_mono_method_SendResponseFromFd);
297 	mono_add_internal_call("uwsgi.uWSGIRequest::GetHeaderByName", uwsgi_mono_method_GetHeaderByName);
298 	mono_add_internal_call("uwsgi.uWSGIRequest::ReadEntityBody", uwsgi_mono_method_ReadEntityBody);
299 	mono_add_internal_call("uwsgi.uWSGIRequest::GetTotalEntityBodyLength", uwsgi_mono_method_GetTotalEntityBodyLength);
300 	mono_add_internal_call("uwsgi.uWSGIRequest::GetHttpVersion", uwsgi_mono_method_GetHttpVersion);
301 	mono_add_internal_call("uwsgi.uWSGIRequest::GetServerVariable", uwsgi_mono_method_GetServerVariable);
302 	mono_add_internal_call("uwsgi.uWSGIRequest::GetRemoteAddress", uwsgi_mono_method_GetRemoteAddress);
303 
304 	// api
305 	mono_add_internal_call("uwsgi.api::Signal", uwsgi_mono_method_api_Signal);
306 	mono_add_internal_call("uwsgi.api::WorkerId", uwsgi_mono_method_api_WorkerId);
307 	mono_add_internal_call("uwsgi.api::RegisterSignal", uwsgi_mono_method_api_RegisterSignal);
308 	mono_add_internal_call("uwsgi.api::CacheGet", uwsgi_mono_method_api_CacheGet);
309 }
310 
uwsgi_mono_init()311 static int uwsgi_mono_init() {
312 
313 	if (!umono.version) {
314 		umono.version = "v4.0.30319";
315 	}
316 
317 	if (!umono.assembly_name) {
318 		umono.assembly_name = "uwsgi.dll";
319 	}
320 
321 	if (!umono.gc_freq) {
322 		umono.gc_freq = 1;
323 	}
324 
325 	return 0;
326 }
327 
328 
uwsgi_mono_create_jit()329 static void uwsgi_mono_create_jit() {
330 
331 
332 	mono_config_parse(umono.config);
333 
334 	umono.main_domain = mono_jit_init_version("uwsgi", umono.version);
335 	if (!umono.main_domain) {
336 		uwsgi_log("unable to initialize Mono JIT\n");
337 		exit(1);
338 	}
339 
340 	uwsgi_log("Mono JIT initialized on worker %d with version %s\n", uwsgi.mywid, umono.version);
341 
342 	MonoAssembly *assembly = mono_domain_assembly_open(umono.main_domain, umono.assembly_name);
343 	if (!assembly) {
344 		uwsgi_log("%s not found trying in global gac...\n", umono.assembly_name);
345 		assembly = mono_assembly_load_with_partial_name(umono.assembly_name, NULL);
346 		if (!assembly) {
347 			if (!strcmp("uwsgi.dll", umono.assembly_name)) {
348 				assembly = mono_assembly_load_with_partial_name("uwsgi", NULL);
349 			}
350 		}
351 	}
352 
353 	if (!assembly) {
354 		uwsgi_log("unable to load \"%s\" in the Mono domain\n", umono.assembly_name);
355 		exit(1);
356 	}
357 
358 	uwsgi_mono_add_internal_calls();
359 
360 	MonoImage *image = mono_assembly_get_image(assembly);
361 	if (!image) {
362 		uwsgi_log("unable to get assembly image\n");
363 		exit(1);
364 	}
365 	umono.application_class = mono_class_from_name(image, "uwsgi", "uWSGIApplication");
366 	if (!umono.application_class) {
367 		uwsgi_log("unable to get reference to class uwsgi.uWSGIApplication\n");
368 		exit(1);
369 	}
370 
371 	umono.byte_class = mono_class_from_name(mono_get_corlib(), "System", "Byte");
372         if (!umono.byte_class) {
373                 uwsgi_log("unable to get reference to class System.Byte\n");
374                 exit(1);
375         }
376 
377 	MonoClass *urequest = mono_class_from_name(image, "uwsgi", "uWSGIRequest");
378 	if (!urequest) {
379 		uwsgi_log("unable to get reference to class uwsgi.uWSGIRequest\n");
380                 exit(1);
381 	}
382 
383 	umono.filepath = mono_class_get_field_from_name(urequest, "filepath");
384 	if (!umono.filepath) {
385 		uwsgi_log("unable to get reference to field uwsgi.uWSGIRequest.filepath\n");
386 	}
387 
388 	umono.api_class = mono_class_from_name(image, "uwsgi", "api");
389         if (!umono.api_class) {
390                 uwsgi_log("unable to get reference to class uwsgi.api\n");
391                 exit(1);
392         }
393 
394 	MonoMethodDesc *desc = mono_method_desc_new("uwsgi.uWSGIApplication:.ctor(string,string)", 1);
395 	if (!desc) {
396 		uwsgi_log("unable to create description for uwsgi.uWSGIApplication:.ctor(string,string)\n");
397 		exit(1);
398 	}
399 	umono.create_application_host = mono_method_desc_search_in_class(desc, umono.application_class);
400 	if (!umono.create_application_host) {
401 		uwsgi_log("unable to find constructor in uWSGIApplication class\n");
402 		exit(1);
403 	}
404 	mono_method_desc_free(desc);
405 
406 	desc = mono_method_desc_new("uwsgi.uWSGIApplication:Request()", 1);
407 	if (!desc) {
408 		uwsgi_log("unable to create description for uwsgi.uWSGIApplication:Request()\n");
409 		exit(1);
410 	}
411 	MonoMethod *process_request = mono_method_desc_search_in_class(desc, umono.application_class);
412 	if (!process_request) {
413 		uwsgi_log("unable to find ProcessRequest method in uwsgi_host class\n");
414 		exit(1);
415 	}
416 	mono_method_desc_free(desc);
417 
418 	umono.process_request = mono_method_get_unmanaged_thunk(process_request);
419 
420 	struct uwsgi_string_list *usl = umono.exec;
421 	while(usl) {
422 		char *assembly_name = usl->value;
423 		char *argv = "";
424 		char *colon = strchr(usl->value, ':');
425 		if (colon) {
426 			argv = colon+1;
427 			assembly_name = uwsgi_concat2n(usl->value, colon-usl->value, "", 0);
428 		}
429 
430 		MonoAssembly *assembly = mono_domain_assembly_open(umono.main_domain, assembly_name);
431 		if (!assembly) {
432 			uwsgi_log("unable to load assembly \"%s\"\n", assembly_name);
433 			exit(1);
434 		}
435 		mono_jit_exec(umono.main_domain, assembly, 1, &argv);
436 		if (assembly_name != usl->value) {
437 			free(assembly_name);
438 		}
439 		usl = usl->next;
440 	}
441 
442 }
443 
uwsgi_mono_create_app(char * key,uint16_t key_len,char * physicalDir,uint16_t physicalDir_len,int new_domain)444 static int uwsgi_mono_create_app(char *key, uint16_t key_len, char *physicalDir, uint16_t physicalDir_len, int new_domain) {
445 	void *params[3];
446         params[2] = NULL;
447 
448 	params[0] = mono_string_new(mono_domain_get(), "/");
449 	params[1] = mono_string_new_len(mono_domain_get(), physicalDir, physicalDir_len);
450 
451 	int id = uwsgi_apps_cnt;
452 	time_t now = uwsgi_now();
453 
454 	MonoObject *appHost = mono_object_new(mono_domain_get(), umono.application_class);
455         if (!appHost) {
456         	uwsgi_log("unable to initialize asp.net ApplicationHost\n");
457 		return -1;
458 	}
459 
460 	MonoObject *exc = NULL;
461 	mono_runtime_invoke(umono.create_application_host, appHost, params, &exc);
462 	if (exc) {
463                 mono_print_unhandled_exception(exc);
464 		return -1;
465         }
466 
467 	struct uwsgi_app *app = uwsgi_add_app(id, mono_plugin.modifier1, key, key_len, uwsgi_concat2n(physicalDir, physicalDir_len, "", 0), appHost);
468         app->started_at = now;
469         app->startup_time = uwsgi_now() - now;
470 	// get a handlet to appHost
471 	mono_gchandle_new(app->callable, 1);
472 	uwsgi_log("Mono asp.net app %d (%.*s) loaded in %d seconds at %p (worker %d)\n", id, key_len, key, (int) app->startup_time, appHost, uwsgi.mywid);
473 
474 	// set it as default app if needed
475 	if (uwsgi.default_app == -1) {
476 		uwsgi.default_app = id;
477 	}
478 
479 	return id;
480 }
481 
uwsgi_mono_init_apps()482 static void uwsgi_mono_init_apps() {
483 
484 	if (!umono.main_domain) {
485 		uwsgi_mono_create_jit();
486 	}
487 
488 	struct uwsgi_string_list *usl = umono.app;
489 
490 	while(usl) {
491 
492 		char *mountpoint = usl->value;
493 		uint8_t mountpoint_len = usl->len;
494 		char *physicalDir = mountpoint;
495 		uint8_t physicalDir_len = mountpoint_len;
496 
497 		char *equal = strchr(mountpoint, '=');
498 		if (equal) {
499 			physicalDir = equal+1;
500 			physicalDir_len = strlen(physicalDir);
501 			// ensure NULL char is at end (just for being backward compatible)
502 			mountpoint = uwsgi_concat2n(mountpoint, equal - mountpoint, "", 0);
503 			mountpoint_len = strlen(mountpoint);
504 		}
505 
506 		int id = uwsgi_mono_create_app(mountpoint, mountpoint_len, physicalDir, physicalDir_len, 0);
507 		if (id == -1) {
508 			exit(1);
509 		}
510 		uwsgi_emulate_cow_for_apps(id);
511 
512 		usl = usl->next;
513 	}
514 }
515 
uwsgi_mono_request(struct wsgi_request * wsgi_req)516 static int uwsgi_mono_request(struct wsgi_request *wsgi_req) {
517 
518 	/* Standard ASP.NET request */
519         if (!wsgi_req->uh->pktsize) {
520                 uwsgi_log("Empty Mono/ASP.NET request. skip.\n");
521                 return -1;
522         }
523 
524         if (uwsgi_parse_vars(wsgi_req)) {
525                 return -1;
526         }
527 
528 	char *key = wsgi_req->document_root;
529 	uint16_t key_len = wsgi_req->document_root_len;
530 
531 	struct uwsgi_string_list *usl = umono.key;
532 	while(usl) {
533 		key = uwsgi_get_var(wsgi_req, usl->value, usl->len, &key_len);
534 		if (key) break;
535 		usl = usl->next;
536 	}
537 
538 	// avoid unexpected values
539 	if (key == NULL) {
540 		key = "";
541 		key_len = 0;
542 	}
543 
544         wsgi_req->app_id = uwsgi_get_app_id(NULL, key, key_len, mono_plugin.modifier1);
545         // if it is -1, try to load a dynamic app
546         if (wsgi_req->app_id == -1 && key_len > 0) {
547         	if (uwsgi.threads > 1) {
548                 	pthread_mutex_lock(&umono.lock_loader);
549                 }
550 
551 		// check if in the mean time, something changed
552 		wsgi_req->app_id = uwsgi_get_app_id(NULL, key, key_len, mono_plugin.modifier1);
553 
554 		if (wsgi_req->app_id == -1) {
555                 	wsgi_req->app_id = uwsgi_mono_create_app(key, key_len, key, key_len, 0);
556 		}
557 
558                 if (uwsgi.threads > 1) {
559                 	pthread_mutex_unlock(&umono.lock_loader);
560                 }
561         }
562 
563 
564         if (wsgi_req->app_id == -1) {
565 		if (!uwsgi.no_default_app && uwsgi.default_app > -1 && uwsgi_apps[uwsgi.default_app].modifier1 == mono_plugin.modifier1) {
566                		wsgi_req->app_id = uwsgi.default_app;
567                 }
568 		else {
569         		uwsgi_500(wsgi_req);
570                 	uwsgi_log("--- unable to find Mono/ASP.NET application ---\n");
571                 	// nothing to clear/free
572                 	return UWSGI_OK;
573 		}
574         }
575 
576         struct uwsgi_app *app = &uwsgi_apps[wsgi_req->app_id];
577         app->requests++;
578 
579 	// check for directory without slash
580 	char *path = uwsgi_concat3n(app->interpreter, strlen(app->interpreter), "/", 1, wsgi_req->path_info, wsgi_req->path_info_len);
581         size_t path_len = strlen(app->interpreter) + 1 + wsgi_req->path_info_len;
582 
583         if (uwsgi_is_dir(path) && path[path_len-1] != '/') {
584 		free(path);
585 		uwsgi_redirect_to_slash(wsgi_req);
586         	return UWSGI_OK;
587 	}
588 	free(path);
589 
590 	MonoException *exc = NULL;
591 
592 	umono.process_request(app->callable, &exc);
593 
594 	if (exc) {
595 		mono_print_unhandled_exception((MonoObject *)exc);
596 	}
597 
598 	if ( uwsgi.workers[uwsgi.mywid].cores[wsgi_req->async_id].requests % umono.gc_freq == 0) {
599 		mono_gc_collect (mono_gc_max_generation());
600 	}
601 
602 	return UWSGI_OK;
603 }
604 
uwsgi_mono_after_request(struct wsgi_request * wsgi_req)605 static void uwsgi_mono_after_request(struct wsgi_request *wsgi_req) {
606 	log_request(wsgi_req);
607 }
608 
uwsgi_mono_init_thread(int core_id)609 static void uwsgi_mono_init_thread(int core_id) {
610 	mono_thread_attach(umono.main_domain);
611 	// SIGPWR, SIGXCPU: these are used internally by the GC and pthreads.
612 	sigset_t smask;
613         sigemptyset(&smask);
614 #if defined(__APPLE__) || defined(__OpenBSD__) || defined(__FreeBSD__) || defined(__GNU_kFreeBSD__)
615         sigaddset(&smask, SIGXFSZ);
616 #else
617         sigaddset(&smask, SIGPWR);
618 #endif
619         if (sigprocmask(SIG_UNBLOCK, &smask, NULL)) {
620                 uwsgi_error("uwsgi_mono_init_thread()/sigprocmask()");
621         }
622 }
623 
uwsgi_mono_pthread_prepare(void)624 static void uwsgi_mono_pthread_prepare(void) {
625         pthread_mutex_lock(&umono.lock_loader);
626 }
627 
uwsgi_mono_pthread_parent(void)628 static void uwsgi_mono_pthread_parent(void) {
629         pthread_mutex_unlock(&umono.lock_loader);
630 }
631 
uwsgi_mono_pthread_child(void)632 static void uwsgi_mono_pthread_child(void) {
633         pthread_mutex_init(&umono.lock_loader, NULL);
634 }
635 
636 
uwsgi_mono_enable_threads(void)637 static void uwsgi_mono_enable_threads(void) {
638         pthread_mutex_init(&umono.lock_loader, NULL);
639         pthread_atfork(uwsgi_mono_pthread_prepare, uwsgi_mono_pthread_parent, uwsgi_mono_pthread_child);
640 }
641 
uwsgi_mono_post_fork()642 static void uwsgi_mono_post_fork() {
643 
644 	// yes, Mono is not fork-friendly, so we initialize it in the post_fork hook
645 	uwsgi_mono_init_apps();
646 
647 	MonoMethodDesc *desc = mono_method_desc_new("uwsgi.api:RunPostForkHook()", 1);
648         if (!desc) {
649 		return;
650         }
651         MonoMethod *method = mono_method_desc_search_in_class(desc, umono.api_class);
652         mono_method_desc_free(desc);
653         if (!method) {
654 		return;
655         }
656 
657 	MonoObject *exc = NULL;
658 	mono_runtime_invoke(method, NULL, NULL, &exc);
659 	if (exc) {
660 		mono_print_unhandled_exception(exc);
661 	}
662 }
663 
uwsgi_mono_signal_handler(uint8_t sig,void * handler)664 static int uwsgi_mono_signal_handler(uint8_t sig, void *handler) {
665 	void *params[2];
666 	int signum = sig;
667 	params[0] = &signum;
668 	params[1] = NULL;
669 	MonoObject *exc = NULL;
670 	mono_runtime_delegate_invoke((MonoObject *) handler, params, &exc);
671 	if (exc) {
672 		mono_print_unhandled_exception(exc);
673 		return -1;
674 	}
675 	return 0;
676 }
677 
678 struct uwsgi_plugin mono_plugin = {
679 
680 	.name = "mono",
681 	.modifier1 = 15,
682 
683 	.options = uwsgi_mono_options,
684 
685 	.init = uwsgi_mono_init,
686 
687 	.request = uwsgi_mono_request,
688 	.after_request = uwsgi_mono_after_request,
689 
690 	.init_thread = uwsgi_mono_init_thread,
691 	.enable_threads = uwsgi_mono_enable_threads,
692 
693 	.post_fork = uwsgi_mono_post_fork,
694 
695 	.signal_handler = uwsgi_mono_signal_handler,
696 };
697