0
|
1
|
|
2 /*
|
|
3 * Copyright (C) Igor Sysoev
|
|
4 */
|
|
5
|
|
6
|
|
7 #include <ngx_config.h>
|
|
8 #include <ngx_core.h>
|
|
9 #include <ngx_event.h>
|
|
10 #include <ngx_channel.h>
|
|
11
|
|
12
|
|
13 static void ngx_execute_proc(ngx_cycle_t *cycle, void *data);
|
|
14
|
|
15 ngx_int_t ngx_process_slot;
|
|
16 ngx_socket_t ngx_channel;
|
|
17 ngx_int_t ngx_last_process;
|
|
18 ngx_process_t ngx_processes[NGX_MAX_PROCESSES];
|
|
19
|
|
20
|
|
21 ngx_pid_t ngx_spawn_process(ngx_cycle_t *cycle,
|
|
22 ngx_spawn_proc_pt proc, void *data,
|
|
23 char *name, ngx_int_t respawn)
|
|
24 {
|
|
25 u_long on;
|
|
26 ngx_pid_t pid;
|
|
27 ngx_int_t s;
|
|
28
|
|
29 if (respawn >= 0) {
|
|
30 s = respawn;
|
|
31
|
|
32 } else {
|
|
33 for (s = 0; s < ngx_last_process; s++) {
|
|
34 if (ngx_processes[s].pid == -1) {
|
|
35 break;
|
|
36 }
|
|
37 }
|
|
38
|
|
39 if (s == NGX_MAX_PROCESSES) {
|
|
40 ngx_log_error(NGX_LOG_ALERT, cycle->log, 0,
|
|
41 "no more than %d processes can be spawned",
|
|
42 NGX_MAX_PROCESSES);
|
|
43 return NGX_ERROR;
|
|
44 }
|
|
45 }
|
|
46
|
|
47
|
|
48 if (respawn != NGX_PROCESS_DETACHED) {
|
|
49
|
|
50 /* Solaris 9 still has no AF_LOCAL */
|
|
51
|
|
52 if (socketpair(AF_UNIX, SOCK_STREAM, 0, ngx_processes[s].channel) == -1)
|
|
53 {
|
|
54 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
55 "socketpair() failed while spawning \"%s\"", name);
|
|
56 return NGX_ERROR;
|
|
57 }
|
|
58
|
|
59 ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
|
|
60 "channel %d:%d",
|
|
61 ngx_processes[s].channel[0],
|
|
62 ngx_processes[s].channel[1]);
|
|
63
|
|
64 if (ngx_nonblocking(ngx_processes[s].channel[0]) == -1) {
|
|
65 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
66 ngx_nonblocking_n " failed while spawning \"%s\"",
|
|
67 name);
|
|
68 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
|
69 return NGX_ERROR;
|
|
70 }
|
|
71
|
|
72 if (ngx_nonblocking(ngx_processes[s].channel[1]) == -1) {
|
|
73 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
74 ngx_nonblocking_n " failed while spawning \"%s\"",
|
|
75 name);
|
|
76 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
|
77 return NGX_ERROR;
|
|
78 }
|
|
79
|
|
80 on = 1;
|
|
81 if (ioctl(ngx_processes[s].channel[0], FIOASYNC, &on) == -1) {
|
|
82 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
83 "ioctl(FIOASYNC) failed while spawning \"%s\"", name);
|
|
84 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
|
85 return NGX_ERROR;
|
|
86 }
|
|
87
|
|
88 if (fcntl(ngx_processes[s].channel[0], F_SETOWN, ngx_pid) == -1) {
|
|
89 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
90 "fcntl(F_SETOWN) failed while spawning \"%s\"", name);
|
|
91 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
|
92 return NGX_ERROR;
|
|
93 }
|
|
94
|
|
95 if (fcntl(ngx_processes[s].channel[0], F_SETFD, FD_CLOEXEC) == -1) {
|
|
96 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
97 "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
|
|
98 name);
|
|
99 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
|
100 return NGX_ERROR;
|
|
101 }
|
|
102
|
|
103 if (fcntl(ngx_processes[s].channel[1], F_SETFD, FD_CLOEXEC) == -1) {
|
|
104 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
105 "fcntl(FD_CLOEXEC) failed while spawning \"%s\"",
|
|
106 name);
|
|
107 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
|
108 return NGX_ERROR;
|
|
109 }
|
|
110
|
|
111 ngx_channel = ngx_processes[s].channel[1];
|
|
112
|
|
113 } else {
|
|
114 ngx_processes[s].channel[0] = -1;
|
|
115 ngx_processes[s].channel[1] = -1;
|
|
116 }
|
|
117
|
|
118 ngx_process_slot = s;
|
|
119
|
|
120
|
|
121 pid = fork();
|
|
122
|
|
123 switch (pid) {
|
|
124
|
|
125 case -1:
|
|
126 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
127 "fork() failed while spawning \"%s\"", name);
|
|
128 ngx_close_channel(ngx_processes[s].channel, cycle->log);
|
|
129 return NGX_ERROR;
|
|
130
|
|
131 case 0:
|
|
132 ngx_pid = ngx_getpid();
|
|
133 proc(cycle, data);
|
|
134 break;
|
|
135
|
|
136 default:
|
|
137 break;
|
|
138 }
|
|
139
|
|
140 ngx_log_debug2(NGX_LOG_DEBUG_CORE, cycle->log, 0,
|
|
141 "spawn %s: " PID_T_FMT, name, pid);
|
|
142
|
|
143 ngx_processes[s].pid = pid;
|
|
144 ngx_processes[s].exited = 0;
|
|
145
|
|
146 if (respawn >= 0) {
|
|
147 return pid;
|
|
148 }
|
|
149
|
|
150 ngx_processes[s].proc = proc;
|
|
151 ngx_processes[s].data = data;
|
|
152 ngx_processes[s].name = name;
|
|
153 ngx_processes[s].exiting = 0;
|
|
154
|
|
155 switch (respawn) {
|
|
156
|
|
157 case NGX_PROCESS_RESPAWN:
|
|
158 ngx_processes[s].respawn = 1;
|
|
159 ngx_processes[s].just_respawn = 0;
|
|
160 ngx_processes[s].detached = 0;
|
|
161 break;
|
|
162
|
|
163 case NGX_PROCESS_JUST_RESPAWN:
|
|
164 ngx_processes[s].respawn = 1;
|
|
165 ngx_processes[s].just_respawn = 1;
|
|
166 ngx_processes[s].detached = 0;
|
|
167 break;
|
|
168
|
|
169 case NGX_PROCESS_DETACHED:
|
|
170 ngx_processes[s].respawn = 0;
|
|
171 ngx_processes[s].just_respawn = 0;
|
|
172 ngx_processes[s].detached = 1;
|
|
173 break;
|
|
174 }
|
|
175
|
|
176 if (s == ngx_last_process) {
|
|
177 ngx_last_process++;
|
|
178 }
|
|
179
|
|
180 return pid;
|
|
181 }
|
|
182
|
|
183
|
|
184 ngx_pid_t ngx_execute(ngx_cycle_t *cycle, ngx_exec_ctx_t *ctx)
|
|
185 {
|
|
186 return ngx_spawn_process(cycle, ngx_execute_proc, ctx, ctx->name,
|
|
187 NGX_PROCESS_DETACHED);
|
|
188 }
|
|
189
|
|
190
|
|
191 static void ngx_execute_proc(ngx_cycle_t *cycle, void *data)
|
|
192 {
|
|
193 ngx_exec_ctx_t *ctx = data;
|
|
194
|
|
195 if (execve(ctx->path, ctx->argv, ctx->envp) == -1) {
|
|
196 ngx_log_error(NGX_LOG_ALERT, cycle->log, ngx_errno,
|
|
197 "execve() failed while executing %s \"%s\"",
|
|
198 ctx->name, ctx->path);
|
|
199 }
|
|
200
|
|
201 exit(1);
|
|
202 }
|
|
203
|
|
204
|
|
205 void ngx_process_get_status()
|
|
206 {
|
|
207 int status;
|
|
208 char *process;
|
|
209 ngx_pid_t pid;
|
|
210 ngx_err_t err;
|
|
211 ngx_int_t i;
|
|
212 ngx_uint_t one;
|
|
213 struct timeval tv;
|
|
214 one = 0;
|
|
215
|
|
216 for ( ;; ) {
|
|
217 pid = waitpid(-1, &status, WNOHANG);
|
|
218
|
|
219 if (pid == 0) {
|
|
220 return;
|
|
221 }
|
|
222
|
|
223 if (pid == -1) {
|
|
224 err = ngx_errno;
|
|
225
|
|
226 if (err == NGX_EINTR) {
|
|
227 continue;
|
|
228 }
|
|
229
|
|
230 if (err == NGX_ECHILD && one) {
|
|
231 return;
|
|
232 }
|
|
233
|
|
234 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, errno,
|
|
235 "waitpid() failed");
|
|
236 return;
|
|
237 }
|
|
238
|
|
239
|
|
240 if (ngx_accept_mutex_ptr) {
|
|
241
|
|
242 /*
|
|
243 * unlock the accept mutex if the abnormally exited process
|
|
244 * held it
|
|
245 */
|
|
246
|
|
247 ngx_atomic_cmp_set(ngx_accept_mutex_ptr, pid, 0);
|
|
248 }
|
|
249
|
|
250
|
|
251 one = 1;
|
|
252 process = "unknown process";
|
|
253
|
|
254 for (i = 0; i < ngx_last_process; i++) {
|
|
255 if (ngx_processes[i].pid == pid) {
|
|
256 ngx_processes[i].status = status;
|
|
257 ngx_processes[i].exited = 1;
|
|
258 process = ngx_processes[i].name;
|
|
259 break;
|
|
260 }
|
|
261 }
|
|
262
|
|
263 if (WTERMSIG(status)) {
|
|
264 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
|
265 "%s " PID_T_FMT " exited on signal %d%s",
|
|
266 process, pid, WTERMSIG(status),
|
|
267 WCOREDUMP(status) ? " (core dumped)" : "");
|
|
268
|
|
269 } else {
|
|
270 ngx_log_error(NGX_LOG_INFO, ngx_cycle->log, 0,
|
|
271 "%s " PID_T_FMT " exited with code %d",
|
|
272 process, pid, WEXITSTATUS(status));
|
|
273 }
|
|
274
|
|
275 if (WEXITSTATUS(status) == 2 && ngx_processes[i].respawn) {
|
|
276 ngx_log_error(NGX_LOG_ALERT, ngx_cycle->log, 0,
|
|
277 "%s " PID_T_FMT
|
|
278 " exited with fatal code %d and could not respawn",
|
|
279 process, pid, WEXITSTATUS(status));
|
|
280 ngx_processes[i].respawn = 0;
|
|
281 }
|
|
282 }
|
|
283 }
|