1 /**
2  * angel process for lighttpd
3  *
4  * the purpose is the run as root all the time and handle:
5  * - restart on crash
6  * - spawn on HUP to allow graceful restart
7  * - ...
8  *
9  * it has to stay safe and small to be trustable
10  */
11 
12 #include <sys/wait.h>
13 
14 #include <stdlib.h>
15 #include <stdio.h>
16 #include <string.h>
17 #include <errno.h>
18 #include <unistd.h>
19 #include <time.h>
20 #include <signal.h>
21 
22 #define BINPATH SBIN_DIR"/lighttpd"
23 
24 static siginfo_t last_sigterm_info;
25 static siginfo_t last_sighup_info;
26 
27 static volatile sig_atomic_t start_process    = 1;
28 static volatile pid_t pid = -1;
29 
30 #define UNUSED(x) ( (void)(x) )
31 
sigaction_handler(int sig,siginfo_t * si,void * context)32 static void sigaction_handler(int sig, siginfo_t *si, void *context) {
33 	int exitcode;
34 
35 	UNUSED(context);
36 	switch (sig) {
37 	case SIGINT:
38 	case SIGTERM:
39 		memcpy(&last_sigterm_info, si, sizeof(*si));
40 
41 		/** forward the sig to the child */
42 		kill(pid, sig);
43 		break;
44 	case SIGHUP: /** do a graceful restart */
45 		memcpy(&last_sighup_info, si, sizeof(*si));
46 
47 		/** do a graceful shutdown on the main process and start a new child */
48 		kill(pid, SIGINT);
49 
50 		usleep(5 * 1000); /** wait 5 microsec */
51 
52 		start_process = 1;
53 		break;
54 	case SIGCHLD:
55 		/** a child died, de-combie it */
56 		wait(&exitcode);
57 		break;
58 	}
59 }
60 
main(int argc,char ** argv)61 int main(int argc, char **argv) {
62 	int is_shutdown = 0;
63 	struct sigaction act;
64 
65 	UNUSED(argc);
66 
67 	/**
68 	 * we are running as root BEWARE
69 	 */
70 
71 	memset(&act, 0, sizeof(act));
72 	act.sa_handler = SIG_IGN;
73 	sigaction(SIGPIPE, &act, NULL);
74 	sigaction(SIGUSR1, &act, NULL);
75 
76 	act.sa_sigaction = sigaction_handler;
77 	sigemptyset(&act.sa_mask);
78 	act.sa_flags = SA_SIGINFO;
79 
80 	sigaction(SIGINT,  &act, NULL);
81 	sigaction(SIGTERM, &act, NULL);
82 	sigaction(SIGHUP,  &act, NULL);
83 	sigaction(SIGALRM, &act, NULL);
84 	sigaction(SIGCHLD, &act, NULL);
85 
86 	/* check that the compiled in path has the right user,
87 	 *
88 	 * BEWARE: there is a race between the check here and the exec later
89 	 */
90 
91 	while (!is_shutdown) {
92 		int exitcode = 0;
93 
94 		if (start_process) {
95 			pid = fork();
96 
97 			if (0 == pid) {
98 				/* i'm the child */
99 
100 				argv[0] = BINPATH;
101 
102 				execvp(BINPATH, argv);
103 
104 				exit(1);
105 			} else if (-1 == pid) {
106 				/** error */
107 
108 				return -1;
109 			}
110 
111 			/* I'm the angel */
112 			start_process = 0;
113 		}
114 
115 		if ((pid_t)-1 == waitpid(pid, &exitcode, 0)) {
116 			switch (errno) {
117 			case EINTR:
118 				/* someone sent a signal ...
119 				 * do we have to shutdown or restart the process */
120 				break;
121 			case ECHILD:
122 				/**
123 				 * make sure we are not in a race between the signal handler
124 				 * and the process restart */
125 				if (!start_process) is_shutdown = 1;
126 				break;
127 			default:
128 				break;
129 			}
130 		} else {
131 			/** process went away */
132 
133 			if (WIFEXITED(exitcode)) {
134 				/** normal exit */
135 
136 				is_shutdown = 1;
137 
138 				fprintf(stderr, "%s.%d: child (pid=%d) exited normally with exitcode: %d\n",
139 						__FILE__, __LINE__,
140 						pid,
141 						WEXITSTATUS(exitcode));
142 
143 			} else if (WIFSIGNALED(exitcode)) {
144 				/** got a signal */
145 
146 				fprintf(stderr, "%s.%d: child (pid=%d) exited unexpectedly with signal %d, restarting\n",
147 						__FILE__, __LINE__,
148 						pid,
149 						WTERMSIG(exitcode));
150 
151 				start_process = 1;
152 			}
153 		}
154 	}
155 
156 	return 0;
157 }
158 
159