Line data Source code
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 89 : 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 : {
55 1 : struct tevent_req *req, *subreq;
56 1 : struct background_job_state *state;
57 1 : size_t i;
58 :
59 89 : req = tevent_req_create(mem_ctx, &state,
60 : struct background_job_state);
61 89 : if (req == NULL) {
62 0 : return NULL;
63 : }
64 :
65 89 : state->ev = ev;
66 89 : state->msg = msg;
67 :
68 89 : if (num_trigger_msgs != 0) {
69 1 : state->trigger_msgs = (uint32_t *)talloc_memdup(
70 : state, trigger_msgs,
71 : sizeof(uint32_t) * num_trigger_msgs);
72 1 : if (tevent_req_nomem(state->trigger_msgs, req)) {
73 0 : return tevent_req_post(req, ev);
74 : }
75 1 : state->num_trigger_msgs = num_trigger_msgs;
76 : }
77 :
78 89 : state->fn = fn;
79 89 : state->private_data = private_data;
80 :
81 89 : state->pipe_fd = -1;
82 89 : talloc_set_destructor(state, background_job_state_destructor);
83 :
84 91 : for (i=0; i<num_trigger_msgs; i++) {
85 1 : subreq = messaging_filtered_read_send(
86 : state, ev, msg, background_job_trigger, state);
87 1 : if (tevent_req_nomem(subreq, req)) {
88 0 : return tevent_req_post(req, ev);
89 : }
90 : }
91 :
92 89 : subreq = tevent_wakeup_send(
93 89 : state, state->ev, timeval_current_ofs(initial_wait_sec, 0));
94 89 : if (tevent_req_nomem(subreq, req)) {
95 0 : return tevent_req_post(req, ev);
96 : }
97 89 : tevent_req_set_callback(subreq, background_job_waited, req);
98 89 : state->wakeup_req = subreq;
99 89 : return req;
100 : }
101 :
102 31552 : static int background_job_state_destructor(struct background_job_state *state)
103 : {
104 31552 : TALLOC_FREE(state->pipe_req);
105 31552 : if (state->pipe_fd != -1) {
106 0 : close(state->pipe_fd);
107 0 : state->pipe_fd = -1;
108 : }
109 :
110 31552 : return 0;
111 : }
112 :
113 0 : static bool background_job_trigger(
114 : struct messaging_rec *rec, void *private_data)
115 : {
116 0 : struct background_job_state *state = talloc_get_type_abort(
117 : private_data, struct background_job_state);
118 0 : size_t i;
119 :
120 0 : if (state->wakeup_req == NULL) {
121 0 : return false;
122 : }
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 : }
127 : }
128 0 : if (i == state->num_trigger_msgs) {
129 0 : return false;
130 : }
131 0 : if (!tevent_req_set_endtime(state->wakeup_req, state->ev,
132 : timeval_zero())) {
133 0 : DEBUG(10, ("tevent_req_set_endtime failed\n"));
134 : }
135 0 : return false;
136 : }
137 :
138 45 : static void background_job_waited(struct tevent_req *subreq)
139 : {
140 45 : struct tevent_req *req = tevent_req_callback_data(
141 : subreq, struct tevent_req);
142 45 : struct background_job_state *state = tevent_req_data(
143 : req, struct background_job_state);
144 0 : int fds[2];
145 0 : int res;
146 0 : bool ret;
147 :
148 45 : ret = tevent_wakeup_recv(subreq);
149 45 : TALLOC_FREE(subreq);
150 45 : state->wakeup_req = NULL;
151 45 : if (!ret) {
152 0 : tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
153 0 : return;
154 : }
155 :
156 45 : res = pipe(fds);
157 45 : if (res == -1) {
158 0 : tevent_req_nterror(req, map_nt_error_from_unix(errno));
159 0 : return;
160 : }
161 :
162 45 : res = fork();
163 45 : 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 : }
170 :
171 45 : 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 : } else {
183 0 : res = -1;
184 : }
185 0 : written = write(fds[1], &res, sizeof(res));
186 0 : if (written == -1) {
187 0 : _exit(1);
188 : }
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 : }
203 :
204 : /* parent */
205 :
206 45 : close(fds[1]);
207 45 : state->pipe_fd = fds[0];
208 :
209 45 : subreq = read_packet_send(state, state->ev, state->pipe_fd,
210 : sizeof(int), NULL, NULL);
211 45 : if (tevent_req_nomem(subreq, req)) {
212 0 : return;
213 : }
214 45 : tevent_req_set_callback(subreq, background_job_done, req);
215 45 : state->pipe_req = subreq;
216 : }
217 :
218 45 : static void background_job_done(struct tevent_req *subreq)
219 : {
220 45 : struct tevent_req *req = tevent_req_callback_data(
221 : subreq, struct tevent_req);
222 45 : struct background_job_state *state = tevent_req_data(
223 : 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 45 : state->pipe_req = NULL;
230 :
231 45 : ret = read_packet_recv(subreq, talloc_tos(), &buf, &err);
232 45 : TALLOC_FREE(subreq);
233 45 : if (ret == -1) {
234 0 : tevent_req_nterror(req, map_nt_error_from_unix(err));
235 0 : return;
236 : }
237 45 : close(state->pipe_fd);
238 45 : state->pipe_fd = -1;
239 45 : memcpy(&wait_secs, buf, sizeof(wait_secs));
240 45 : if (wait_secs == -1) {
241 0 : tevent_req_done(req);
242 0 : return;
243 : }
244 45 : subreq = tevent_wakeup_send(
245 : state, state->ev, timeval_current_ofs(wait_secs, 0));
246 45 : if (tevent_req_nomem(subreq, req)) {
247 0 : return;
248 : }
249 45 : tevent_req_set_callback(subreq, background_job_waited, req);
250 45 : state->wakeup_req = subreq;
251 : }
252 :
253 0 : NTSTATUS background_job_recv(struct tevent_req *req)
254 : {
255 0 : return tevent_req_simple_recv_ntstatus(req);
256 : }
|