/src/samba/source3/lib/background.c
Line | Count | Source |
1 | | /* |
2 | | Unix SMB/CIFS implementation. |
3 | | Regular background jobs as forked helpers |
4 | | Copyright (C) Volker Lendecke 2012 |
5 | | |
6 | | This program is free software; you can redistribute it and/or modify |
7 | | it under the terms of the GNU General Public License as published by |
8 | | the Free Software Foundation; either version 3 of the License, or |
9 | | (at your option) any later version. |
10 | | |
11 | | This program is distributed in the hope that it will be useful, |
12 | | but WITHOUT ANY WARRANTY; without even the implied warranty of |
13 | | MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
14 | | GNU General Public License for more details. |
15 | | |
16 | | You should have received a copy of the GNU General Public License |
17 | | along with this program. If not, see <http://www.gnu.org/licenses/>. |
18 | | */ |
19 | | |
20 | | #include "includes.h" |
21 | | #include "lib/util/tevent_ntstatus.h" |
22 | | #include "lib/async_req/async_sock.h" |
23 | | #include "include/messages.h" |
24 | | #include "background.h" |
25 | | |
26 | | struct background_job_state { |
27 | | struct tevent_context *ev; |
28 | | struct messaging_context *msg; |
29 | | uint32_t *trigger_msgs; |
30 | | size_t num_trigger_msgs; |
31 | | bool parent_longlived; |
32 | | int (*fn)(void *private_data); |
33 | | void *private_data; |
34 | | |
35 | | struct tevent_req *wakeup_req; |
36 | | int pipe_fd; |
37 | | struct tevent_req *pipe_req; |
38 | | }; |
39 | | |
40 | | static int background_job_state_destructor(struct background_job_state *s); |
41 | | static void background_job_waited(struct tevent_req *subreq); |
42 | | static void background_job_done(struct tevent_req *subreq); |
43 | | static bool background_job_trigger( |
44 | | struct messaging_rec *rec, void *private_data); |
45 | | |
46 | | struct tevent_req *background_job_send(TALLOC_CTX *mem_ctx, |
47 | | struct tevent_context *ev, |
48 | | struct messaging_context *msg, |
49 | | uint32_t *trigger_msgs, |
50 | | size_t num_trigger_msgs, |
51 | | time_t initial_wait_sec, |
52 | | int (*fn)(void *private_data), |
53 | | void *private_data) |
54 | 0 | { |
55 | 0 | struct tevent_req *req, *subreq; |
56 | 0 | struct background_job_state *state; |
57 | 0 | size_t i; |
58 | |
|
59 | 0 | req = tevent_req_create(mem_ctx, &state, |
60 | 0 | struct background_job_state); |
61 | 0 | if (req == NULL) { |
62 | 0 | return NULL; |
63 | 0 | } |
64 | | |
65 | 0 | state->ev = ev; |
66 | 0 | state->msg = msg; |
67 | |
|
68 | 0 | if (num_trigger_msgs != 0) { |
69 | 0 | state->trigger_msgs = (uint32_t *)talloc_memdup( |
70 | 0 | state, trigger_msgs, |
71 | 0 | sizeof(uint32_t) * num_trigger_msgs); |
72 | 0 | if (tevent_req_nomem(state->trigger_msgs, req)) { |
73 | 0 | return tevent_req_post(req, ev); |
74 | 0 | } |
75 | 0 | state->num_trigger_msgs = num_trigger_msgs; |
76 | 0 | } |
77 | | |
78 | 0 | state->fn = fn; |
79 | 0 | state->private_data = private_data; |
80 | |
|
81 | 0 | state->pipe_fd = -1; |
82 | 0 | talloc_set_destructor(state, background_job_state_destructor); |
83 | |
|
84 | 0 | for (i=0; i<num_trigger_msgs; i++) { |
85 | 0 | subreq = messaging_filtered_read_send( |
86 | 0 | state, ev, msg, background_job_trigger, state); |
87 | 0 | if (tevent_req_nomem(subreq, req)) { |
88 | 0 | return tevent_req_post(req, ev); |
89 | 0 | } |
90 | 0 | } |
91 | | |
92 | 0 | subreq = tevent_wakeup_send( |
93 | 0 | state, state->ev, timeval_current_ofs(initial_wait_sec, 0)); |
94 | 0 | if (tevent_req_nomem(subreq, req)) { |
95 | 0 | return tevent_req_post(req, ev); |
96 | 0 | } |
97 | 0 | tevent_req_set_callback(subreq, background_job_waited, req); |
98 | 0 | state->wakeup_req = subreq; |
99 | 0 | return req; |
100 | 0 | } |
101 | | |
102 | | static int background_job_state_destructor(struct background_job_state *state) |
103 | 0 | { |
104 | 0 | TALLOC_FREE(state->pipe_req); |
105 | 0 | if (state->pipe_fd != -1) { |
106 | 0 | close(state->pipe_fd); |
107 | 0 | state->pipe_fd = -1; |
108 | 0 | } |
109 | |
|
110 | 0 | return 0; |
111 | 0 | } |
112 | | |
113 | | static bool background_job_trigger( |
114 | | struct messaging_rec *rec, void *private_data) |
115 | 0 | { |
116 | 0 | struct background_job_state *state = talloc_get_type_abort( |
117 | 0 | private_data, struct background_job_state); |
118 | 0 | size_t i; |
119 | |
|
120 | 0 | if (state->wakeup_req == NULL) { |
121 | 0 | return false; |
122 | 0 | } |
123 | 0 | for (i=0; i<state->num_trigger_msgs; i++) { |
124 | 0 | if (rec->msg_type == state->trigger_msgs[i]) { |
125 | 0 | break; |
126 | 0 | } |
127 | 0 | } |
128 | 0 | if (i == state->num_trigger_msgs) { |
129 | 0 | return false; |
130 | 0 | } |
131 | 0 | if (!tevent_req_set_endtime(state->wakeup_req, state->ev, |
132 | 0 | timeval_zero())) { |
133 | 0 | DEBUG(10, ("tevent_req_set_endtime failed\n")); |
134 | 0 | } |
135 | 0 | return false; |
136 | 0 | } |
137 | | |
138 | | static void background_job_waited(struct tevent_req *subreq) |
139 | 0 | { |
140 | 0 | struct tevent_req *req = tevent_req_callback_data( |
141 | 0 | subreq, struct tevent_req); |
142 | 0 | struct background_job_state *state = tevent_req_data( |
143 | 0 | req, struct background_job_state); |
144 | 0 | int fds[2]; |
145 | 0 | int res; |
146 | 0 | bool ret; |
147 | |
|
148 | 0 | ret = tevent_wakeup_recv(subreq); |
149 | 0 | TALLOC_FREE(subreq); |
150 | 0 | state->wakeup_req = NULL; |
151 | 0 | if (!ret) { |
152 | 0 | tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR); |
153 | 0 | return; |
154 | 0 | } |
155 | | |
156 | 0 | res = pipe(fds); |
157 | 0 | if (res == -1) { |
158 | 0 | tevent_req_nterror(req, map_nt_error_from_unix(errno)); |
159 | 0 | return; |
160 | 0 | } |
161 | | |
162 | 0 | res = fork(); |
163 | 0 | if (res == -1) { |
164 | 0 | int err = errno; |
165 | 0 | close(fds[0]); |
166 | 0 | close(fds[1]); |
167 | 0 | tevent_req_nterror(req, map_nt_error_from_unix(err)); |
168 | 0 | return; |
169 | 0 | } |
170 | | |
171 | 0 | if (res == 0) { |
172 | | /* child */ |
173 | |
|
174 | 0 | NTSTATUS status; |
175 | 0 | ssize_t written; |
176 | |
|
177 | 0 | close(fds[0]); |
178 | |
|
179 | 0 | status = reinit_after_fork(state->msg, state->ev, true); |
180 | 0 | if (NT_STATUS_IS_OK(status)) { |
181 | 0 | res = state->fn(state->private_data); |
182 | 0 | } else { |
183 | 0 | res = -1; |
184 | 0 | } |
185 | 0 | written = write(fds[1], &res, sizeof(res)); |
186 | 0 | if (written == -1) { |
187 | 0 | _exit(1); |
188 | 0 | } |
189 | | |
190 | | /* |
191 | | * No TALLOC_FREE here, messaging_parent_dgm_cleanup_init for |
192 | | * example calls background_job_send with "messaging_context" |
193 | | * as talloc parent. Thus "state" will be freed with the |
194 | | * following talloc_free will have removed "state" when it |
195 | | * returns. TALLOC_FREE will then write a NULL into free'ed |
196 | | * memory. talloc_free() is required although we immediately |
197 | | * exit, the messaging_context's destructor will want to clean |
198 | | * up. |
199 | | */ |
200 | 0 | talloc_free(state->msg); |
201 | 0 | _exit(0); |
202 | 0 | } |
203 | | |
204 | | /* parent */ |
205 | | |
206 | 0 | close(fds[1]); |
207 | 0 | state->pipe_fd = fds[0]; |
208 | |
|
209 | 0 | subreq = read_packet_send(state, state->ev, state->pipe_fd, |
210 | 0 | sizeof(int), NULL, NULL); |
211 | 0 | if (tevent_req_nomem(subreq, req)) { |
212 | 0 | return; |
213 | 0 | } |
214 | 0 | tevent_req_set_callback(subreq, background_job_done, req); |
215 | 0 | state->pipe_req = subreq; |
216 | 0 | } |
217 | | |
218 | | static void background_job_done(struct tevent_req *subreq) |
219 | 0 | { |
220 | 0 | struct tevent_req *req = tevent_req_callback_data( |
221 | 0 | subreq, struct tevent_req); |
222 | 0 | struct background_job_state *state = tevent_req_data( |
223 | 0 | req, struct background_job_state); |
224 | 0 | ssize_t ret; |
225 | 0 | uint8_t *buf; |
226 | 0 | int err; |
227 | 0 | int wait_secs; |
228 | |
|
229 | 0 | state->pipe_req = NULL; |
230 | |
|
231 | 0 | ret = read_packet_recv(subreq, talloc_tos(), &buf, &err); |
232 | 0 | TALLOC_FREE(subreq); |
233 | 0 | if (ret == -1) { |
234 | 0 | tevent_req_nterror(req, map_nt_error_from_unix(err)); |
235 | 0 | return; |
236 | 0 | } |
237 | 0 | close(state->pipe_fd); |
238 | 0 | state->pipe_fd = -1; |
239 | 0 | memcpy(&wait_secs, buf, sizeof(wait_secs)); |
240 | 0 | if (wait_secs == -1) { |
241 | 0 | tevent_req_done(req); |
242 | 0 | return; |
243 | 0 | } |
244 | 0 | subreq = tevent_wakeup_send( |
245 | 0 | state, state->ev, timeval_current_ofs(wait_secs, 0)); |
246 | 0 | if (tevent_req_nomem(subreq, req)) { |
247 | 0 | return; |
248 | 0 | } |
249 | 0 | tevent_req_set_callback(subreq, background_job_waited, req); |
250 | 0 | state->wakeup_req = subreq; |
251 | 0 | } |
252 | | |
253 | | NTSTATUS background_job_recv(struct tevent_req *req) |
254 | 0 | { |
255 | 0 | return tevent_req_simple_recv_ntstatus(req); |
256 | 0 | } |