Project

General

Profile

Feature #2409 ยป multiwatch-persistence.diff

Anonymous, 2012-04-10 15:25

View differences:

multiwatch.1
9 9
\-v
10 10

  
11 11
.B multiwatch
12
\-\-help | \-?
12
\-\-help | \-? | \-h
13 13
.SH DESCRIPTION
14 14
\fImultiwatch\fP is used to fork and watch multiple FastCGI backends.
15 15
.SH OPTIONS
......
17 17
.B \-f, \-\-forks=children
18 18
Number of children to fork and watch (default 1)
19 19
.TP 8
20
.B \-h, --help, \-?
21
General usage instructions
22
.TP 8
23
.B \-p, \-\-persist
24
Don't exit when all children fail to run
25
.TP 8
20 26
.B \-r, --retry=retries
21 27
Number of retries to fork a single child (default 3)
22 28
.TP 8
23
.B \-t, --timeout=msecs
24
Retry timeout in ms; if the child dies after the timeout the retry counter is reset (default 10000 ms = 10 s)
25
.TP 8
26 29
.B \-s, --signal=signame
27
Signal to send to children to signal 'graceful' termination (HUP,INT,QUIT,TERM,USR1,USR2, default is USR1)
30
Preferred signal to be propagated to child processes e.g. to gracefully terminate (HUP, INT, QUIT, TERM, USR1 or USR2)
28 31
.TP 8
29
.B \-?, --help
30
General usage instructions
32
.B \-t, \-\-timeout=msecs
33
Retry timeout in ms; if the child dies after the timeout the retry counter is reset (default 10000 ms = 10 s)
31 34
.TP 8
32 35
.B \-v, --version
33 36
Show version and exit
34 37
.SH SIGNAL HANDLING
35
multiwatch will forward the signal USR2; if it receives HUP,INT,QUIT,TERM or USR1, it will send the
36
signal from --signal to the children; if it receives another signal from that group, it will forward the
37
signal again.
38
If multiwatch receives a USR1 or USR2 signal, it will propagate the signal to its children.
39

  
40
If multiwatch receives a HUP, INT, QUIT, TERM signal, it will propagate the signal to its children unless a preferred signal was nominated, in which case that signal will be propagated instead.
41

  
42
A HUP signal will also cause multiwatch to restart any children not running (i.e. those which had previously failed too often and are no longer restarted automatically).
43

  
44
The first INT, QUIT or TERM signal places multiwatch into "shutdown" mode. When in this mode, multiwatch will not restart children and will propagate further signals without regard for any nominated preferred signal.
38 45

  
39
A signal of the group HUP,INT,QUIT,TERM and USR1 tells multiwatch to go into "shutdown" mode, so it won't
40
restart children after receiving it.
41 46
.SH EXAMPLE
42 47
.TP 8
43 48
Spawn 2 rails instances on the same FastCGI socket (and supervise them):
multiwatch.c
24 24

  
25 25
	gint forks;
26 26

  
27
	gint persist;
28

  
27 29
	/* how many times we try to spawn a child */
28 30
	gint retry;
