1 #include "../uwsgi.h"
2 
3 extern struct uwsgi_server uwsgi;
4 
5 /*
6 
7 	uWSGI Legions subsystem
8 
9 	A Legion is a group of uWSGI instances sharing a single object. This single
10 	object can be owned only by the instance with the higher valor. Such an instance is the
11 	Lord of the Legion. There can only be one (and only one) Lord for each Legion.
12 	If a member of a Legion spawns with an higher valor than the current Lord, it became the new Lord.
13 
14 
15 */
16 
uwsgi_legion_get_by_socket(int fd)17 struct uwsgi_legion *uwsgi_legion_get_by_socket(int fd) {
18 	struct uwsgi_legion *ul = uwsgi.legions;
19 	while (ul) {
20 		if (ul->socket == fd) {
21 			return ul;
22 		}
23 		ul = ul->next;
24 	}
25 
26 	return NULL;
27 }
28 
uwsgi_legion_get_by_name(char * name)29 struct uwsgi_legion *uwsgi_legion_get_by_name(char *name) {
30 	struct uwsgi_legion *ul = uwsgi.legions;
31 	while (ul) {
32 		if (!strcmp(name, ul->legion)) {
33 			return ul;
34 		}
35 		ul = ul->next;
36 	}
37 
38 	return NULL;
39 }
40 
41 
uwsgi_parse_legion(char * key,uint16_t keylen,char * value,uint16_t vallen,void * data)42 void uwsgi_parse_legion(char *key, uint16_t keylen, char *value, uint16_t vallen, void *data) {
43 	struct uwsgi_legion *ul = (struct uwsgi_legion *) data;
44 
45 	if (!uwsgi_strncmp(key, keylen, "legion", 6)) {
46 		ul->legion = value;
47 		ul->legion_len = vallen;
48 	}
49 	else if (!uwsgi_strncmp(key, keylen, "valor", 5)) {
50 		ul->valor = uwsgi_str_num(value, vallen);
51 	}
52 	else if (!uwsgi_strncmp(key, keylen, "name", 4)) {
53 		ul->name = value;
54 		ul->name_len = vallen;
55 	}
56 	else if (!uwsgi_strncmp(key, keylen, "pid", 3)) {
57 		ul->pid = uwsgi_str_num(value, vallen);
58 	}
59 	else if (!uwsgi_strncmp(key, keylen, "unix", 4)) {
60 		ul->unix_check = uwsgi_str_num(value, vallen);
61 	}
62 	else if (!uwsgi_strncmp(key, keylen, "checksum", 8)) {
63 		ul->checksum = uwsgi_str_num(value, vallen);
64 	}
65 	else if (!uwsgi_strncmp(key, keylen, "uuid", 4)) {
66 		if (vallen == 36) {
67 			memcpy(ul->uuid, value, 36);
68 		}
69 	}
70 	else if (!uwsgi_strncmp(key, keylen, "lord_valor", 10)) {
71 		ul->lord_valor = uwsgi_str_num(value, vallen);
72 	}
73 	else if (!uwsgi_strncmp(key, keylen, "lord_uuid", 9)) {
74 		if (vallen == 36) {
75 			memcpy(ul->lord_uuid, value, 36);
76 		}
77 	}
78 	else if (!uwsgi_strncmp(key, keylen, "scroll", 6)) {
79 		ul->scroll = value;
80 		ul->scroll_len = vallen;
81 	}
82 	else if (!uwsgi_strncmp(key, keylen, "dead", 4)) {
83 		ul->dead = 1;
84 	}
85 }
86 
87 // this function is called when a node is added or removed (heavy locking is needed)
legion_rebuild_scrolls(struct uwsgi_legion * ul)88 static void legion_rebuild_scrolls(struct uwsgi_legion *ul) {
89 	uint64_t max_size = ul->scrolls_max_size;
90 
91 	// first, try to add myself
92 	if (ul->scroll_len + (uint64_t) 2 > max_size) {
93 		uwsgi_log("[DANGER] you have configured a too much tiny buffer for the scrolls list !!! tune it with --legion-scroll-list-max-size\n");
94 		ul->scroll_len = 0;
95 		return;
96 	}
97 
98 	char *ptr = ul->scrolls;
99 	*ptr ++= (uint8_t) (ul->scroll_len & 0xff);
100 	*ptr ++= (uint8_t) ((ul->scroll_len >> 8) &0xff);
101 	memcpy(ptr, ul->scroll, ul->scroll_len); ptr += ul->scroll_len;
102 	ul->scrolls_len = 2 + ul->scroll_len;
103 	// ok start adding nodes;
104 	struct uwsgi_legion_node *uln = ul->nodes_head;
105 	while(uln) {
106 		if (ul->scrolls_len + 2 + uln->scroll_len > max_size) {
107 			uwsgi_log("[DANGER] you have configured a too much tiny buffer for the scrolls list !!! tune it with --legion-scroll-list-max-size\n");
108 			return;
109 		}
110 		*ptr ++= (uint8_t) (uln->scroll_len & 0xff);
111         	*ptr ++= (uint8_t) ((uln->scroll_len >> 8) &0xff);
112         	memcpy(ptr, uln->scroll, uln->scroll_len); ptr += uln->scroll_len;
113         	ul->scrolls_len += 2 + uln->scroll_len;
114 		uln = uln->next;
115 	}
116 }
117 
118 // critical section (remember to lock when you use it)
uwsgi_legion_add_node(struct uwsgi_legion * ul,uint16_t valor,char * name,uint16_t name_len,char * uuid)119 struct uwsgi_legion_node *uwsgi_legion_add_node(struct uwsgi_legion *ul, uint16_t valor, char *name, uint16_t name_len, char *uuid) {
120 
121 	struct uwsgi_legion_node *node = uwsgi_calloc(sizeof(struct uwsgi_legion_node));
122 	if (!name_len)
123 		goto error;
124 	node->name = uwsgi_calloc(name_len);
125 	node->name_len = name_len;
126 	memcpy(node->name, name, name_len);
127 	node->valor = valor;
128 	memcpy(node->uuid, uuid, 36);
129 
130 	if (ul->nodes_tail) {
131 		node->prev = ul->nodes_tail;
132 		ul->nodes_tail->next = node;
133 	}
134 
135 	ul->nodes_tail = node;
136 
137 	if (!ul->nodes_head) {
138 		ul->nodes_head = node;
139 	}
140 
141 
142 	return node;
143 
144 
145 error:
146 	free(node);
147 	return NULL;
148 }
149 
150 // critical section (remember to lock when you use it)
uwsgi_legion_remove_node(struct uwsgi_legion * ul,struct uwsgi_legion_node * node)151 void uwsgi_legion_remove_node(struct uwsgi_legion *ul, struct uwsgi_legion_node *node) {
152 	// check if the node is the first one
153 	if (node == ul->nodes_head) {
154 		ul->nodes_head = node->next;
155 	}
156 
157 	// check if the node is the last one
158 	if (node == ul->nodes_tail) {
159 		ul->nodes_tail = node->prev;
160 	}
161 
162 	if (node->prev) {
163 		node->prev->next = node->next;
164 	}
165 
166 	if (node->next) {
167 		node->next->prev = node->prev;
168 	}
169 
170 	if (node->name_len) {
171 		free(node->name);
172 	}
173 
174 	if (node->scroll_len) {
175 		free(node->scroll);
176 	}
177 
178 	free(node);
179 
180 	legion_rebuild_scrolls(ul);
181 }
182 
uwsgi_legion_get_node(struct uwsgi_legion * ul,uint64_t valor,char * name,uint16_t name_len,char * uuid)183 struct uwsgi_legion_node *uwsgi_legion_get_node(struct uwsgi_legion *ul, uint64_t valor, char *name, uint16_t name_len, char *uuid) {
184 	struct uwsgi_legion_node *nodes = ul->nodes_head;
185 	while (nodes) {
186 		if (valor != nodes->valor)
187 			goto next;
188 		if (name_len != nodes->name_len)
189 			goto next;
190 		if (memcmp(nodes->name, name, name_len))
191 			goto next;
192 		if (memcmp(nodes->uuid, uuid, 36))
193 			goto next;
194 		return nodes;
195 next:
196 		nodes = nodes->next;
197 	}
198 	return NULL;
199 }
200 
legions_check_nodes()201 static void legions_check_nodes() {
202 
203 	struct uwsgi_legion *legion = uwsgi.legions;
204 	while (legion) {
205 		time_t now = uwsgi_now();
206 
207 		struct uwsgi_legion_node *node = legion->nodes_head;
208 		while (node) {
209 			if (now - node->last_seen > uwsgi.legion_tolerance) {
210 				struct uwsgi_legion_node *tmp_node = node;
211 				node = node->next;
212 				uwsgi_log("[uwsgi-legion] %s: %.*s valor: %llu uuid: %.*s left Legion %s\n", tmp_node->valor > 0 ? "node" : "arbiter", tmp_node->name_len, tmp_node->name, tmp_node->valor, 36, tmp_node->uuid, legion->legion);
213 				uwsgi_wlock(legion->lock);
214 				uwsgi_legion_remove_node(legion, tmp_node);
215 				uwsgi_rwunlock(legion->lock);
216 				// trigger node_left hooks
217 				struct uwsgi_string_list *usl = legion->node_left_hooks;
218 				while (usl) {
219 					int ret = uwsgi_legion_action_call("node_left", legion, usl);
220 					if (ret) {
221 						uwsgi_log("[uwsgi-legion] ERROR, node_left hook returned: %d\n", ret);
222 					}
223 					usl = usl->next;
224 				}
225 				continue;
226 			}
227 			node = node->next;
228 		}
229 
230 		legion = legion->next;
231 	}
232 }
233 
234 struct uwsgi_legion_node *uwsgi_legion_get_lord(struct uwsgi_legion *);
235 
legions_report_quorum(struct uwsgi_legion * ul,uint64_t best_valor,char * best_uuid,int votes)236 static void legions_report_quorum(struct uwsgi_legion *ul, uint64_t best_valor, char *best_uuid, int votes) {
237 	struct uwsgi_legion_node *nodes = ul->nodes_head;
238 	uwsgi_log("[uwsgi-legion] --- WE HAVE QUORUM FOR LEGION %s !!! (valor: %llu uuid: %.*s checksum: %llu votes: %d) ---\n", ul->legion, best_valor, 36, best_uuid, ul->checksum, votes);
239 	while (nodes) {
240 		uwsgi_log("[uwsgi-legion-node] %s: %.*s valor: %llu uuid: %.*s last_seen: %d vote_valor: %llu vote_uuid: %.*s\n", nodes->valor > 0 ? "node" : "arbiter", nodes->name_len, nodes->name, nodes->valor, 36, nodes->uuid, nodes->last_seen, nodes->lord_valor, 36, nodes->lord_uuid);
241 		nodes = nodes->next;
242 	}
243 	uwsgi_log("[uwsgi-legion] --- END OF QUORUM REPORT ---\n");
244 }
245 
uwsgi_legion_checksum(struct uwsgi_legion * ul)246 uint64_t uwsgi_legion_checksum(struct uwsgi_legion *ul) {
247 	uint16_t i;
248 	uint64_t checksum = ul->valor;
249 	for(i=0;i<36;i++) {
250 		checksum += ul->uuid[i];
251 	}
252 
253 	struct uwsgi_legion_node *nodes = ul->nodes_head;
254 	while (nodes) {
255 		checksum += nodes->valor;
256 		for(i=0;i<36;i++) {
257 			checksum += nodes->uuid[i];
258 		}
259 		nodes = nodes->next;
260 	}
261 
262 	return checksum;
263 
264 }
265 
legions_check_nodes_step2()266 static void legions_check_nodes_step2() {
267 	struct uwsgi_legion *ul = uwsgi.legions;
268 	while (ul) {
269 		// ok now we can check the status of the lord
270 		int i_am_the_best = 0;
271 		uint64_t best_valor = 0;
272 		char best_uuid[36];
273 		struct uwsgi_legion_node *node = uwsgi_legion_get_lord(ul);
274 		if (node) {
275 			// a node is the best candidate
276 			best_valor = node->valor;
277 			memcpy(best_uuid, node->uuid, 36);
278 		}
279 		// go on if i am not an arbiter
280 		// no potential Lord is available, i will propose myself
281 		// but only if i am not suspended...
282 		else if (ul->valor > 0 && uwsgi_now() > ul->suspended_til) {
283 			best_valor = ul->valor;
284 			memcpy(best_uuid, ul->uuid, 36);
285 			i_am_the_best = 1;
286 		}
287 		else {
288 			// empty lord
289 			memset(best_uuid, 0, 36);
290 		}
291 
292 		// calculate the checksum
293 		uint64_t new_checksum = uwsgi_legion_checksum(ul);
294 		if (new_checksum != ul->checksum) {
295 			ul->changed = 1;
296 		}
297 		ul->checksum = new_checksum;
298 
299 		// ... ok let's see if all of the nodes agree on the lord
300 		// ... but first check if i am not alone...
301 		int votes = 1;
302 		struct uwsgi_legion_node *nodes = ul->nodes_head;
303 		while (nodes) {
304 			if (nodes->checksum != ul->checksum) {
305 				votes = 0;
306 				break;
307 			}
308 			if (nodes->lord_valor != best_valor) {
309 				votes = 0;
310 				break;
311 			}
312 			if (memcmp(nodes->lord_uuid, best_uuid, 36)) {
313 				votes = 0;
314 				break;
315 			}
316 			votes++;
317 			nodes = nodes->next;
318 		}
319 
320 		// we have quorum !!!
321 		if (votes > 0 && votes >= ul->quorum) {
322 			if (!ul->joined) {
323 				// triggering join hooks
324 				struct uwsgi_string_list *usl = ul->join_hooks;
325 				while (usl) {
326 					int ret = uwsgi_legion_action_call("join", ul, usl);
327 					if (ret) {
328 						uwsgi_log("[uwsgi-legion] ERROR, join hook returned: %d\n", ret);
329 					}
330 					usl = usl->next;
331 				}
332 				ul->joined = 1;
333 			}
334 			// something changed ???
335 			if (ul->changed) {
336 				legions_report_quorum(ul, best_valor, best_uuid, votes);
337 				ul->changed = 0;
338 			}
339 			if (i_am_the_best) {
340 				if (!ul->i_am_the_lord) {
341 					// triggering lord hooks
342 					uwsgi_log("[uwsgi-legion] attempting to become the Lord of the Legion %s\n", ul->legion);
343 					struct uwsgi_string_list *usl = ul->lord_hooks;
344 					while (usl) {
345 						int ret = uwsgi_legion_action_call("lord", ul, usl);
346 						if (ret) {
347 							uwsgi_log("[uwsgi-legion] ERROR, lord hook returned: %d\n", ret);
348 							if (uwsgi.legion_death_on_lord_error) {
349 								ul->dead = 1;
350                 						uwsgi_legion_announce(ul);
351 								ul->suspended_til = uwsgi_now() + uwsgi.legion_death_on_lord_error;
352 								uwsgi_log("[uwsgi-legion] suspending myself from Legion \"%s\" for %d seconds\n", ul->legion, uwsgi.legion_death_on_lord_error);
353 								goto next;
354 							}
355 						}
356 						usl = usl->next;
357 					}
358 					if (ul->scroll_len > 0 && ul->scroll_len <= ul->lord_scroll_size) {
359                 				uwsgi_wlock(ul->lock);
360                 				ul->lord_scroll_len = ul->scroll_len;
361                 				memcpy(ul->lord_scroll, ul->scroll, ul->lord_scroll_len);
362                 				uwsgi_rwunlock(ul->lock);
363         				}
364         				else {
365                 				ul->lord_scroll_len = 0;
366         				}
367 					uwsgi_log("[uwsgi-legion] i am now the Lord of the Legion %s\n", ul->legion);
368 					ul->i_am_the_lord = uwsgi_now();
369 					// trick: reduce the time needed by the old lord to unlord itself
370 					uwsgi_legion_announce(ul);
371 				}
372 			}
373 			else {
374 				if (ul->i_am_the_lord) {
375 					uwsgi_log("[uwsgi-legion] a new Lord (valor: %llu uuid: %.*s) raised for Legion %s...\n", ul->lord_valor, 36, ul->lord_uuid, ul->legion);
376 					if (ul->lord_scroll_len > 0) {
377 						uwsgi_log("*********** The New Lord Scroll ***********\n");
378 						uwsgi_log("%.*s\n", ul->lord_scroll_len, ul->lord_scroll);
379 						uwsgi_log("*********** End of the New Lord Scroll ***********\n");
380 					}
381 					// no more lord, trigger unlord hooks
382 					struct uwsgi_string_list *usl = ul->unlord_hooks;
383 					while (usl) {
384 						int ret = uwsgi_legion_action_call("unlord", ul, usl);
385 						if (ret) {
386 							uwsgi_log("[uwsgi-legion] ERROR, unlord hook returned: %d\n", ret);
387 						}
388 						usl = usl->next;
389 					}
390 					ul->i_am_the_lord = 0;
391 				}
392 			}
393 		}
394 		else if (votes > 0 && votes < ul->quorum && (uwsgi_now() - ul->last_warning >= 60)) {
395 			uwsgi_log("[uwsgi-legion] no quorum: only %d vote(s) for Legion %s, %d needed to elect a Lord\n", votes, ul->legion, ul->quorum);
396 			// no more quorum, leave the Lord state
397 			if (ul->i_am_the_lord) {
398 				uwsgi_log("[uwsgi-legion] i cannot be The Lord of The Legion %s without a quorum ...\n", ul->legion);
399 				// no more lord, trigger unlord hooks
400                                 struct uwsgi_string_list *usl = ul->unlord_hooks;
401                                 while (usl) {
402                                 	int ret = uwsgi_legion_action_call("unlord", ul, usl);
403                                         if (ret) {
404                                         	uwsgi_log("[uwsgi-legion] ERROR, unlord hook returned: %d\n", ret);
405                                         }
406                                         usl = usl->next;
407                                 }
408                                 ul->i_am_the_lord = 0;
409 			}
410 			ul->last_warning = uwsgi_now();
411 		}
412 next:
413 		ul = ul->next;
414 	}
415 }
416 
417 // check who should be the lord of the legion
uwsgi_legion_get_lord(struct uwsgi_legion * ul)418 struct uwsgi_legion_node *uwsgi_legion_get_lord(struct uwsgi_legion *ul) {
419 
420 	char best_uuid[36];
421 
422 	memcpy(best_uuid, ul->uuid, 36);
423 	uint64_t best_valor = ul->valor;
424 
425 	struct uwsgi_legion_node *best_node = NULL;
426 
427 	struct uwsgi_legion_node *nodes = ul->nodes_head;
428 	while (nodes) {
429 		// skip arbiters
430 		if (nodes->valor == 0) goto next;
431 		if (nodes->valor > best_valor) {
432 			best_node = nodes;
433 			best_valor = nodes->valor;
434 			memcpy(best_uuid, nodes->uuid, 36);
435 		}
436 		else if (nodes->valor == best_valor) {
437 			if (uwsgi_uuid_cmp(nodes->uuid, best_uuid) > 0) {
438 				best_node = nodes;
439 				best_valor = nodes->valor;
440 				memcpy(best_uuid, nodes->uuid, 36);
441 			}
442 		}
443 next:
444 		nodes = nodes->next;
445 	}
446 
447 	// first round ? (skip first round if arbiter)
448 	if (ul->valor > 0 && ul->lord_valor == 0) {
449 		ul->changed = 1;
450 	}
451 	else if (best_valor != ul->lord_valor) {
452 		ul->changed = 1;
453 	}
454 	else {
455 		if (memcmp(best_uuid, ul->lord_uuid, 36)) {
456 			ul->changed = 1;
457 		}
458 	}
459 
460 	ul->lord_valor = best_valor;
461 	memcpy(ul->lord_uuid, best_uuid, 36);
462 
463 	if (!best_node) return NULL;
464 
465 	if (best_node->scroll_len > 0 && best_node->scroll_len <= ul->lord_scroll_size) {
466 		uwsgi_wlock(ul->lock);
467 		ul->lord_scroll_len = best_node->scroll_len;
468 		memcpy(ul->lord_scroll, best_node->scroll, ul->lord_scroll_len);
469 		uwsgi_rwunlock(ul->lock);
470 	}
471 	else {
472 		ul->lord_scroll_len = 0;
473 	}
474 
475 	return best_node;
476 }
477 
478 
legion_loop(void * foobar)479 static void *legion_loop(void *foobar) {
480 
481 	time_t last_round = uwsgi_now();
482 
483 	unsigned char *crypted_buf = uwsgi_malloc(UMAX16 - EVP_MAX_BLOCK_LENGTH - 4);
484 	unsigned char *clear_buf = uwsgi_malloc(UMAX16);
485 
486 	struct uwsgi_legion legion_msg;
487 
488 	if (!uwsgi.legion_freq)
489 		uwsgi.legion_freq = 3;
490 	if (!uwsgi.legion_tolerance)
491 		uwsgi.legion_tolerance = 15;
492 	if (!uwsgi.legion_skew_tolerance)
493 		uwsgi.legion_skew_tolerance = 60;
494 
495 	int first_round = 1;
496 	for (;;) {
497 		int timeout = uwsgi.legion_freq;
498 		time_t now = uwsgi_now();
499 		if (now > last_round) {
500 			timeout -= (now - last_round);
501 			if (timeout < 0) {
502 				timeout = 0;
503 			}
504 		}
505 		last_round = now;
506 		// wait for event
507 		int interesting_fd = -1;
508 		if (uwsgi_instance_is_reloading || uwsgi_instance_is_dying) return NULL;
509 		int rlen = event_queue_wait(uwsgi.legion_queue, timeout, &interesting_fd);
510 
511 		if (rlen < 0 && errno != EINTR) {
512 			if (uwsgi_instance_is_reloading || uwsgi_instance_is_dying) return NULL;
513 			uwsgi_nuclear_blast();
514 			return NULL;
515 		}
516 
517 		now = uwsgi_now();
518 		if (timeout == 0 || rlen == 0 || (now - last_round) >= timeout) {
519 			struct uwsgi_legion *legions = uwsgi.legions;
520 			while (legions) {
521 				uwsgi_legion_announce(legions);
522 				legions = legions->next;
523 			}
524 			last_round = now;
525 		}
526 
527 		// check the nodes
528 		legions_check_nodes();
529 
530 		if (rlen > 0) {
531 			struct uwsgi_legion *ul = uwsgi_legion_get_by_socket(interesting_fd);
532 			if (!ul)
533 				continue;
534 			// ensure the first 4 bytes are valid
535 			ssize_t len = read(ul->socket, crypted_buf, (UMAX16 - EVP_MAX_BLOCK_LENGTH - 4));
536 			if (len < 0) {
537 				uwsgi_error("[uwsgi-legion] read()");
538 				continue;
539 			}
540 			else if (len < 4) {
541 				uwsgi_log("[uwsgi-legion] invalid packet size: %d\n", (int) len);
542 				continue;
543 			}
544 
545 			struct uwsgi_header *uh = (struct uwsgi_header *) crypted_buf;
546 
547 			if (uh->modifier1 != 109) {
548 				uwsgi_log("[uwsgi-legion] invalid modifier1");
549 				continue;
550 			}
551 
552 			int d_len = 0;
553 			int d2_len = 0;
554 			// decrypt packet using the secret
555 			if (EVP_DecryptInit_ex(ul->decrypt_ctx, NULL, NULL, NULL, NULL) <= 0) {
556 				uwsgi_error("[uwsgi-legion] EVP_DecryptInit_ex()");
557 				continue;
558 			}
559 
560 			if (EVP_DecryptUpdate(ul->decrypt_ctx, clear_buf, &d_len, crypted_buf + 4, len - 4) <= 0) {
561 				uwsgi_error("[uwsgi-legion] EVP_DecryptUpdate()");
562 				continue;
563 			}
564 
565 			if (EVP_DecryptFinal_ex(ul->decrypt_ctx, clear_buf + d_len, &d2_len) <= 0) {
566 				ERR_print_errors_fp(stderr);
567 				uwsgi_log("[uwsgi-legion] EVP_DecryptFinal_ex()\n");
568 				continue;
569 			}
570 
571 			d_len += d2_len;
572 
573 			if (d_len != uh->pktsize) {
574 				uwsgi_log("[uwsgi-legion] invalid packet size\n");
575 				continue;
576 			}
577 
578 			// parse packet
579 			memset(&legion_msg, 0, sizeof(struct uwsgi_legion));
580 			if (uwsgi_hooked_parse((char *) clear_buf, d_len, uwsgi_parse_legion, &legion_msg)) {
581 				uwsgi_log("[uwsgi-legion] invalid packet\n");
582 				continue;
583 			}
584 
585 			if (uwsgi_strncmp(ul->legion, ul->legion_len, legion_msg.legion, legion_msg.legion_len)) {
586 				uwsgi_log("[uwsgi-legion] invalid legion name\n");
587 				continue;
588 			}
589 
590 			// check for loop packets... (expecially when in multicast mode)
591 			if (!uwsgi_strncmp(uwsgi.hostname, uwsgi.hostname_len, legion_msg.name, legion_msg.name_len)) {
592 				if (legion_msg.pid == ul->pid) {
593 					if (legion_msg.valor == ul->valor) {
594 						if (!memcmp(legion_msg.uuid, ul->uuid, 36)) {
595 							continue;
596 						}
597 					}
598 				}
599 			}
600 
601 			// check for "tolerable" unix time
602 			if (legion_msg.unix_check < (uwsgi_now() - uwsgi.legion_skew_tolerance)) {
603 				uwsgi_log("[uwsgi-legion] untolerable packet received for Legion %s , check your clock !!!\n", ul->legion);
604 				continue;
605 			}
606 
607 			// check if the node is already accounted
608 			struct uwsgi_legion_node *node = uwsgi_legion_get_node(ul, legion_msg.valor, legion_msg.name, legion_msg.name_len, legion_msg.uuid);
609 			if (!node) {
610 				// if a lord hook election fails, a node can announce itself as dead for long time...
611 				if (legion_msg.dead) continue;
612 				// add the new node
613 				uwsgi_wlock(ul->lock);
614 				node = uwsgi_legion_add_node(ul, legion_msg.valor, legion_msg.name, legion_msg.name_len, legion_msg.uuid);
615 				if (!node) continue;
616 				if (legion_msg.scroll_len > 0) {
617 					node->scroll = uwsgi_malloc(legion_msg.scroll_len);
618 					node->scroll_len = legion_msg.scroll_len;
619 					memcpy(node->scroll, legion_msg.scroll, node->scroll_len);
620 				}
621 				// we are still locked (and safe), let's rebuild the scrolls list
622 				legion_rebuild_scrolls(ul);
623 				uwsgi_rwunlock(ul->lock);
624 				uwsgi_log("[uwsgi-legion] %s: %.*s valor: %llu uuid: %.*s joined Legion %s\n", node->valor > 0 ? "node" : "arbiter", node->name_len, node->name, node->valor, 36, node->uuid, ul->legion);
625 				// trigger node_joined hooks
626 				struct uwsgi_string_list *usl = ul->node_joined_hooks;
627 				while (usl) {
628 					int ret = uwsgi_legion_action_call("node_joined", ul, usl);
629 					if (ret) {
630 						uwsgi_log("[uwsgi-legion] ERROR, node_joined hook returned: %d\n", ret);
631 					}
632 					usl = usl->next;
633 				}
634 			}
635 			// remove node announcing death
636 			else if (legion_msg.dead) {
637 				uwsgi_log("[uwsgi-legion] %s: %.*s valor: %llu uuid: %.*s announced its death to Legion %s\n", node->valor > 0 ? "node" : "arbiter", node->name_len, node->name, node->valor, 36, node->uuid, ul->legion);
638                                 uwsgi_wlock(ul->lock);
639                                 uwsgi_legion_remove_node(ul, node);
640                                 uwsgi_rwunlock(ul->lock);
641 				continue;
642 			}
643 
644 			node->last_seen = uwsgi_now();
645 			node->lord_valor = legion_msg.lord_valor;
646 			node->checksum = legion_msg.checksum;
647 			memcpy(node->lord_uuid, legion_msg.lord_uuid, 36);
648 
649 		}
650 
651 		// skip the first round if i no packet is received
652 		if (first_round) {
653 			first_round = 0;
654 			continue;
655 		}
656 		legions_check_nodes_step2();
657 	}
658 
659 	return NULL;
660 }
661 
uwsgi_legion_action_call(char * phase,struct uwsgi_legion * ul,struct uwsgi_string_list * usl)662 int uwsgi_legion_action_call(char *phase, struct uwsgi_legion *ul, struct uwsgi_string_list *usl) {
663 	struct uwsgi_legion_action *ula = uwsgi_legion_action_get(usl->custom_ptr);
664 	if (!ula) {
665 		uwsgi_log("[uwsgi-legion] ERROR unable to find legion_action \"%s\"\n", (char *) usl->custom_ptr);
666 		return -1;
667 	}
668 
669 	if (ula->log_msg) {
670 		uwsgi_log("[uwsgi-legion] (phase: %s legion: %s) %s\n", phase, ul->legion, ula->log_msg);
671 	}
672 	else {
673 		uwsgi_log("[uwsgi-legion] (phase: %s legion: %s) calling %s\n", phase, ul->legion, usl->value);
674 	}
675 	return ula->func(ul, usl->value + usl->custom);
676 }
677 
legion_action_cmd(struct uwsgi_legion * ul,char * arg)678 static int legion_action_cmd(struct uwsgi_legion *ul, char *arg) {
679 	return uwsgi_run_command_and_wait(NULL, arg);
680 }
681 
legion_action_signal(struct uwsgi_legion * ul,char * arg)682 static int legion_action_signal(struct uwsgi_legion *ul, char *arg) {
683 	return uwsgi_signal_send(uwsgi.signal_socket, atoi(arg));
684 }
685 
legion_action_log(struct uwsgi_legion * ul,char * arg)686 static int legion_action_log(struct uwsgi_legion *ul, char *arg) {
687 	char *logline = uwsgi_concat2(arg, "\n");
688 	uwsgi_log(logline);
689 	free(logline);
690 	return 0;
691 }
692 
legion_action_alarm(struct uwsgi_legion * ul,char * arg)693 static int legion_action_alarm(struct uwsgi_legion *ul, char *arg) {
694 	char *space = strchr(arg,' ');
695         if (!space) {
696                 uwsgi_log("invalid alarm action syntax, must be: <alarm> <msg>\n");
697                 return -1;
698         }
699         *space = 0;
700         uwsgi_alarm_trigger(arg, space+1,  strlen(space+1));
701         *space = ' ';
702         return 0;
703 }
704 
uwsgi_start_legions()705 void uwsgi_start_legions() {
706 	pthread_t legion_loop_t;
707 
708 	if (!uwsgi.legions)
709 		return;
710 
711 	// register embedded actions
712 	uwsgi_legion_action_register("cmd", legion_action_cmd);
713 	uwsgi_legion_action_register("exec", legion_action_cmd);
714 	uwsgi_legion_action_register("signal", legion_action_signal);
715 	uwsgi_legion_action_register("log", legion_action_log);
716 	uwsgi_legion_action_register("alarm", legion_action_alarm);
717 
718 	uwsgi.legion_queue = event_queue_init();
719 	struct uwsgi_legion *legion = uwsgi.legions;
720 	while (legion) {
721 		char *colon = strchr(legion->addr, ':');
722 		if (colon) {
723 			legion->socket = bind_to_udp(legion->addr, 0, 0);
724 		}
725 		else {
726 			legion->socket = bind_to_unix_dgram(legion->addr);
727 		}
728 		if (legion->socket < 0 || event_queue_add_fd_read(uwsgi.legion_queue, legion->socket)) {
729 			uwsgi_log("[uwsgi-legion] unable to activate legion %s\n", legion->legion);
730 			exit(1);
731 		}
732 		uwsgi_socket_nb(legion->socket);
733 		legion->pid = uwsgi.mypid;
734 		uwsgi_uuid(legion->uuid);
735 		struct uwsgi_string_list *usl = legion->setup_hooks;
736 		while (usl) {
737 			int ret = uwsgi_legion_action_call("setup", legion, usl);
738 			if (ret) {
739 				uwsgi_log("[uwsgi-legion] ERROR, setup hook returned: %d\n", ret);
740 			}
741 			usl = usl->next;
742 		}
743 		legion = legion->next;
744 	}
745 
746 #ifndef UWSGI_UUID
747 	uwsgi_log("WARNING: you are not using libuuid to generate Legions UUID\n");
748 #endif
749 
750 	if (pthread_create(&legion_loop_t, NULL, legion_loop, NULL)) {
751 		uwsgi_error("pthread_create()");
752 		uwsgi_log("unable to run the legion server !!!\n");
753 	}
754 	else {
755 		uwsgi_log("legion manager thread enabled\n");
756 	}
757 
758 }
759 
uwsgi_legion_add(struct uwsgi_legion * ul)760 void uwsgi_legion_add(struct uwsgi_legion *ul) {
761 	struct uwsgi_legion *old_legion = NULL, *legion = uwsgi.legions;
762 	while (legion) {
763 		old_legion = legion;
764 		legion = legion->next;
765 	}
766 
767 	if (old_legion) {
768 		old_legion->next = ul;
769 	}
770 	else {
771 		uwsgi.legions = ul;
772 	}
773 }
774 
uwsgi_legion_announce(struct uwsgi_legion * ul)775 int uwsgi_legion_announce(struct uwsgi_legion *ul) {
776 	time_t now = uwsgi_now();
777 
778 	if (now <= ul->suspended_til) return 0;
779 	ul->suspended_til = 0;
780 
781 	struct uwsgi_buffer *ub = uwsgi_buffer_new(4096);
782 	unsigned char *encrypted = NULL;
783 
784 	if (uwsgi_buffer_append_keyval(ub, "legion", 6, ul->legion, ul->legion_len))
785 		goto err;
786 	if (uwsgi_buffer_append_keynum(ub, "valor", 5, ul->valor))
787 		goto err;
788 	if (uwsgi_buffer_append_keynum(ub, "unix", 4, now))
789 		goto err;
790 	if (uwsgi_buffer_append_keynum(ub, "lord", 4, ul->i_am_the_lord ? ul->i_am_the_lord : 0))
791 		goto err;
792 	if (uwsgi_buffer_append_keyval(ub, "name", 4, uwsgi.hostname, uwsgi.hostname_len))
793 		goto err;
794 	if (uwsgi_buffer_append_keynum(ub, "pid", 3, ul->pid))
795 		goto err;
796 	if (uwsgi_buffer_append_keyval(ub, "uuid", 4, ul->uuid, 36))
797 		goto err;
798 	if (uwsgi_buffer_append_keynum(ub, "checksum", 8, ul->checksum))
799 		goto err;
800 	if (uwsgi_buffer_append_keynum(ub, "lord_valor", 10, ul->lord_valor))
801 		goto err;
802 	if (uwsgi_buffer_append_keyval(ub, "lord_uuid", 9, ul->lord_uuid, 36))
803 		goto err;
804 
805 	if (ul->scroll_len > 0) {
806 		if (uwsgi_buffer_append_keyval(ub, "scroll", 6, ul->scroll, ul->scroll_len))
807                 	goto err;
808 	}
809 
810 	if (ul->dead) {
811 		if (uwsgi_buffer_append_keyval(ub, "dead", 4, "1", 1))
812                 	goto err;
813 	}
814 
815 	encrypted = uwsgi_malloc(ub->pos + 4 + EVP_MAX_BLOCK_LENGTH);
816 	if (EVP_EncryptInit_ex(ul->encrypt_ctx, NULL, NULL, NULL, NULL) <= 0) {
817 		uwsgi_error("[uwsgi-legion] EVP_EncryptInit_ex()");
818 		goto err;
819 	}
820 
821 	int e_len = 0;
822 
823 	if (EVP_EncryptUpdate(ul->encrypt_ctx, encrypted + 4, &e_len, (unsigned char *) ub->buf, ub->pos) <= 0) {
824 		uwsgi_error("[uwsgi-legion] EVP_EncryptUpdate()");
825 		goto err;
826 	}
827 
828 	int tmplen = 0;
829 	if (EVP_EncryptFinal_ex(ul->encrypt_ctx, encrypted + 4 + e_len, &tmplen) <= 0) {
830 		uwsgi_error("[uwsgi-legion] EVP_EncryptFinal_ex()");
831 		goto err;
832 	}
833 
834 	e_len += tmplen;
835 	uint16_t pktsize = ub->pos;
836 	encrypted[0] = 109;
837 	encrypted[1] = (unsigned char) (pktsize & 0xff);
838 	encrypted[2] = (unsigned char) ((pktsize >> 8) & 0xff);
839 	encrypted[3] = 0;
840 
841 	struct uwsgi_string_list *usl = ul->nodes;
842 	while (usl) {
843 		if (sendto(ul->socket, encrypted, e_len + 4, 0, usl->custom_ptr, usl->custom) != e_len + 4) {
844 			uwsgi_error("[uwsgi-legion] sendto()");
845 		}
846 		usl = usl->next;
847 	}
848 
849 	uwsgi_buffer_destroy(ub);
850 	free(encrypted);
851 	return 0;
852 err:
853 	uwsgi_buffer_destroy(ub);
854 	free(encrypted);
855 	return -1;
856 }
857 
uwsgi_opt_legion_mcast(char * opt,char * value,void * foobar)858 void uwsgi_opt_legion_mcast(char *opt, char *value, void *foobar) {
859 	uwsgi_opt_legion(opt, value, foobar);
860 	char *legion = uwsgi_str(value);
861 	char *space = strchr(legion, ' ');
862 	// over engineering
863 	if (!space) exit(1);
864 	*space = 0;
865 	struct uwsgi_legion *ul = uwsgi_legion_get_by_name(legion);
866         if (!ul) {
867                 uwsgi_log("unknown legion: %s\n", legion);
868                 exit(1);
869         }
870 	uwsgi_legion_register_node(ul, uwsgi_str(ul->addr));
871 	free(legion);
872 }
873 
uwsgi_opt_legion_node(char * opt,char * value,void * foobar)874 void uwsgi_opt_legion_node(char *opt, char *value, void *foobar) {
875 
876 	char *legion = uwsgi_str(value);
877 
878 	char *space = strchr(legion, ' ');
879 	if (!space) {
880 		uwsgi_log("invalid legion-node syntax, must be <legion> <addr>\n");
881 		exit(1);
882 	}
883 	*space = 0;
884 
885 	struct uwsgi_legion *ul = uwsgi_legion_get_by_name(legion);
886 	if (!ul) {
887 		uwsgi_log("unknown legion: %s\n", legion);
888 		exit(1);
889 	}
890 
891 	uwsgi_legion_register_node(ul, space + 1);
892 
893 }
894 
uwsgi_legion_register_node(struct uwsgi_legion * ul,char * addr)895 void uwsgi_legion_register_node(struct uwsgi_legion *ul, char *addr) {
896 	struct uwsgi_string_list *usl = uwsgi_string_new_list(&ul->nodes, addr);
897 	char *port = strchr(addr, ':');
898 	if (!port) {
899 		uwsgi_log("[uwsgi-legion] invalid udp address: %s\n", addr);
900 		exit(1);
901 	}
902 	// no need to zero the memory, socket_to_in_addr will do that
903 	struct sockaddr_in *sin = uwsgi_malloc(sizeof(struct sockaddr_in));
904 	usl->custom = socket_to_in_addr(addr, port, 0, sin);
905 	usl->custom_ptr = sin;
906 }
907 
uwsgi_opt_legion_quorum(char * opt,char * value,void * foobar)908 void uwsgi_opt_legion_quorum(char *opt, char *value, void *foobar) {
909 
910         char *legion = uwsgi_str(value);
911 
912         char *space = strchr(legion, ' ');
913         if (!space) {
914                 uwsgi_log("invalid legion-quorum syntax, must be <legion> <quorum>\n");
915                 exit(1);
916         }
917         *space = 0;
918 
919         struct uwsgi_legion *ul = uwsgi_legion_get_by_name(legion);
920         if (!ul) {
921                 uwsgi_log("unknown legion: %s\n", legion);
922                 exit(1);
923         }
924 
925 	ul->quorum = atoi(space+1);
926 	free(legion);
927 }
928 
uwsgi_opt_legion_scroll(char * opt,char * value,void * foobar)929 void uwsgi_opt_legion_scroll(char *opt, char *value, void *foobar) {
930 
931         char *legion = uwsgi_str(value);
932 
933         char *space = strchr(legion, ' ');
934         if (!space) {
935                 uwsgi_log("invalid legion-scroll syntax, must be <legion> <scroll>\n");
936                 exit(1);
937         }
938         *space = 0;
939 
940         struct uwsgi_legion *ul = uwsgi_legion_get_by_name(legion);
941         if (!ul) {
942                 uwsgi_log("unknown legion: %s\n", legion);
943                 exit(1);
944         }
945 
946         ul->scroll = space+1;
947 	ul->scroll_len = strlen(ul->scroll);
948 	// DO NOT FREE IT !!!
949         //free(legion);
950 }
951 
952 
953 
uwsgi_opt_legion_hook(char * opt,char * value,void * foobar)954 void uwsgi_opt_legion_hook(char *opt, char *value, void *foobar) {
955 
956 	char *event = strchr(opt, '-');
957 	if (!event) {
958 		uwsgi_log("[uwsgi-legion] invalid option name (%s), this should not happen (possible bug)\n", opt);
959 		exit(1);
960 	}
961 
962 	char *legion = uwsgi_str(value);
963 
964 	char *space = strchr(legion, ' ');
965 	if (!space) {
966 		uwsgi_log("[uwsgi-legion] invalid %s syntax, must be <legion> <action>\n", opt);
967 		exit(1);
968 	}
969 	*space = 0;
970 
971 	struct uwsgi_legion *ul = uwsgi_legion_get_by_name(legion);
972 	if (!ul) {
973 		uwsgi_log("[uwsgi-legion] unknown legion: %s\n", legion);
974 		exit(1);
975 	}
976 
977 	uwsgi_legion_register_hook(ul, event + 1, space + 1);
978 }
979 
uwsgi_legion_register_hook(struct uwsgi_legion * ul,char * event,char * action)980 void uwsgi_legion_register_hook(struct uwsgi_legion *ul, char *event, char *action) {
981 
982 	struct uwsgi_string_list *usl = NULL;
983 
984 	if (!strcmp(event, "lord")) {
985 		usl = uwsgi_string_new_list(&ul->lord_hooks, action);
986 	}
987 	else if (!strcmp(event, "unlord")) {
988 		usl = uwsgi_string_new_list(&ul->unlord_hooks, action);
989 	}
990 	else if (!strcmp(event, "setup")) {
991 		usl = uwsgi_string_new_list(&ul->setup_hooks, action);
992 	}
993 	else if (!strcmp(event, "death")) {
994 		usl = uwsgi_string_new_list(&ul->death_hooks, action);
995 	}
996 	else if (!strcmp(event, "join")) {
997 		usl = uwsgi_string_new_list(&ul->join_hooks, action);
998 	}
999 	else if (!strcmp(event, "node-joined")) {
1000 		usl = uwsgi_string_new_list(&ul->node_joined_hooks, action);
1001 	}
1002 	else if (!strcmp(event, "node-left")) {
1003 		usl = uwsgi_string_new_list(&ul->node_left_hooks, action);
1004 	}
1005 
1006 	else {
1007 		uwsgi_log("[uwsgi-legion] invalid event: %s\n", event);
1008 		exit(1);
1009 	}
1010 
1011 	if (!usl)
1012 		return;
1013 
1014 	char *hook = strchr(action, ':');
1015 	if (!hook) {
1016 		uwsgi_log("[uwsgi-legion] invalid %s action: %s\n", event, action);
1017 		exit(1);
1018 	}
1019 
1020 	// pointer to action plugin
1021 	usl->custom_ptr = uwsgi_concat2n(action, hook - action, "", 0);;
1022 	// add that to check the plugin value
1023 	usl->custom = hook - action + 1;
1024 
1025 }
1026 
uwsgi_opt_legion(char * opt,char * value,void * foobar)1027 void uwsgi_opt_legion(char *opt, char *value, void *foobar) {
1028 
1029 	// legion addr valor algo:secret
1030 	char *legion = uwsgi_str(value);
1031 	char *space = strchr(legion, ' ');
1032 	if (!space) {
1033 		uwsgi_log("invalid legion syntax, must be <legion> <addr> <valor> <algo:secret>\n");
1034 		exit(1);
1035 	}
1036 	*space = 0;
1037 	char *addr = space + 1;
1038 
1039 	space = strchr(addr, ' ');
1040 	if (!space) {
1041 		uwsgi_log("invalid legion syntax, must be <legion> <addr> <valor> <algo:secret>\n");
1042 		exit(1);
1043 	}
1044 	*space = 0;
1045 	char *valor = space + 1;
1046 
1047 	space = strchr(valor, ' ');
1048 	if (!space) {
1049 		uwsgi_log("invalid legion syntax, must be <legion> <addr> <valor> <algo:secret>\n");
1050 		exit(1);
1051 	}
1052 	*space = 0;
1053 	char *algo_secret = space + 1;
1054 
1055 	char *colon = strchr(algo_secret, ':');
1056 	if (!colon) {
1057 		uwsgi_log("invalid legion syntax, must be <legion> <addr> <valor> <algo:secret>\n");
1058 		exit(1);
1059 	}
1060 	*colon = 0;
1061 	char *secret = colon + 1;
1062 
1063 	uwsgi_legion_register(legion, addr, valor, algo_secret, secret);
1064 }
1065 
uwsgi_legion_register(char * legion,char * addr,char * valor,char * algo,char * secret)1066 struct uwsgi_legion *uwsgi_legion_register(char *legion, char *addr, char *valor, char *algo, char *secret) {
1067 	char *iv = strchr(secret, ' ');
1068 	if (iv) {
1069 		*iv = 0;
1070 		iv++;
1071 	}
1072 
1073 	if (!uwsgi.ssl_initialized) {
1074 		uwsgi_ssl_init();
1075 	}
1076 
1077 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1078 	EVP_CIPHER_CTX *ctx = uwsgi_malloc(sizeof(EVP_CIPHER_CTX));
1079 	EVP_CIPHER_CTX_init(ctx);
1080 #else
1081 	EVP_CIPHER_CTX *ctx = EVP_CIPHER_CTX_new();
1082 #endif
1083 
1084 	const EVP_CIPHER *cipher = EVP_get_cipherbyname(algo);
1085 	if (!cipher) {
1086 		uwsgi_log("[uwsgi-legion] unable to find algorithm/cipher %s\n", algo);
1087 		exit(1);
1088 	}
1089 
1090 	int cipher_len = EVP_CIPHER_key_length(cipher);
1091 	size_t s_len = strlen(secret);
1092 	if ((unsigned int) cipher_len > s_len) {
1093 		char *secret_tmp = uwsgi_malloc(cipher_len);
1094 		memcpy(secret_tmp, secret, s_len);
1095 		memset(secret_tmp + s_len, 0, cipher_len - s_len);
1096 		secret = secret_tmp;
1097 	}
1098 
1099 	int iv_len = EVP_CIPHER_iv_length(cipher);
1100 	size_t s_iv_len = 0;
1101 	if (iv) {
1102 		s_iv_len = strlen(iv);
1103 	}
1104 	if ((unsigned int) iv_len > s_iv_len) {
1105                 char *secret_tmp = uwsgi_malloc(iv_len);
1106                 memcpy(secret_tmp, iv, s_iv_len);
1107                 memset(secret_tmp + s_iv_len, '0', iv_len - s_iv_len);
1108                 iv = secret_tmp;
1109         }
1110 
1111 	if (EVP_EncryptInit_ex(ctx, cipher, NULL, (const unsigned char *) secret, (const unsigned char *) iv) <= 0) {
1112 		uwsgi_error("EVP_EncryptInit_ex()");
1113 		exit(1);
1114 	}
1115 
1116 #if OPENSSL_VERSION_NUMBER < 0x10100000L
1117 	EVP_CIPHER_CTX *ctx2 = uwsgi_malloc(sizeof(EVP_CIPHER_CTX));
1118 	EVP_CIPHER_CTX_init(ctx2);
1119 #else
1120 	EVP_CIPHER_CTX *ctx2 = EVP_CIPHER_CTX_new();
1121 #endif
1122 
1123 	if (EVP_DecryptInit_ex(ctx2, cipher, NULL, (const unsigned char *) secret, (const unsigned char *) iv) <= 0) {
1124 		uwsgi_error("EVP_DecryptInit_ex()");
1125 		exit(1);
1126 	}
1127 
1128 	// we use shared memory, as we want to export legion status to the api
1129 	struct uwsgi_legion *ul = uwsgi_calloc_shared(sizeof(struct uwsgi_legion));
1130 	ul->legion = legion;
1131 	ul->legion_len = strlen(ul->legion);
1132 
1133 	ul->valor = strtol(valor, (char **) NULL, 10);
1134 	ul->addr = addr;
1135 
1136 	ul->encrypt_ctx = ctx;
1137 	ul->decrypt_ctx = ctx2;
1138 
1139 	if (!uwsgi.legion_scroll_max_size) {
1140 		uwsgi.legion_scroll_max_size = 4096;
1141 	}
1142 
1143 	if (!uwsgi.legion_scroll_list_max_size) {
1144 		uwsgi.legion_scroll_list_max_size = 32768;
1145 	}
1146 
1147 	ul->lord_scroll_size = uwsgi.legion_scroll_max_size;
1148 	ul->lord_scroll = uwsgi_calloc_shared(ul->lord_scroll_size);
1149 	ul->scrolls_max_size = uwsgi.legion_scroll_list_max_size;
1150 	ul->scrolls = uwsgi_calloc_shared(ul->scrolls_max_size);
1151 
1152 	uwsgi_legion_add(ul);
1153 
1154 	return ul;
1155 }
1156 
uwsgi_legion_action_get(char * name)1157 struct uwsgi_legion_action *uwsgi_legion_action_get(char *name) {
1158 	struct uwsgi_legion_action *ula = uwsgi.legion_actions;
1159 	while (ula) {
1160 		if (!strcmp(name, ula->name)) {
1161 			return ula;
1162 		}
1163 		ula = ula->next;
1164 	}
1165 	return NULL;
1166 }
1167 
uwsgi_legion_action_register(char * name,int (* func)(struct uwsgi_legion *,char *))1168 struct uwsgi_legion_action *uwsgi_legion_action_register(char *name, int (*func) (struct uwsgi_legion *, char *)) {
1169 	struct uwsgi_legion_action *found_ula = uwsgi_legion_action_get(name);
1170 	if (found_ula) {
1171 		uwsgi_log("[uwsgi-legion] action \"%s\" is already registered !!!\n", name);
1172 		return found_ula;
1173 	}
1174 
1175 	struct uwsgi_legion_action *old_ula = NULL, *ula = uwsgi.legion_actions;
1176 	while (ula) {
1177 		old_ula = ula;
1178 		ula = ula->next;
1179 	}
1180 
1181 	ula = uwsgi_calloc(sizeof(struct uwsgi_legion_action));
1182 	ula->name = name;
1183 	ula->func = func;
1184 
1185 	if (old_ula) {
1186 		old_ula->next = ula;
1187 	}
1188 	else {
1189 		uwsgi.legion_actions = ula;
1190 	}
1191 
1192 	return ula;
1193 }
1194 
uwsgi_legion_announce_death(void)1195 void uwsgi_legion_announce_death(void) {
1196 	struct uwsgi_legion *legion = uwsgi.legions;
1197         while (legion) {
1198                 legion->dead = 1;
1199                 uwsgi_legion_announce(legion);
1200                 legion = legion->next;
1201         }
1202 }
1203 
uwsgi_legion_atexit(void)1204 void uwsgi_legion_atexit(void) {
1205 	struct uwsgi_legion *legion = uwsgi.legions;
1206 	while (legion) {
1207 		if (getpid() != legion->pid)
1208 			goto next;
1209 		struct uwsgi_string_list *usl = legion->death_hooks;
1210 		while (usl) {
1211 			int ret = uwsgi_legion_action_call("death", legion, usl);
1212 			if (ret) {
1213 				uwsgi_log("[uwsgi-legion] ERROR, death hook returned: %d\n", ret);
1214 			}
1215 			usl = usl->next;
1216 		}
1217 next:
1218 		legion = legion->next;
1219 	}
1220 
1221 	// this must be called only by the master !!!
1222 	if (!uwsgi.workers) return;
1223 	if (uwsgi.workers[0].pid != getpid()) return;
1224 	uwsgi_legion_announce_death();
1225 }
1226 
uwsgi_legion_i_am_the_lord(char * name)1227 int uwsgi_legion_i_am_the_lord(char *name) {
1228 	struct uwsgi_legion *legion = uwsgi_legion_get_by_name(name);
1229 	if (!legion) return 0;
1230 	if (legion->i_am_the_lord) {
1231 		return 1;
1232 	}
1233 	return 0;
1234 }
1235 
uwsgi_legion_lord_scroll(char * name,uint16_t * rlen)1236 char *uwsgi_legion_lord_scroll(char *name, uint16_t *rlen) {
1237 	char *buf = NULL;
1238 	struct uwsgi_legion *legion = uwsgi_legion_get_by_name(name);
1239         if (!legion) return 0;
1240 	uwsgi_rlock(legion->lock);
1241 	if (legion->lord_scroll_len > 0) {
1242 		buf = uwsgi_malloc(legion->lord_scroll_len);
1243 		memcpy(buf, legion->lord_scroll, legion->lord_scroll_len);
1244 		*rlen = legion->lord_scroll_len;
1245 	}
1246 	uwsgi_rwunlock(legion->lock);
1247 	return buf;
1248 }
1249 
uwsgi_legion_scrolls(char * name,uint64_t * rlen)1250 char *uwsgi_legion_scrolls(char *name, uint64_t *rlen) {
1251 	char *buf = NULL;
1252         struct uwsgi_legion *legion = uwsgi_legion_get_by_name(name);
1253         if (!legion) return NULL;
1254 	uwsgi_rlock(legion->lock);
1255 	buf = uwsgi_malloc(legion->scrolls_len);
1256 	memcpy(buf, legion->scrolls, legion->scrolls_len);
1257 	*rlen = legion->scrolls_len;
1258 	uwsgi_rwunlock(legion->lock);
1259 	return buf;
1260 }
1261