Line data Source code
1 : /*
2 : * Printing background queue helper
3 : *
4 : * This program is free software; you can redistribute it and/or modify
5 : * it under the terms of the GNU General Public License as published by
6 : * the Free Software Foundation; either version 3 of the License, or
7 : * (at your option) any later version.
8 : *
9 : * This program is distributed in the hope that it will be useful,
10 : * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 : * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
12 : * GNU General Public License for more details.
13 : *
14 : * You should have received a copy of the GNU General Public License
15 : * along with this program; if not, see <http://www.gnu.org/licenses/>.
16 : */
17 :
18 : #include "replace.h"
19 : #include "system/filesys.h"
20 : #include "lib/util/server_id.h"
21 : #include "source3/locking/share_mode_lock.h"
22 : #include "source3/param/loadparm.h"
23 : #include "source3/param/param_proto.h"
24 : #include "lib/cmdline/cmdline.h"
25 : #include "lib/cmdline/closefrom_except.h"
26 : #include "lib/util/talloc_stack.h"
27 : #include "lib/util/debug.h"
28 : #include "lib/util/signal.h"
29 : #include "lib/util/fault.h"
30 : #include "lib/util/become_daemon.h"
31 : #include "lib/util/charset/charset.h"
32 : #include "lib/util/samba_util.h"
33 : #include "lib/util/sys_rw.h"
34 : #include "lib/util/pidfile.h"
35 : #include "lib/async_req/async_sock.h"
36 : #include "dynconfig/dynconfig.h"
37 : #include "source3/lib/global_contexts.h"
38 : #include "messages.h"
39 : #include "nsswitch/winbind_client.h"
40 : #include "source3/include/auth.h"
41 : #include "source3/lib/util_procid.h"
42 : #include "source3/auth/proto.h"
43 : #include "source3/printing/queue_process.h"
44 : #include "source3/lib/substitute.h"
45 :
46 12 : static void watch_handler(struct tevent_req *req)
47 : {
48 12 : bool *pdone = tevent_req_callback_data_void(req);
49 12 : *pdone = true;
50 12 : }
51 :
52 0 : static void bgqd_sig_term_handler(
53 : struct tevent_context *ev,
54 : struct tevent_signal *se,
55 : int signum,
56 : int count,
57 : void *siginfo,
58 : void *private_data)
59 : {
60 0 : bool *pdone = private_data;
61 0 : *pdone = true;
62 0 : }
63 :
64 110 : static bool ready_signal_filter(
65 : struct messaging_rec *rec, void *private_data)
66 : {
67 110 : pid_t pid = getpid();
68 : ssize_t written;
69 :
70 110 : if (rec->msg_type != MSG_DAEMON_READY_FD) {
71 94 : return false;
72 : }
73 16 : if (rec->num_fds != 1) {
74 0 : return false;
75 : }
76 :
77 16 : written = sys_write(rec->fds[0], &pid, sizeof(pid));
78 16 : if (written != sizeof(pid)) {
79 0 : DBG_ERR("Could not write pid: %s\n", strerror(errno));
80 : }
81 :
82 16 : return false;
83 : }
84 :
85 28 : static int samba_bgqd_pidfile_create(
86 : struct messaging_context *msg_ctx,
87 : const char *progname,
88 : int ready_signal_fd)
89 28 : {
90 28 : const char *piddir = lp_pid_directory();
91 28 : size_t len = strlen(piddir) + strlen(progname) + 6;
92 28 : char pidFile[len];
93 : pid_t existing_pid;
94 : int fd, ret;
95 :
96 28 : snprintf(pidFile,
97 : sizeof(pidFile),
98 : "%s/%s.pid",
99 : piddir, progname);
100 :
101 28 : ret = pidfile_path_create(pidFile, &fd, &existing_pid);
102 28 : if (ret == 0) {
103 12 : struct tevent_req *ready_signal_req = NULL;
104 :
105 : /*
106 : * Listen for fd's sent via MSG_DAEMON_READY_FD:
107 : * Multiple instances of this process might have raced
108 : * for creating the pidfile. Make sure the parent does
109 : * not suffer from this race, reply on behalf of the
110 : * loser of this race.
111 : */
112 :
113 12 : ready_signal_req = messaging_filtered_read_send(
114 : msg_ctx,
115 : messaging_tevent_context(msg_ctx),
116 : msg_ctx,
117 : ready_signal_filter,
118 : NULL);
119 12 : if (ready_signal_req == NULL) {
120 0 : DBG_DEBUG("messaging_filtered_read_send failed\n");
121 0 : pidfile_unlink(piddir, progname);
122 0 : pidfile_fd_close(fd);
123 0 : return ENOMEM;
124 : }
125 :
126 : /* leak fd */
127 12 : return 0;
128 : }
129 :
130 16 : if (ret != EAGAIN) {
131 0 : DBG_DEBUG("pidfile_path_create() failed: %s\n",
132 : strerror(ret));
133 0 : return ret;
134 : }
135 :
136 16 : DBG_DEBUG("%s pid %d exists\n", progname, (int)existing_pid);
137 :
138 16 : if (ready_signal_fd != -1) {
139 : /*
140 : * We lost the race for the pidfile, but someone else
141 : * can report readiness on our behalf.
142 : */
143 16 : NTSTATUS status = messaging_send_iov(
144 : msg_ctx,
145 : pid_to_procid(existing_pid),
146 : MSG_DAEMON_READY_FD,
147 : NULL,
148 : 0,
149 : &ready_signal_fd,
150 : 1);
151 16 : if (!NT_STATUS_IS_OK(status)) {
152 0 : DBG_DEBUG("Could not send ready_signal_fd: %s\n",
153 : nt_errstr(status));
154 : }
155 : }
156 :
157 16 : return EAGAIN;
158 : }
159 :
160 28 : int main(int argc, const char *argv[])
161 : {
162 28 : struct samba_cmdline_daemon_cfg *cmdline_daemon_cfg = NULL;
163 : const struct loadparm_substitution *lp_sub =
164 28 : loadparm_s3_global_substitution();
165 28 : const char *progname = getprogname();
166 28 : TALLOC_CTX *frame = NULL;
167 : poptContext pc;
168 28 : struct messaging_context *msg_ctx = NULL;
169 28 : struct tevent_context *ev = NULL;
170 28 : struct tevent_req *watch_req = NULL;
171 28 : struct tevent_signal *sigterm_handler = NULL;
172 28 : struct bq_state *bq = NULL;
173 28 : int log_stdout = 0;
174 28 : int ready_signal_fd = -1;
175 28 : int watch_fd = -1;
176 : NTSTATUS status;
177 : int ret;
178 : bool ok;
179 28 : bool done = false;
180 28 : int exitcode = 1;
181 :
182 84 : struct poptOption long_options[] = {
183 : POPT_AUTOHELP
184 28 : POPT_COMMON_SAMBA
185 28 : POPT_COMMON_DAEMON
186 :
187 : /*
188 : * File descriptor to write the PID of the helper
189 : * process to
190 : */
191 : {
192 : .longName = "ready-signal-fd",
193 : .argInfo = POPT_ARG_INT,
194 : .arg = &ready_signal_fd,
195 : .descrip = "Fd to signal readiness to" ,
196 : },
197 :
198 : /*
199 : * Read end of a pipe held open by the parent
200 : * smbd. Exit this process when it becomes readable.
201 : */
202 : {
203 : .longName = "parent-watch-fd",
204 : .argInfo = POPT_ARG_INT,
205 : .arg = &watch_fd,
206 : .descrip = "Fd to watch for exiting",
207 : },
208 : POPT_TABLEEND
209 : };
210 :
211 : {
212 28 : const char *fd_params[] = {
213 : "ready-signal-fd", "parent-watch-fd",
214 : };
215 :
216 28 : closefrom_except_fd_params(
217 : 3, ARRAY_SIZE(fd_params), fd_params, argc, argv);
218 : }
219 :
220 28 : talloc_enable_null_tracking();
221 28 : frame = talloc_stackframe();
222 28 : umask(0);
223 28 : set_remote_machine_name("smbd-bgqd", true);
224 :
225 28 : ok = samba_cmdline_init(frame,
226 : SAMBA_CMDLINE_CONFIG_SERVER,
227 : true /* require_smbconf */);
228 28 : if (!ok) {
229 0 : DBG_ERR("Failed to setup cmdline parser!\n");
230 0 : exit(ENOMEM);
231 : }
232 :
233 28 : cmdline_daemon_cfg = samba_cmdline_get_daemon_cfg();
234 :
235 28 : pc = samba_popt_get_context(progname,
236 : argc,
237 : argv,
238 : long_options,
239 : 0);
240 28 : if (pc == NULL) {
241 0 : DBG_ERR("Failed to get popt context!\n");
242 0 : exit(ENOMEM);
243 : }
244 :
245 28 : ret = poptGetNextOpt(pc);
246 28 : if (ret < -1) {
247 0 : fprintf(stderr, "invalid options: %s\n", poptStrerror(ret));
248 0 : goto done;
249 : }
250 :
251 28 : poptFreeContext(pc);
252 :
253 28 : log_stdout = (debug_get_log_type() == DEBUG_STDOUT);
254 :
255 : /* main process will notify systemd */
256 28 : daemon_sd_notifications(false);
257 :
258 28 : if (!cmdline_daemon_cfg->fork) {
259 28 : daemon_status(progname, "Starting process ... ");
260 : } else {
261 0 : become_daemon(true,
262 0 : cmdline_daemon_cfg->no_process_group,
263 : log_stdout);
264 : }
265 :
266 28 : BlockSignals(true, SIGPIPE);
267 :
268 28 : smb_init_locale();
269 28 : dump_core_setup(progname, lp_logfile(frame, lp_sub));
270 :
271 28 : msg_ctx = global_messaging_context();
272 28 : if (msg_ctx == NULL) {
273 0 : DBG_ERR("messaging_init() failed\n");
274 0 : goto done;
275 : }
276 28 : ev = messaging_tevent_context(msg_ctx);
277 :
278 28 : ret = samba_bgqd_pidfile_create(msg_ctx, progname, ready_signal_fd);
279 28 : if (ret != 0) {
280 16 : goto done;
281 : }
282 :
283 12 : if (watch_fd != -1) {
284 12 : watch_req = wait_for_read_send(ev, ev, watch_fd, true);
285 12 : if (watch_req == NULL) {
286 0 : fprintf(stderr, "tevent_add_fd failed\n");
287 0 : goto done;
288 : }
289 12 : tevent_req_set_callback(watch_req, watch_handler, &done);
290 : }
291 :
292 12 : (void)winbind_off();
293 12 : ok = init_guest_session_info(frame);
294 12 : (void)winbind_on();
295 12 : if (!ok) {
296 0 : DBG_ERR("init_guest_session_info failed\n");
297 0 : goto done;
298 : }
299 :
300 12 : (void)winbind_off();
301 12 : status = init_system_session_info(frame);
302 12 : (void)winbind_on();
303 12 : if (!NT_STATUS_IS_OK(status)) {
304 0 : DBG_ERR("init_system_session_info failed: %s\n",
305 : nt_errstr(status));
306 0 : goto done;
307 : }
308 :
309 12 : sigterm_handler = tevent_add_signal(
310 : ev, frame, SIGTERM, 0, bgqd_sig_term_handler, &done);
311 12 : if (sigterm_handler == NULL) {
312 0 : DBG_ERR("Could not install SIGTERM handler\n");
313 0 : goto done;
314 : }
315 :
316 12 : bq = register_printing_bq_handlers(frame, msg_ctx);
317 12 : if (bq == NULL) {
318 0 : DBG_ERR("Could not register bq handlers\n");
319 0 : goto done;
320 : }
321 :
322 12 : ok = locking_init();
323 12 : if (!ok) {
324 0 : DBG_ERR("locking_init failed\n");
325 0 : goto done;
326 : }
327 :
328 12 : if (ready_signal_fd != -1) {
329 12 : pid_t pid = getpid();
330 : ssize_t written;
331 :
332 12 : written = sys_write(ready_signal_fd, &pid, sizeof(pid));
333 12 : if (written != sizeof(pid)) {
334 0 : DBG_ERR("Reporting readiness failed\n");
335 0 : goto done;
336 : }
337 12 : close(ready_signal_fd);
338 12 : ready_signal_fd = -1;
339 : }
340 :
341 3773 : while (!done) {
342 3761 : TALLOC_CTX *tmp = talloc_stackframe();
343 3761 : ret = tevent_loop_once(ev);
344 3761 : TALLOC_FREE(tmp);
345 3761 : if (ret != 0) {
346 0 : DBG_ERR("tevent_loop_once failed\n");
347 0 : break;
348 : }
349 : }
350 :
351 12 : exitcode = 0;
352 28 : done:
353 28 : TALLOC_FREE(watch_req);
354 28 : TALLOC_FREE(bq);
355 28 : TALLOC_FREE(sigterm_handler);
356 28 : global_messaging_context_free();
357 28 : TALLOC_FREE(frame);
358 28 : return exitcode;
359 : }
|