29 31

  
......
66 68
static options opts = {
67 69
	/* app:     */ NULL,
68 70
	/* forks:   */ 1,
71
	/* persist: */ FALSE,
69 72
	/* retry:   */ 3,
70 73
	/* timeout: */ 10000,
71 74
	/* version: */ FALSE,
......
101 104
	return -1;
102 105
}
103 106

  
104
static void forward_sig_cb(struct ev_loop *loop, ev_signal *w, int revents) {
105
	data *d = (data*) w->data;
106
	UNUSED(loop);
107
	UNUSED(revents);
108

  
109
	for (gint i = 0; i < opts.forks; i++) {
110
		if (d->children[i].pid != -1) {
111
			kill(d->children[i].pid, w->signum);
112
		}
113
	}
114
}
115

  
116
static void terminate_forward_sig_cb(struct ev_loop *loop, ev_signal *w, int revents) {
117
	data *d = (data*) w->data;
118
	gint signum = opts.sig_nice_kill; /* terminate children with "nice" signal */
119
	UNUSED(loop);
120
	UNUSED(revents);
121

  
122
	/* on second signal forward original signal */
123
	if (d->shutdown || signum < 0) {
124
		signum = w->signum;
125
	}
126
	d->shutdown = TRUE;
127
	opts.sig_nice_kill = -1;
128

  
129
	for (gint i = 0; i < opts.forks; i++) {
130
		if (d->children[i].pid != -1) {
131
			kill(d->children[i].pid, signum);
132
		}
133
	}
134

  
135
}
136

  
137 107
static void spawn(child* c) {
138 108
	pid_t pid;
139 109

  
140 110
	if (c->tries++ > opts.retry) {
141
		g_printerr("Child[%i] died to often, not forking again\n", c->id);
111
		g_printerr("Child[%i] died too often, will not respawn\n", c->id);
142 112
		return;
143 113
	}
144 114

  
......
190 160
	if (c->d->shutdown) return;
191 161

  
192 162
	if (ev_now(c->d->loop) - c->last_spawn > (opts.retry_timeout_ms / (ev_tstamp) 1000)) {
193
		g_printerr("Child[%i] died, respawn\n", c->id);
163
		g_printerr("Child[%i] died old\n", c->id);
194 164
		c->tries = 0;
195 165
	} else {
196
		g_printerr("Spawning child[%i] failed, next try\n", c->id);
166
		g_printerr("Child[%i] died young, tries == %d\n", c->id, c->tries);
197 167
	}
198 168

  
199 169
	spawn(c);
200 170
}
201 171

  
172
static void forward_sig_cb(struct ev_loop *loop, ev_signal *w, int revents) {
173
	data *d = (data*) w->data;
174
	UNUSED(loop);
175
	UNUSED(revents);
176

  
177
	for (gint i = 0; i < opts.forks; i++) {
178
		if (d->children[i].pid != -1) {
179
			kill(d->children[i].pid, w->signum);
180
		}
181
	}
182
}
183

  
184
static void restart_forward_sig_cb(struct ev_loop *loop, ev_signal *w, int revents) {
185
	data *d = (data*) w->data;
186
	gint signum = opts.sig_nice_kill;
187
	UNUSED(loop);
188
	UNUSED(revents);
189

  
190
	/* if in shutdown mode, forward original signal */
191
	if (d->shutdown || signum < 0) {
192
		signum = w->signum;
193
	}
194

  
195
	for (gint i = 0; i < opts.forks; i++) {
196
		d->children[i].tries = 0;
197
		if (d->children[i].pid != -1) {
198
			kill(d->children[i].pid, signum);
199
		} else if (!d->shutdown) {
200
			spawn(&d->children[i]);
201
		}
202
	}
203
}
204

  
205
static void shutdown_forward_sig_cb(struct ev_loop *loop, ev_signal *w, int revents) {
206
	data *d = (data*) w->data;
207
	gint signum = opts.sig_nice_kill;
208
	UNUSED(loop);
209
	UNUSED(revents);
210

  
211
	/* if in shutdown mode, forward original signal */
212
	if (d->shutdown || signum < 0) {
213
		signum = w->signum;
214
	}
215

  
216
	/* go into shutdown mode */
217
	d->shutdown = TRUE;
218
	opts.sig_nice_kill = -1;
219

  
220
	/* disable persistence */
221
	if (opts.persist) {
222
		opts.persist = FALSE;
223
		ev_unref(d->loop);
224
	}
225

  
226
	for (gint i = 0; i < opts.forks; i++) {
227
		if (d->children[i].pid != -1) {
228
			kill(d->children[i].pid, signum);
229
		}
230
	}
231

  
232
}
233

  
202 234
static gboolean parse_use_signal_arg(const gchar *option_name, const gchar *value, gpointer d, GError **error) {
203 235
	gint sig = signame2num(value);
204 236
	UNUSED(option_name);
......
215 247

  
216 248
static const GOptionEntry entries[] = {
217 249
	{ "forks", 'f', 0, G_OPTION_ARG_INT, &opts.forks, "Number of children to fork and watch (default 1)", "children" },
250
	{ "persist", 'p', 0, G_OPTION_ARG_NONE, &opts.persist, "Don't exit when all children fail to run", NULL },
218 251
	{ "retry", 'r', 0, G_OPTION_ARG_INT, &opts.retry, "Number of retries to fork a single child (default 3)", "retries" },
219 252
	{ "timeout", 't', 0, G_OPTION_ARG_INT, &opts.retry_timeout_ms, "Retry timeout in ms; if the child dies after the timeout the retry counter is reset (default 10000)", "ms" },
220 253
	{ "version", 'v', 0, G_OPTION_ARG_NONE, &opts.show_version, "Show version", NULL },
......
253 286
		return -3;
254 287
	}
255 288

  
256
	if (opts.retry < 1) {
289
	if (opts.retry < 0) {
257 290
		g_printerr("Invalid retry argument: %i\n", opts.retry);
258 291
		return -4;
259 292
	}
......
270 303
	d->return_status = 0;
271 304
	d->loop = ev_default_loop(0);
272 305

  
273
#define WATCH_SIG(x) do { ev_signal_init(&d->sig##x, forward_sig_cb, SIG##x); d->sig##x.data = d; ev_signal_start(d->loop, &d->sig##x); ev_unref(d->loop); } while (0)
274
#define WATCH_TERM_SIG(x) do { ev_signal_init(&d->sig##x, terminate_forward_sig_cb, SIG##x); d->sig##x.data = d; ev_signal_start(d->loop, &d->sig##x); ev_unref(d->loop); } while (0)
275
#define UNWATCH_SIG(x) do { ev_ref(d->loop); ev_signal_stop(d->loop, &d->sig##x); } while (0)
306
	if (opts.persist) {
307
		ev_ref(d->loop);
308
	}
309

  
310
#define WATCH_SIG(x, cb) do { ev_signal_init(&d->sig##x, cb, SIG##x); d->sig##x.data = d; ev_signal_start(d->loop, &d->sig##x); ev_unref(d->loop); } while (0)
311
#define UNWATCH_SIG(x) do { ev_signal_stop(d->loop, &d->sig##x); } while (0)
276 312

  
277
	WATCH_TERM_SIG(HUP);
278
	WATCH_TERM_SIG(INT);
279
	WATCH_TERM_SIG(QUIT);
280
	WATCH_TERM_SIG(TERM);
281
	WATCH_TERM_SIG(USR1);
282
	WATCH_SIG(USR2);
313
	WATCH_SIG(HUP,  restart_forward_sig_cb);
314
	WATCH_SIG(INT,  shutdown_forward_sig_cb);
315
	WATCH_SIG(QUIT, shutdown_forward_sig_cb);
316
	WATCH_SIG(TERM, shutdown_forward_sig_cb);
317
	WATCH_SIG(USR1, forward_sig_cb);
318
	WATCH_SIG(USR2, forward_sig_cb);
283 319

  
284 320
	for (gint i = 0; i < opts.forks; i++) {
285 321
		d->children[i].d = d;
    (1-1/1)