Browse Source

Add signal handling and clean exiting

Yann Weber 4 years ago
parent
commit
11df1455cf
6 changed files with 130 additions and 9 deletions
  1. 7
    0
      include/conf.h
  2. 3
    0
      include/pyworker.h
  3. 3
    0
      include/responder.h
  4. 47
    5
      src/main.c
  5. 5
    0
      src/pyworker.c
  6. 65
    4
      src/responder.c

+ 7
- 0
include/conf.h View File

@@ -117,6 +117,12 @@ typedef struct pyfcgi_context_s pyfcgi_context_t;
117 117
 struct pyfcgi_context_s {
118 118
 	pid_t pid;
119 119
 	pid_t ppid;
120
+
121
+
122
+	/**@brief array of worker pids (pool handler context) */
123
+	pid_t **wrk_pids;
124
+	/**@brief workers count */
125
+	unsigned int n_wrk;
120 126
 };
121 127
 
122 128
 /**@brief Structure containing configuration
@@ -148,6 +154,7 @@ struct pyfcgi_conf_s
148 154
 	 * @ingroup conf_glob */
149 155
 	int max_reqs;
150 156
 
157
+
151 158
 	/**@brief Logger configuration
152 159
 	 * @ingroupe conf_glob */
153 160
 	pyfcgi_conf_logger_t logs;

+ 3
- 0
include/pyworker.h View File

@@ -73,6 +73,7 @@
73 73
 #include <limits.h>
74 74
 #include <poll.h>
75 75
 #include <sched.h>
76
+#include <signal.h>
76 77
 #include <sys/types.h>
77 78
 #include <sys/ipc.h>
78 79
 #include <sys/sem.h>
@@ -156,5 +157,7 @@ void worker_piper_sighandler(int);
156 157
  */
157 158
 int ctl_get_rep_sz(int, size_t*);
158 159
 
160
+void worker_sighandler(int);
161
+
159 162
 #endif
160 163
 

+ 3
- 0
include/responder.h View File

@@ -98,4 +98,7 @@ int new_semaphore();
98 98
  */
99 99
 void clean_exit(int);
100 100
 
101
+/**@brief Handle signals and forward it to workers */
102
+void pool_sighandler(int signum);
103
+
101 104
 #endif

+ 47
- 5
src/main.c View File

@@ -36,6 +36,7 @@
36 36
 #include <syslog.h>
37 37
 #include <string.h>
38 38
 #include <errno.h>
39
+#include <signal.h>
39 40
 #include <sys/types.h>
40 41
 #include <sys/wait.h>
41 42
 
@@ -50,12 +51,52 @@
50 51
 
51 52
 extern pyfcgi_conf_t PyFCGI_conf;
52 53
 
54
+pid_t pool_handler_pid;
55
+
56
+void sighandler(int signum)
57
+{
58
+	int ret;
59
+	if(signum == SIGINT)
60
+	{
61
+		pyfcgi_log(LOG_INFO,
62
+			"Master process received ctrl+c, exiting...");
63
+	}
64
+	else
65
+	{
66
+		pyfcgi_log(LOG_INFO,
67
+			"Master process received signal %s(%d), exiting...",
68
+			strsignal(signum), signum);
69
+	}
70
+	if(pool_handler_pid)
71
+	{
72
+		kill(pool_handler_pid, SIGTERM);
73
+		waitpid(pool_handler_pid, &ret, 0);
74
+	}
75
+	pyfcgi_log(LOG_INFO,
76
+		"Master process exiting.");
77
+	exit(0);
78
+}
79
+
53 80
 int main(int argc, char **argv)
