Coverage Report

Created: 2026-06-07 07:07

next uncovered line (L), next uncovered region (R), next uncovered branch (B)
/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
}