4b96fc43092d427c18295b6da7d7297591e4f1eb
[samba.git] / source3 / printing / samba-bgqd.c
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/util/talloc_stack.h"
26 #include "lib/util/debug.h"
27 #include "lib/util/signal.h"
28 #include "lib/util/fault.h"
29 #include "lib/util/become_daemon.h"
30 #include "lib/util/charset/charset.h"
31 #include "lib/util/samba_util.h"
32 #include "lib/util/sys_rw.h"
33 #include "lib/util/pidfile.h"
34 #include "lib/async_req/async_sock.h"
35 #include "dynconfig/dynconfig.h"
36 #include "source3/lib/global_contexts.h"
37 #include "messages.h"
38 #include "nsswitch/winbind_client.h"
39 #include "source3/include/auth.h"
40 #include "source3/lib/util_procid.h"
41 #include "source3/auth/proto.h"
42 #include "source3/printing/queue_process.h"
43
44 static void watch_handler(struct tevent_req *req)
45 {
46         bool *pdone = tevent_req_callback_data_void(req);
47         *pdone = true;
48 }
49
50 static void bgqd_sig_term_handler(
51         struct tevent_context *ev,
52         struct tevent_signal *se,
53         int signum,
54         int count,
55         void *siginfo,
56         void *private_data)
57 {
58         bool *pdone = private_data;
59         *pdone = true;
60 }
61
62 static bool ready_signal_filter(
63         struct messaging_rec *rec, void *private_data)
64 {
65         pid_t pid = getpid();
66         ssize_t written;
67
68         if (rec->msg_type != MSG_DAEMON_READY_FD) {
69                 return false;
70         }
71         if (rec->num_fds != 1) {
72                 return false;
73         }
74
75         written = sys_write(rec->fds[0], &pid, sizeof(pid));
76         if (written != sizeof(pid)) {
77                 DBG_ERR("Could not write pid: %s\n", strerror(errno));
78         }
79
80         return false;
81 }
82
83 static int samba_bgqd_pidfile_create(
84         struct messaging_context *msg_ctx,
85         const char *progname,
86         int ready_signal_fd)
87 {
88         const char *piddir = lp_pid_directory();
89         size_t len = strlen(piddir) + strlen(progname) + 6;
90         char pidFile[len];
91         pid_t existing_pid;
92         int fd, ret;
93
94         snprintf(pidFile,
95                  sizeof(pidFile),
96                  "%s/%s.pid",
97                  piddir, progname);
98
99         ret = pidfile_path_create(pidFile, &fd, &existing_pid);
100         if (ret == 0) {
101                 struct tevent_req *ready_signal_req = NULL;
102
103                 /*
104                  * Listen for fd's sent via MSG_DAEMON_READY_FD:
105                  * Multiple instances of this process might have raced
106                  * for creating the pidfile. Make sure the parent does
107                  * not suffer from this race, reply on behalf of the
108                  * loser of this race.
109                  */
110
111                 ready_signal_req = messaging_filtered_read_send(
112                         msg_ctx,
113                         messaging_tevent_context(msg_ctx),
114                         msg_ctx,
115                         ready_signal_filter,
116                         NULL);
117                 if (ready_signal_req == NULL) {
118                         DBG_DEBUG("messaging_filtered_read_send failed\n");
119                         pidfile_unlink(piddir, progname);
120                         pidfile_fd_close(fd);
121                         return ENOMEM;
122                 }
123
124                 /* leak fd */
125                 return 0;
126         }
127
128         if (ret != EAGAIN) {
129                 DBG_DEBUG("pidfile_path_create() failed: %s\n",
130                           strerror(ret));
131                 return ret;
132         }
133
134         DBG_DEBUG("%s pid %d exists\n", progname, (int)existing_pid);
135
136         if (ready_signal_fd != -1) {
137                 /*
138                  * We lost the race for the pidfile, but someone else
139                  * can report readiness on our behalf.
140                  */
141                 NTSTATUS status = messaging_send_iov(
142                         msg_ctx,
143                         pid_to_procid(existing_pid),
144                         MSG_DAEMON_READY_FD,
145                         NULL,
146                         0,
147                         &ready_signal_fd,
148                         1);
149                 if (!NT_STATUS_IS_OK(status)) {
150                         DBG_DEBUG("Could not send ready_signal_fd: %s\n",
151                                   nt_errstr(status));
152                 }
153         }
154
155         return EAGAIN;
156 }
157
158 static int closeall_except(int *fds, size_t num_fds)
159 {
160         size_t i;
161         int max_keep = -1;
162         int fd, ret;
163
164         for (i=0; i<num_fds; i++) {
165                 max_keep = MAX(max_keep, fds[i]);
166         }
167         if (max_keep == -1) {
168                 return 0;
169         }
170
171         for (fd = 0; fd < max_keep; fd++) {
172                 bool keep = false;
173
174                 /*
175                  * O(num_fds*max_keep), but we expect the number of
176                  * fds to keep to be very small, typically 0,1,2 and
177                  * very few more.
178                  */
179                 for (i=0; i<num_fds; i++) {
180                         if (fd == fds[i]) {
181                                 keep = true;
182                                 break;
183                         }
184                 }
185                 if (keep) {
186                         continue;
187                 }
188                 ret = close(fd);
189                 if ((ret == -1) && (errno != EBADF)) {
190                         return errno;
191                 }
192         }
193
194         closefrom(max_keep+1);
195         return 0;
196 }
197
198 int main(int argc, const char *argv[])
199 {
200         const struct loadparm_substitution *lp_sub =
201                 loadparm_s3_global_substitution();
202         const char *progname = getprogname();
203         TALLOC_CTX *frame = NULL;
204         poptContext pc;
205         struct messaging_context *msg_ctx = NULL;
206         struct tevent_context *ev = NULL;
207         struct tevent_req *watch_req = NULL;
208         struct tevent_signal *sigterm_handler = NULL;
209         struct bq_state *bq = NULL;
210         int foreground = 0;
211         int no_process_group = 0;
212         int log_stdout = 0;
213         int ready_signal_fd = -1;
214         int watch_fd = -1;
215         NTSTATUS status;
216         int ret;
217         bool ok;
218         bool done = false;
219         int exitcode = 1;
220
221         struct poptOption long_options[] = {
222                 POPT_AUTOHELP
223                 POPT_COMMON_SAMBA
224                 {
225                         .longName   = "foreground",
226                         .shortName  = 'F',
227                         .argInfo    = POPT_ARG_NONE,
228                         .arg        = &foreground,
229                         .descrip    = "Run daemon in foreground "
230                                       "(for daemontools, etc.)",
231                 },
232                 {
233                         .longName   = "no-process-group",
234                         .shortName  = '\0',
235                         .argInfo    = POPT_ARG_NONE,
236                         .arg        = &no_process_group,
237                         .descrip    = "Don't create a new process group" ,
238                 },
239
240                 /*
241                  * File descriptor to write the PID of the helper
242                  * process to
243                  */
244                 {
245                         .longName   = "ready-signal-fd",
246                         .argInfo    = POPT_ARG_INT,
247                         .arg        = &ready_signal_fd,
248                         .descrip    = "Fd to signal readiness to" ,
249                 },
250
251                 /*
252                  * Read end of a pipe held open by the parent
253                  * smbd. Exit this process when it becomes readable.
254                  */
255                 {
256                         .longName   = "parent-watch-fd",
257                         .argInfo    = POPT_ARG_INT,
258                         .arg        = &watch_fd,
259                         .descrip    = "Fd to watch for exiting",
260                 },
261                 POPT_TABLEEND
262         };
263
264         frame = talloc_stackframe();
265
266         umask(0);
267
268         ok = samba_cmdline_init(frame,
269                                 SAMBA_CMDLINE_CONFIG_SERVER,
270                                 true /* require_smbconf */);
271         if (!ok) {
272                 DBG_ERR("Failed to setup cmdline parser!\n");
273                 exit(ENOMEM);
274         }
275
276         pc = samba_popt_get_context(progname,
277                                     argc,
278                                     argv,
279                                     long_options,
280                                     0);
281         if (pc == NULL) {
282                 DBG_ERR("Failed to get popt context!\n");
283                 exit(ENOMEM);
284         }
285
286         ret = poptGetNextOpt(pc);
287         if (ret < -1) {
288                 fprintf(stderr, "invalid options: %s\n", poptStrerror(ret));
289                 goto done;
290         }
291
292         poptFreeContext(pc);
293
294         log_stdout = (debug_get_log_type() == DEBUG_STDOUT);
295
296         {
297                 int keep[] = { 0, 1, 2, ready_signal_fd, watch_fd };
298                 ret = closeall_except(keep, ARRAY_SIZE(keep));
299                 if (ret != 0) {
300                         fprintf(stderr,
301                                 "Could not close fds: %s\n",
302                                 strerror(ret));
303                         goto done;
304                 }
305         }
306
307         if (foreground) {
308                 daemon_status(progname, "Starting process ... ");
309         } else {
310                 become_daemon(true, no_process_group, log_stdout);
311         }
312
313         if (log_stdout) {
314                 setup_logging(progname, DEBUG_STDOUT);
315         } else {
316                 setup_logging(progname, DEBUG_FILE);
317         }
318
319         BlockSignals(true, SIGPIPE);
320
321         smb_init_locale();
322         dump_core_setup(progname, lp_logfile(frame, lp_sub));
323
324         msg_ctx = global_messaging_context();
325         if (msg_ctx == NULL) {
326                 DBG_ERR("messaging_init() failed\n");
327                 goto done;
328         }
329         ev = messaging_tevent_context(msg_ctx);
330
331         ret = samba_bgqd_pidfile_create(msg_ctx, progname, ready_signal_fd);
332         if (ret != 0) {
333                 goto done;
334         }
335
336         if (watch_fd != -1) {
337                 watch_req = wait_for_read_send(ev, ev, watch_fd, true);
338                 if (watch_req == NULL) {
339                         fprintf(stderr, "tevent_add_fd failed\n");
340                         goto done;
341                 }
342                 tevent_req_set_callback(watch_req, watch_handler, &done);
343         }
344
345         (void)winbind_off();
346         ok = init_guest_session_info(frame);
347         (void)winbind_on();
348         if (!ok) {
349                 DBG_ERR("init_guest_session_info failed\n");
350                 goto done;
351         }
352
353         (void)winbind_off();
354         status = init_system_session_info(frame);
355         (void)winbind_on();
356         if (!NT_STATUS_IS_OK(status)) {
357                 DBG_ERR("init_system_session_info failed: %s\n",
358                         nt_errstr(status));
359                 goto done;
360         }
361
362         sigterm_handler = tevent_add_signal(
363                 ev, frame, SIGTERM, 0, bgqd_sig_term_handler, &done);
364         if (sigterm_handler == NULL) {
365                 DBG_ERR("Could not install SIGTERM handler\n");
366                 goto done;
367         }
368
369         bq = register_printing_bq_handlers(frame, msg_ctx);
370         if (bq == NULL) {
371                 DBG_ERR("Could not register bq handlers\n");
372                 goto done;
373         }
374
375         ok = locking_init();
376         if (!ok) {
377                 DBG_ERR("locking_init failed\n");
378                 goto done;
379         }
380
381         if (ready_signal_fd != -1) {
382                 pid_t pid = getpid();
383                 ssize_t written;
384
385                 written = sys_write(ready_signal_fd, &pid, sizeof(pid));
386                 if (written != sizeof(pid)) {
387                         DBG_ERR("Reporting readiness failed\n");
388                         goto done;
389                 }
390                 close(ready_signal_fd);
391                 ready_signal_fd = -1;
392         }
393
394         while (!done) {
395                 TALLOC_CTX *tmp = talloc_stackframe();
396                 ret = tevent_loop_once(ev);
397                 TALLOC_FREE(tmp);
398                 if (ret != 0) {
399                         DBG_ERR("tevent_loop_once failed\n");
400                         break;
401                 }
402         }
403
404         exitcode = 0;
405 done:
406         TALLOC_FREE(watch_req);
407         TALLOC_FREE(bq);
408         TALLOC_FREE(sigterm_handler);
409         global_messaging_context_free();
410         TALLOC_FREE(frame);
411         return exitcode;
412 }