54 81
 {
55
-	pid_t child;
56 82
 	int child_ret;
57 83
 	unsigned int emerg_sleep = 3;
84
+	struct sigaction act;
85
+
86
+	act.sa_handler = sighandler;
87
+	sigemptyset(&act.sa_mask);
88
+	sigaddset(&act.sa_mask, SIGTERM);
89
+	act.sa_flags = 0;
90
+	act.sa_restorer = NULL;
91
+
92
+	if(sigaction(SIGINT, &act, NULL))
93
+	{
94
+		perror("Sigaction error");
95
+		exit(4);
96
+	}
97
+
58 98
 
99
+	pool_handler_pid = 0;
59 100
 	default_conf();
60 101
 	pyfcgi_logger_init();
61 102
 	pyfcgi_logger_set_ident("pyfcgi(main)");
@@ -68,20 +109,21 @@ int main(int argc, char **argv)
68 109
 
69 110
 	while(1)
70 111
 	{
71
-		child = fork();
72
-		if(child == -1)
112
+		pool_handler_pid = fork();
113
+		if(pool_handler_pid == -1)
73 114
 		{
74 115
 			// TODO : Error
75 116
 			pyfcgi_log(LOG_EMERG, "Failed to fork : '%s' sleeping %ds", strerror(errno), emerg_sleep);
76 117
 			sleep(emerg_sleep);
77 118
 			continue;
78 119
 		}
79
-		else if(!child)
120
+		else if(!pool_handler_pid)
80 121
 		{
81 122
 			responder_loop();
82 123
 			exit((unsigned char)-1);
83 124
 		}
84
-		waitpid(child, &child_ret, 0);
125
+		waitpid(pool_handler_pid, &child_ret, 0);
126
+		pool_handler_pid = 0;
85 127
 		if(child_ret)
86 128
 		{
87 129
 			pyfcgi_log(LOG_ERR, "Responder loop function exits with error code '%d'",

+ 5
- 0
src/pyworker.c View File

@@ -551,3 +551,8 @@ static void worker_set_busy(int semid)
551 551
 	}
552 552
 }
553 553
 
554
+void worker_sighandler(int signum)
555
+{
556
+	pyfcgi_log(LOG_INFO, "%s signal received, exiting...", strsignal(signum));
557
+	exit(0);
558
+}

+ 65
- 4
src/responder.c View File

@@ -24,7 +24,6 @@ void init_context()
24 24
 	pyfcgi_semid = 0;
25 25
 }
26 26
 
27
-
28 27
 int responder_loop()
29 28
 {
30 29
 	unsigned int n_wrk, wanted_n, n;
@@ -35,6 +34,19 @@ int responder_loop()
35 34
 	struct sembuf sop;
36 35
 	struct timespec timeout;
37 36
 	short idle;
37
+	struct sigaction act;
38
+
39
+	act.sa_handler = pool_sighandler;
40
+	sigemptyset(&act.sa_mask);
41
+	sigaddset(&act.sa_mask, SIGTERM);
42
+	act.sa_flags = 0;
43
+	act.sa_restorer = NULL;
44
+
45
+	if(sigaction(SIGINT, &act, NULL))
46
+	{
47
+		perror("Sigaction error for pool process");
48
+		exit(PYFCGI_FATAL);
49
+	}
38 50
 
39 51
 	sop.sem_num = 0;
40 52
 	sop.sem_op = 0;
@@ -48,6 +60,8 @@ int responder_loop()
48 60
 
49 61
 	init_context();
50 62
 
63
+	PyFCGI_conf.context.wrk_pids = &wrk_pids;
64
+	PyFCGI_conf.context.n_wrk = 0;
51 65
 	wrk_pids = malloc(sizeof(int) * PyFCGI_conf.max_wrk);
52 66
 	if(!wrk_pids)
53 67
 	{
@@ -67,6 +81,7 @@ int responder_loop()
67 81
 	for(n_wrk=0; n_wrk < wanted_n; n_wrk++)
68 82
 	{
69 83
 		wrk_pids[n_wrk] = spawn(n_wrk, semid);
84
+		PyFCGI_conf.context.n_wrk = n_wrk;
70 85
 	}
71 86
 	//Wait at least for a process to be ready
72 87
 	while(!semtimedop(semid, &sop, 1, &timeout));
@@ -75,6 +90,7 @@ int responder_loop()
75 90
 	// spawn new one if needed, etc.
76 91
 	while(1)
77 92
 	{
93
+		PyFCGI_conf.context.n_wrk = n_wrk;
78 94
 		if( (ret = waitpid(0, &status, WNOHANG)) )
79 95
 		{
80 96
 			if(ret < 0)
@@ -200,11 +216,12 @@ pyfcgi_log( LOG_DEBUG, "GC want %d have %d", wanted_n, n_wrk);
200 216
 	}
201 217
 	
202 218
 	//Debug wait & exit
203
-	for(n_wrk=0; n_wrk != PyFCGI_conf.min_wrk; n_wrk++)
219
+	for(; n_wrk != 0; n_wrk--)
204 220
 	{
205 221
 		waitpid(wrk_pids[n_wrk], &status, 0);
206 222
 		pyfcgi_log(LOG_DEBUG, "Child %d stopped with status %d",
207 223
 		       wrk_pids[n_wrk], status);
224
+		PyFCGI_conf.context.n_wrk = n_wrk;
208 225
 	}
209 226
 		//printf("Content-Type: text/html\r\n\r\nHello world !\n");
210 227
 	pyfcgi_log(LOG_INFO,"Child workers stoped, stopping responder");
@@ -215,9 +232,17 @@ pid_t spawn(int wrk_id, int semid)
215 232
 {
216 233
 	pid_t res;
217 234
 	struct timespec timeout;
235
+	struct sigaction act;
236
+
218 237
 	timeout.tv_sec = 0;
219 238
 	timeout.tv_nsec = 100000000;
220
-	
239
+
240
+	act.sa_handler = worker_sighandler;
241
+	sigemptyset(&act.sa_mask);
242
+	sigaddset(&act.sa_mask, SIGTERM);
243
+	act.sa_flags = 0;
244
+	act.sa_restorer = NULL;
245
+
221 246
 
222 247
 	res = fork();
223 248
 	if(res == -1)
@@ -229,6 +254,12 @@ pid_t spawn(int wrk_id, int semid)
229 254
 	else if(!res)
230 255
 	{
231 256
 		// Child process
257
+		if(sigaction(SIGINT, &act, NULL))
258
+		{
259
+			perror("Sigaction error for pool process");
260
+			exit(PYFCGI_FATAL);
261
+		}
262
+
232 263
 		if(PyFCGI_conf.pep333)
233 264
 		{
234 265
 			exit(work333(wrk_id, semid));
@@ -276,8 +307,38 @@ void clean_exit(int status)
276 307
 {
277 308
 	if(pyfcgi_semid && semctl(pyfcgi_semid, 0, IPC_RMID) == -1)
278 309
 	{
310
+		/*silentely fails (when exiting from sighandler)
279 311
 		pyfcgi_log(	LOG_CRIT,
280
-			"Unable to delete semaphore before exiting.");
312
+			"Unable to delete semaphore before exiting : %s",
313
+			strerror(errno));
314
+		*/
281 315
 	}
282 316
 	exit(status);
283 317
 }
318
+
319
+void pool_sighandler(int signum)
320
+{
321
+	unsigned int i;
322
+	struct timespec req;
323
+	req.tv_sec = 0;
324
+	req.tv_nsec = 200000000; //0.2s
325
+	if(PyFCGI_conf.context.n_wrk < 1) { clean_exit(0); }
326
+
327
+	for(i=0; i<PyFCGI_conf.context.n_wrk; i++)
328
+	{
329
+		pyfcgi_log(LOG_INFO, "Sending SIGTERM to child #%d (pid %d)",
330
+			i,(*PyFCGI_conf.context.wrk_pids)[i]);
331
+		kill((*PyFCGI_conf.context.wrk_pids)[i], SIGTERM);
332
+		nanosleep(&req, NULL); //waiting 0.2s
333
+	}
334
+	for(i=0; i<PyFCGI_conf.context.n_wrk; i++)
335
+	{
336
+		if(kill((*PyFCGI_conf.context.wrk_pids)[i], SIGCONT))
337
+		{
338
+			pyfcgi_log(LOG_INFO, "Sending SIGKILL to child %d", i);
339
+			kill((*PyFCGI_conf.context.wrk_pids)[i], SIGKILL);
340
+		}
341
+		
342
+	}
343
+	clean_exit(0);
344
+}

Loading…
Cancel
Save