pyldb: avoid segfault when adding an element with no name
[kamenim/samba-autobuild/.git] / source3 / lib / background.c
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 void background_job_trigger(
44         struct messaging_context *msg, void *private_data, uint32_t msg_type,
45         struct server_id server_id, DATA_BLOB *data);
46
47 struct tevent_req *background_job_send(TALLOC_CTX *mem_ctx,
48                                        struct tevent_context *ev,
49                                        struct messaging_context *msg,
50                                        uint32_t *trigger_msgs,
51                                        size_t num_trigger_msgs,
52                                        time_t initial_wait_sec,
53                                        int (*fn)(void *private_data),
54                                        void *private_data)
55 {
56         struct tevent_req *req, *subreq;
57         struct background_job_state *state;
58         size_t i;
59
60         req = tevent_req_create(mem_ctx, &state,
61                                 struct background_job_state);
62         if (req == NULL) {
63                 return NULL;
64         }
65
66         state->ev = ev;
67         state->msg = msg;
68
69         if (num_trigger_msgs != 0) {
70                 state->trigger_msgs = (uint32_t *)talloc_memdup(
71                         state, trigger_msgs,
72                         sizeof(uint32_t) * num_trigger_msgs);
73                 if (tevent_req_nomem(state->trigger_msgs, req)) {
74                         return tevent_req_post(req, ev);
75                 }
76                 state->num_trigger_msgs = num_trigger_msgs;
77         }
78
79         state->fn = fn;
80         state->private_data = private_data;
81
82         state->pipe_fd = -1;
83         talloc_set_destructor(state, background_job_state_destructor);
84
85         for (i=0; i<num_trigger_msgs; i++) {
86                 NTSTATUS status;
87                 status = messaging_register(msg, state, trigger_msgs[i],
88                                             background_job_trigger);
89                 if (tevent_req_nterror(req, status)) {
90                         return tevent_req_post(req, ev);
91                 }
92         }
93
94         subreq = tevent_wakeup_send(
95                 state, state->ev, timeval_current_ofs(initial_wait_sec, 0));
96         if (tevent_req_nomem(subreq, req)) {
97                 return tevent_req_post(req, ev);
98         }
99         tevent_req_set_callback(subreq, background_job_waited, req);
100         state->wakeup_req = subreq;
101         return req;
102 }
103
104 static int background_job_state_destructor(struct background_job_state *state)
105 {
106         size_t i;
107
108         TALLOC_FREE(state->pipe_req);
109         if (state->pipe_fd != -1) {
110                 close(state->pipe_fd);
111                 state->pipe_fd = -1;
112         }
113
114         for (i=0; i<state->num_trigger_msgs; i++) {
115                 messaging_deregister(state->msg, state->trigger_msgs[i],
116                                      state);
117         }
118
119         return 0;
120 }
121
122 static void background_job_trigger(
123         struct messaging_context *msg, void *private_data, uint32_t msg_type,
124         struct server_id server_id, DATA_BLOB *data)
125 {
126         struct background_job_state *state = talloc_get_type_abort(
127                 private_data, struct background_job_state);
128
129         if (state->wakeup_req == NULL) {
130                 return;
131         }
132         if (!tevent_req_set_endtime(state->wakeup_req, state->ev,
133                                     timeval_zero())) {
134                 DEBUG(10, ("tevent_req_set_endtime failed\n"));
135         }
136 }
137
138 static void background_job_waited(struct tevent_req *subreq)
139 {
140         struct tevent_req *req = tevent_req_callback_data(
141                 subreq, struct tevent_req);
142         struct background_job_state *state = tevent_req_data(
143                 req, struct background_job_state);
144         int fds[2];
145         int res;
146         bool ret;
147
148         ret = tevent_wakeup_recv(subreq);
149         TALLOC_FREE(subreq);
150         state->wakeup_req = NULL;
151         if (!ret) {
152                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
153                 return;
154         }
155
156         res = pipe(fds);
157         if (res == -1) {
158                 tevent_req_nterror(req, map_nt_error_from_unix(errno));
159                 return;
160         }
161
162         res = fork();
163         if (res == -1) {
164                 int err = errno;
165                 close(fds[0]);
166                 close(fds[1]);
167                 tevent_req_nterror(req, map_nt_error_from_unix(err));
168                 return;
169         }
170
171         if (res == 0) {
172                 /* child */
173
174                 NTSTATUS status;
175                 ssize_t written;
176
177                 close(fds[0]);
178
179                 status = reinit_after_fork(state->msg, state->ev, true, NULL);
180                 if (NT_STATUS_IS_OK(status)) {
181                         res = state->fn(state->private_data);
182                 } else {
183                         res = -1;
184                 }
185                 written = write(fds[1], &res, sizeof(res));
186                 if (written == -1) {
187                         _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                 talloc_free(state->msg);
201                 _exit(0);
202         }
203
204         /* parent */
205
206         close(fds[1]);
207         state->pipe_fd = fds[0];
208
209         subreq = read_packet_send(state, state->ev, state->pipe_fd,
210                                   sizeof(int), NULL, NULL);
211         if (tevent_req_nomem(subreq, req)) {
212                 return;
213         }
214         tevent_req_set_callback(subreq, background_job_done, req);
215         state->pipe_req = subreq;
216 }
217
218 static void background_job_done(struct tevent_req *subreq)
219 {
220         struct tevent_req *req = tevent_req_callback_data(
221                 subreq, struct tevent_req);
222         struct background_job_state *state = tevent_req_data(
223                 req, struct background_job_state);
224         ssize_t ret;
225         uint8_t *buf;
226         int err;
227         int wait_secs;
228
229         state->pipe_req = NULL;
230
231         ret = read_packet_recv(subreq, talloc_tos(), &buf, &err);
232         TALLOC_FREE(subreq);
233         if (ret == -1) {
234                 tevent_req_nterror(req, map_nt_error_from_unix(err));
235                 return;
236         }
237         close(state->pipe_fd);
238         state->pipe_fd = -1;
239         memcpy(&wait_secs, buf, sizeof(wait_secs));
240         if (wait_secs == -1) {
241                 tevent_req_done(req);
242                 return;
243         }
244         subreq = tevent_wakeup_send(
245                 state, state->ev, timeval_current_ofs(wait_secs, 0));
246         if (tevent_req_nomem(subreq, req)) {
247                 return;
248         }
249         tevent_req_set_callback(subreq, background_job_waited, req);
250         state->wakeup_req = subreq;
251 }
252
253 NTSTATUS background_job_recv(struct tevent_req *req)
254 {
255         return tevent_req_simple_recv_ntstatus(req);
256 }