summaryrefslogtreecommitdiff
path: root/src/lighttpd-angel.c
blob: d7dd1f44b91297db9c57c2a225eee6ffb9f6b6a8 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
/**
 * angel process for lighttpd 
 *
 * the purpose is the run as root all the time and handle:
 * - restart on crash
 * - spawn on HUP to allow graceful restart
 * - ...
 *
 * it has to stay safe and small to be trustable
 */

#include <sys/wait.h>

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <time.h>
#include <signal.h>

#define BINPATH SBIN_DIR"/lighttpd"

static siginfo_t last_sigterm_info;
static siginfo_t last_sighup_info;

static volatile sig_atomic_t start_process    = 1;
static volatile sig_atomic_t graceful_restart = 0;
static volatile pid_t pid = -1;

#define UNUSED(x) ( (void)(x) )

static void sigaction_handler(int sig, siginfo_t *si, void *context) {
	int exitcode;

	UNUSED(context);
	switch (sig) {
	case SIGINT: 
	case SIGTERM:
		memcpy(&last_sigterm_info, si, sizeof(*si));

		/** forward the sig to the child */
		kill(pid, sig);
		break;
	case SIGHUP: /** do a graceful restart */
		memcpy(&last_sighup_info, si, sizeof(*si));

		/** do a graceful shutdown on the main process and start a new child */
		kill(pid, SIGINT);

		usleep(5 * 1000); /** wait 5 microsec */
		
		start_process = 1;
		break;
	case SIGCHLD:
		/** a child died, de-combie it */
		wait(&exitcode);
		break;
	}
}

int main(int argc, char **argv) {
	int is_shutdown = 0;
	struct sigaction act;

	UNUSED(argc);

	/**
	 * we are running as root BEWARE
	 */

	memset(&act, 0, sizeof(act));
	act.sa_handler = SIG_IGN;
	sigaction(SIGPIPE, &act, NULL);
	sigaction(SIGUSR1, &act, NULL);

	act.sa_sigaction = sigaction_handler;
	sigemptyset(&act.sa_mask);
	act.sa_flags = SA_SIGINFO;

	sigaction(SIGINT,  &act, NULL);
	sigaction(SIGTERM, &act, NULL);
	sigaction(SIGHUP,  &act, NULL);
	sigaction(SIGALRM, &act, NULL);
	sigaction(SIGCHLD, &act, NULL);

	/* check that the compiled in path has the right user,
	 *
	 * BEWARE: there is a race between the check here and the exec later
	 */

	while (!is_shutdown) {
		int exitcode = 0;

		if (start_process) {
			pid = fork();

			if (0 == pid) {
				/* i'm the child */

				argv[0] = BINPATH;

				execvp(BINPATH, argv);

				exit(1);
			} else if (-1 == pid) {
				/** error */

				return -1;
			}

			/* I'm the angel */
			start_process = 0;
		}
	       
		if ((pid_t)-1 == waitpid(pid, &exitcode, 0)) {
			switch (errno) {
			case EINTR:
				/* someone sent a signal ... 
				 * do we have to shutdown or restart the process */
				break;
			case ECHILD:
				/** 
				 * make sure we are not in a race between the signal handler
				 * and the process restart */
				if (!start_process) is_shutdown = 1;
				break;
			default:
				break;
			}
		} else {
			/** process went away */

			if (WIFEXITED(exitcode)) {
				/** normal exit */

				is_shutdown = 1;

				fprintf(stderr, "%s.%d: child (pid=%d) exited normally with exitcode: %d\n", 
						__FILE__, __LINE__,
						pid,
						WEXITSTATUS(exitcode));

			} else if (WIFSIGNALED(exitcode)) {
				/** got a signal */

				fprintf(stderr, "%s.%d: child (pid=%d) exited unexpectedly with signal %d, restarting\n", 
						__FILE__, __LINE__,
						pid,
						WTERMSIG(exitcode));

				start_process = 1;
			}
		}
	}

	return 0;
}