smbd: Call the msg_ctx destructor for background jobs
[obnox/samba/samba-obnox.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 };
38
39 static int background_job_state_destructor(struct background_job_state *s);
40 static void background_job_waited(struct tevent_req *subreq);
41 static void background_job_done(struct tevent_req *subreq);
42 static void background_job_trigger(
43         struct messaging_context *msg, void *private_data, uint32_t msg_type,
44         struct server_id server_id, DATA_BLOB *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 {
55         struct tevent_req *req, *subreq;
56         struct background_job_state *state;
57         size_t i;
58
59         req = tevent_req_create(mem_ctx, &state,
60                                 struct background_job_state);
61         if (req == NULL) {
62                 return NULL;
63         }
64
65         state->ev = ev;
66         state->msg = msg;
67
68         if (num_trigger_msgs != 0) {
69                 state->trigger_msgs = (uint32_t *)talloc_memdup(
70                         state, trigger_msgs,
71                         sizeof(uint32_t) * num_trigger_msgs);
72                 if (tevent_req_nomem(state->trigger_msgs, req)) {
73                         return tevent_req_post(req, ev);
74                 }
75                 state->num_trigger_msgs = num_trigger_msgs;
76         }
77
78         state->fn = fn;
79         state->private_data = private_data;
80
81         state->pipe_fd = -1;
82         talloc_set_destructor(state, background_job_state_destructor);
83
84         for (i=0; i<num_trigger_msgs; i++) {
85                 NTSTATUS status;
86                 status = messaging_register(msg, state, trigger_msgs[i],
87                                             background_job_trigger);
88                 if (tevent_req_nterror(req, status)) {
89                         return tevent_req_post(req, ev);
90                 }
91         }
92
93         subreq = tevent_wakeup_send(
94                 state, state->ev, timeval_current_ofs(initial_wait_sec, 0));
95         if (tevent_req_nomem(subreq, req)) {
96                 return tevent_req_post(req, ev);
97         }
98         tevent_req_set_callback(subreq, background_job_waited, req);
99         state->wakeup_req = subreq;
100         return req;
101 }
102
103 static int background_job_state_destructor(struct background_job_state *state)
104 {
105         size_t i;
106         if (state->pipe_fd != -1) {
107                 close(state->pipe_fd);
108                 state->pipe_fd = -1;
109         }
110         for (i=0; i<state->num_trigger_msgs; i++) {
111                 messaging_deregister(state->msg, state->trigger_msgs[i],
112                                      state);
113         }
114         return 0;
115 }
116
117 static void background_job_trigger(
118         struct messaging_context *msg, void *private_data, uint32_t msg_type,
119         struct server_id server_id, DATA_BLOB *data)
120 {
121         struct background_job_state *state = talloc_get_type_abort(
122                 private_data, struct background_job_state);
123
124         if (state->wakeup_req == NULL) {
125                 return;
126         }
127         if (!tevent_req_set_endtime(state->wakeup_req, state->ev,
128                                     timeval_zero())) {
129                 DEBUG(10, ("tevent_req_set_endtime failed\n"));
130         }
131 }
132
133 static void background_job_waited(struct tevent_req *subreq)
134 {
135         struct tevent_req *req = tevent_req_callback_data(
136                 subreq, struct tevent_req);
137         struct background_job_state *state = tevent_req_data(
138                 req, struct background_job_state);
139         int fds[2];
140         int res;
141         bool ret;
142
143         ret = tevent_wakeup_recv(subreq);
144         TALLOC_FREE(subreq);
145         state->wakeup_req = NULL;
146         if (!ret) {
147                 tevent_req_nterror(req, NT_STATUS_INTERNAL_ERROR);
148                 return;
149         }
150
151         res = pipe(fds);
152         if (res == -1) {
153                 tevent_req_nterror(req, map_nt_error_from_unix(errno));
154                 return;
155         }
156
157         res = fork();
158         if (res == -1) {
159                 int err = errno;
160                 close(fds[0]);
161                 close(fds[1]);
162                 tevent_req_nterror(req, map_nt_error_from_unix(err));
163                 return;
164         }
165
166         if (res == 0) {
167                 /* child */
168
169                 NTSTATUS status;
170                 ssize_t written;
171
172                 close(fds[0]);
173
174                 status = reinit_after_fork(state->msg, state->ev, true);
175                 if (NT_STATUS_IS_OK(status)) {
176                         res = state->fn(state->private_data);
177                 } else {
178                         res = -1;
179                 }
180                 written = write(fds[1], &res, sizeof(res));
181                 if (written == -1) {
182                         _exit(1);
183                 }
184                 TALLOC_FREE(state->msg);
185                 _exit(0);
186         }
187
188         /* parent */
189
190         close(fds[1]);
191         state->pipe_fd = fds[0];
192
193         subreq = read_packet_send(state, state->ev, state->pipe_fd,
194                                   sizeof(int), NULL, NULL);
195         if (tevent_req_nomem(subreq, req)) {
196                 return;
197         }
198         tevent_req_set_callback(subreq, background_job_done, req);
199 }
200
201 static void background_job_done(struct tevent_req *subreq)
202 {
203         struct tevent_req *req = tevent_req_callback_data(
204                 subreq, struct tevent_req);
205         struct background_job_state *state = tevent_req_data(
206                 req, struct background_job_state);
207         ssize_t ret;
208         uint8_t *buf;
209         int err;
210         int wait_secs;
211
212         ret = read_packet_recv(subreq, talloc_tos(), &buf, &err);
213         TALLOC_FREE(subreq);
214         if (ret == -1) {
215                 tevent_req_nterror(req, map_nt_error_from_unix(err));
216                 return;
217         }
218         close(state->pipe_fd);
219         state->pipe_fd = -1;
220         memcpy(&wait_secs, buf, sizeof(wait_secs));
221         if (wait_secs == -1) {
222                 tevent_req_done(req);
223                 return;
224         }
225         subreq = tevent_wakeup_send(
226                 state, state->ev, timeval_current_ofs(wait_secs, 0));
227         if (tevent_req_nomem(subreq, req)) {
228                 return;
229         }
230         tevent_req_set_callback(subreq, background_job_waited, req);
231         state->wakeup_req = subreq;
232 }
233
234 NTSTATUS background_job_recv(struct tevent_req *req)
235 {
236         return tevent_req_simple_recv_ntstatus(req);
237 }