diff options
author | Volker Lendecke <vl@samba.org> | 2021-04-22 13:39:31 +0200 |
---|---|---|
committer | Jeremy Allison <jra@samba.org> | 2021-05-11 22:56:37 +0000 |
commit | bad19e208ce3b0ff5a9b99245b815423c3f469bd (patch) | |
tree | bb50dd9bc261b15e425bcafd8c4bd5cec221f49d /source3/printing | |
parent | ecf9ba381e7bd2691d0c8a674f519516c63600ff (diff) | |
download | samba-bad19e208ce3b0ff5a9b99245b815423c3f469bd.tar.gz |
printing: Introduce samba-bgqd
This is a separate binary executed from start_background_queue(). As
such it does not really gain much, but the idea is to move all the
code this runs out of the smbd and spoolssd binaries to just link
here.
Signed-off-by: Volker Lendecke <vl@samba.org>
Reviewed-by: Jeremy Allison <jra@samba.org>
Diffstat (limited to 'source3/printing')
-rw-r--r-- | source3/printing/queue_process.c | 132 | ||||
-rw-r--r-- | source3/printing/samba-bgqd.c | 406 |
2 files changed, 462 insertions, 76 deletions
diff --git a/source3/printing/queue_process.c b/source3/printing/queue_process.c index c22c442832d..0867784bf89 100644 --- a/source3/printing/queue_process.c +++ b/source3/printing/queue_process.c @@ -21,9 +21,11 @@ */ #include "includes.h" +#include <spawn.h> #include "smbd/globals.h" #include "include/messages.h" #include "lib/util/util_process.h" +#include "lib/util/sys_rw.h" #include "printing.h" #include "printing/pcap.h" #include "printing/printer_list.h" @@ -204,30 +206,6 @@ static void bq_reopen_logs(char *logfile) reopen_logs(); } -static void bq_sig_term_handler(struct tevent_context *ev, - struct tevent_signal *se, - int signum, - int count, - void *siginfo, - void *private_data) -{ - exit_server_cleanly("termination signal"); -} - -static void bq_setup_sig_term_handler(void) -{ - struct tevent_signal *se; - - se = tevent_add_signal(global_event_context(), - global_event_context(), - SIGTERM, 0, - bq_sig_term_handler, - NULL); - if (!se) { - exit_server("failed to setup SIGTERM handler"); - } -} - static void bq_sig_hup_handler(struct tevent_context *ev, struct tevent_signal *se, int signum, @@ -305,6 +283,7 @@ struct bq_state *register_printing_bq_handlers( { struct bq_state *state = NULL; NTSTATUS status; + bool ok; state = talloc_zero(mem_ctx, struct bq_state); if (state == NULL) { @@ -340,6 +319,11 @@ struct bq_state *register_printing_bq_handlers( goto fail_free_handlers; } + ok = printing_subsystem_queue_tasks(state); + if (!ok) { + goto fail_free_handlers; + } + talloc_set_destructor(state, bq_state_destructor); return state; @@ -358,6 +342,8 @@ fail: return NULL; } +extern char **environ; + /**************************************************************************** main thread of the background lpq updater ****************************************************************************/ @@ -366,75 +352,69 @@ pid_t start_background_queue(struct tevent_context *ev, char *logfile) { pid_t pid; - struct bq_state *state; int ret; - NTSTATUS status; + ssize_t nread; + char **argv = NULL; + int ready_fds[2]; DEBUG(3,("start_background_queue: Starting background LPQ thread\n")); - /* - * Block signals before forking child as it will have to - * set its own handlers. Child will re-enable SIGHUP as - * soon as the handlers are set up. - */ - BlockSignals(true, SIGTERM); - BlockSignals(true, SIGHUP); - - pid = fork(); - - /* parent or error */ - if (pid != 0) { - /* Re-enable SIGHUP before returnig */ - BlockSignals(false, SIGTERM); - BlockSignals(false, SIGHUP); - return pid; + ret = pipe(ready_fds); + if (ret == -1) { + return -1; } - /* Child. */ - DEBUG(5,("start_background_queue: background LPQ thread started\n")); - - status = smbd_reinit_after_fork(msg_ctx, ev, true, "lpqd"); - - if (!NT_STATUS_IS_OK(status)) { - DEBUG(0,("reinit_after_fork() failed\n")); - smb_panic("reinit_after_fork() failed"); + argv = str_list_make_empty(talloc_tos()); + str_list_add_printf( + &argv, "%s/samba-bgqd", get_dyn_SAMBA_LIBEXECDIR()); + str_list_add_printf( + &argv, "--ready-signal-fd=%d", ready_fds[1]); + str_list_add_printf( + &argv, "--parent-watch-fd=%d", parent_watch_fd()); + str_list_add_printf( + &argv, "--debuglevel=%d", debuglevel_get_class(DBGC_RPC_SRV)); + if (!is_default_dyn_CONFIGFILE()) { + str_list_add_printf( + &argv, "--configfile=%s", get_dyn_CONFIGFILE()); } - - /* Remove previous forwarder message set in parent. */ - messaging_deregister(msg_ctx, MSG_PRINTER_DRVUPGRADE, NULL); - - state = register_printing_bq_handlers(NULL, msg_ctx); - if (state == NULL) { - DBG_ERR("Could not register bq handlers\n"); - exit(1); + if (!is_default_dyn_LOGFILEBASE()) { + str_list_add_printf( + &argv, "--log-basename=%s", get_dyn_LOGFILEBASE()); } - - bq_reopen_logs(logfile); - bq_setup_sig_term_handler(); - - BlockSignals(false, SIGTERM); - BlockSignals(false, SIGHUP); - - if (!printing_subsystem_queue_tasks(state)) { - exit(1); + str_list_add_printf(&argv, "-F"); + if (argv == NULL) { + goto nomem; } - if (!locking_init()) { - exit(1); + ret = posix_spawn(&pid, argv[0], NULL, NULL, argv, environ); + if (ret == -1) { + goto fail; } + TALLOC_FREE(argv); - pcap_cache_reload(ev, msg_ctx, reload_pcap_change_notify); + close(ready_fds[1]); - DEBUG(5,("start_background_queue: background LPQ thread waiting for messages\n")); - ret = tevent_loop_wait(ev); - /* should not be reached */ - DEBUG(0,("background_queue: tevent_loop_wait() exited with %d - %s\n", - ret, (ret == 0) ? "out of events" : strerror(errno))); - exit(1); + nread = sys_read(ready_fds[0], &pid, sizeof(pid)); + close(ready_fds[0]); + if (nread != sizeof(pid)) { + goto fail; + } return pid; + +nomem: + errno = ENOMEM; +fail: + { + int err = errno; + TALLOC_FREE(argv); + errno = err; + } + + return -1; } + /* Run before the parent forks */ bool printing_subsystem_init(struct tevent_context *ev_ctx, struct messaging_context *msg_ctx, diff --git a/source3/printing/samba-bgqd.c b/source3/printing/samba-bgqd.c new file mode 100644 index 00000000000..ced1de876c7 --- /dev/null +++ b/source3/printing/samba-bgqd.c @@ -0,0 +1,406 @@ +/* + * Printing background queue helper + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, see <http://www.gnu.org/licenses/>. + */ + +#include "replace.h" +#include "system/filesys.h" +#include "lib/util/server_id.h" +#include "source3/locking/share_mode_lock.h" +#include "source3/param/loadparm.h" +#include "source3/param/param_proto.h" +#include "source3/include/popt_common.h" +#include "lib/util/talloc_stack.h" +#include "lib/util/debug.h" +#include "lib/util/signal.h" +#include "lib/util/fault.h" +#include "lib/util/become_daemon.h" +#include "lib/util/charset/charset.h" +#include "lib/util/samba_util.h" +#include "lib/util/sys_rw.h" +#include "lib/util/pidfile.h" +#include "lib/async_req/async_sock.h" +#include "dynconfig/dynconfig.h" +#include "source3/lib/global_contexts.h" +#include "messages.h" +#include "nsswitch/winbind_client.h" +#include "source3/include/auth.h" +#include "source3/lib/util_procid.h" +#include "source3/auth/proto.h" +#include "source3/printing/queue_process.h" + +static void watch_handler(struct tevent_req *req) +{ + bool *pdone = tevent_req_callback_data_void(req); + *pdone = true; +} + +static void bgqd_sig_term_handler( + struct tevent_context *ev, + struct tevent_signal *se, + int signum, + int count, + void *siginfo, + void *private_data) +{ + bool *pdone = private_data; + *pdone = true; +} + +static bool ready_signal_filter( + struct messaging_rec *rec, void *private_data) +{ + pid_t pid = getpid(); + ssize_t written; + + if (rec->msg_type != MSG_DAEMON_READY_FD) { + return false; + } + if (rec->num_fds != 1) { + return false; + } + + written = sys_write(rec->fds[0], &pid, sizeof(pid)); + if (written != sizeof(pid)) { + DBG_ERR("Could not write pid: %s\n", strerror(errno)); + } + + return false; +} + +static int samba_bgqd_pidfile_create( + struct messaging_context *msg_ctx, + const char *progname, + int ready_signal_fd) +{ + const char *piddir = lp_pid_directory(); + size_t len = strlen(piddir) + strlen(progname) + 6; + char pidFile[len]; + pid_t existing_pid; + int fd, ret; + + snprintf(pidFile, + sizeof(pidFile), + "%s/%s.pid", + piddir, progname); + + ret = pidfile_path_create(pidFile, &fd, &existing_pid); + if (ret == 0) { + struct tevent_req *ready_signal_req = NULL; + + /* + * Listen for fd's sent via MSG_DAEMON_READY_FD: + * Multiple instances of this process might have raced + * for creating the pidfile. Make sure the parent does + * not suffer from this race, reply on behalf of the + * loser of this race. + */ + + ready_signal_req = messaging_filtered_read_send( + msg_ctx, + messaging_tevent_context(msg_ctx), + msg_ctx, + ready_signal_filter, + NULL); + if (ready_signal_req == NULL) { + DBG_DEBUG("messaging_filtered_read_send failed\n"); + pidfile_unlink(piddir, progname); + pidfile_fd_close(fd); + return ENOMEM; + } + + /* leak fd */ + return 0; + } + + if (ret != EAGAIN) { + DBG_DEBUG("pidfile_path_create() failed: %s\n", + strerror(ret)); + return ret; + } + + DBG_DEBUG("%s pid %d exists\n", progname, (int)existing_pid); + + if (ready_signal_fd != -1) { + /* + * We lost the race for the pidfile, but someone else + * can report readiness on our behalf. + */ + NTSTATUS status = messaging_send_iov( + msg_ctx, + pid_to_procid(existing_pid), + MSG_DAEMON_READY_FD, + NULL, + 0, + &ready_signal_fd, + 1); + if (!NT_STATUS_IS_OK(status)) { + DBG_DEBUG("Could not send ready_signal_fd: %s\n", + nt_errstr(status)); + } + } + + return EAGAIN; +} + +static int closeall_except(int *fds, size_t num_fds) +{ + size_t i; + int max_keep = -1; + int fd, ret; + + for (i=0; i<num_fds; i++) { + max_keep = MAX(max_keep, fds[i]); + } + if (max_keep == -1) { + return 0; + } + + for (fd = 0; fd < max_keep; fd++) { + bool keep = false; + + /* + * O(num_fds*max_keep), but we expect the number of + * fds to keep to be very small, typically 0,1,2 and + * very few more. + */ + for (i=0; i<num_fds; i++) { + if (fd == fds[i]) { + keep = true; + break; + } + } + if (keep) { + continue; + } + ret = close(fd); + if ((ret == -1) && (errno != EBADF)) { + return errno; + } + } + + closefrom(max_keep+1); + return 0; +} + +int main(int argc, const char *argv[]) +{ + const struct loadparm_substitution *lp_sub = + loadparm_s3_global_substitution(); + const char *progname = getprogname(); + TALLOC_CTX *frame = NULL; + poptContext pc; + struct messaging_context *msg_ctx = NULL; + struct tevent_context *ev = NULL; + struct tevent_req *watch_req = NULL; + struct tevent_signal *sigterm_handler = NULL; + struct bq_state *bq = NULL; + int foreground = 0; + int no_process_group = 0; + int log_stdout = 0; + int ready_signal_fd = -1; + int watch_fd = -1; + NTSTATUS status; + int ret; + bool ok; + bool done = false; + int exitcode = 1; + + struct poptOption long_options[] = { + POPT_AUTOHELP + POPT_COMMON_SAMBA + { + .longName = "foreground", + .shortName = 'F', + .argInfo = POPT_ARG_NONE, + .arg = &foreground, + .descrip = "Run daemon in foreground " + "(for daemontools, etc.)", + }, + { + .longName = "no-process-group", + .shortName = '\0', + .argInfo = POPT_ARG_NONE, + .arg = &no_process_group, + .descrip = "Don't create a new process group" , + }, + { + .longName = "log-stdout", + .shortName = 'S', + .argInfo = POPT_ARG_NONE, + .arg = &log_stdout, + .descrip = "Log to stdout" , + }, + + /* + * File descriptor to write the PID of the helper + * process to + */ + { + .longName = "ready-signal-fd", + .argInfo = POPT_ARG_INT, + .arg = &ready_signal_fd, + .descrip = "Fd to signal readiness to" , + }, + + /* + * Read end of a pipe held open by the parent + * smbd. Exit this process when it becomes readable. + */ + { + .longName = "parent-watch-fd", + .argInfo = POPT_ARG_INT, + .arg = &watch_fd, + .descrip = "Fd to watch for exiting", + }, + POPT_TABLEEND + }; + + talloc_enable_null_tracking(); + frame = talloc_stackframe(); + + umask(0); + setup_logging(progname, DEBUG_DEFAULT_STDERR); + + pc = poptGetContext(progname, argc, argv, long_options, 0); + + ret = poptGetNextOpt(pc); + if (ret < -1) { + fprintf(stderr, "invalid options: %s\n", poptStrerror(ret)); + goto done; + } + + poptFreeContext(pc); + + { + int keep[] = { 0, 1, 2, ready_signal_fd, watch_fd }; + ret = closeall_except(keep, ARRAY_SIZE(keep)); + if (ret != 0) { + fprintf(stderr, + "Could not close fds: %s\n", + strerror(ret)); + goto done; + } + } + + if (foreground) { + daemon_status(progname, "Starting process ... "); + } else { + become_daemon(true, no_process_group, log_stdout); + } + + if (log_stdout) { + setup_logging(progname, DEBUG_STDOUT); + } else { + setup_logging(progname, DEBUG_FILE); + } + + lp_load_initial_only(get_dyn_CONFIGFILE()); + + BlockSignals(true, SIGPIPE); + + smb_init_locale(); + fault_setup(); + dump_core_setup(progname, lp_logfile(frame, lp_sub)); + + msg_ctx = global_messaging_context(); + if (msg_ctx == NULL) { + DBG_ERR("messaging_init() failed\n"); + goto done; + } + ev = messaging_tevent_context(msg_ctx); + + ret = samba_bgqd_pidfile_create(msg_ctx, progname, ready_signal_fd); + if (ret != 0) { + goto done; + } + + if (watch_fd != -1) { + watch_req = wait_for_read_send(ev, ev, watch_fd, true); + if (watch_req == NULL) { + fprintf(stderr, "tevent_add_fd failed\n"); + goto done; + } + tevent_req_set_callback(watch_req, watch_handler, &done); + } + + (void)winbind_off(); + ok = init_guest_session_info(frame); + (void)winbind_on(); + if (!ok) { + DBG_ERR("init_guest_session_info failed\n"); + goto done; + } + + (void)winbind_off(); + status = init_system_session_info(frame); + (void)winbind_on(); + if (!NT_STATUS_IS_OK(status)) { + DBG_ERR("init_system_session_info failed: %s\n", + nt_errstr(status)); + goto done; + } + + sigterm_handler = tevent_add_signal( + ev, frame, SIGTERM, 0, bgqd_sig_term_handler, &done); + if (sigterm_handler == NULL) { + DBG_ERR("Could not install SIGTERM handler\n"); + goto done; + } + + bq = register_printing_bq_handlers(frame, msg_ctx); + if (bq == NULL) { + DBG_ERR("Could not register bq handlers\n"); + goto done; + } + + ok = locking_init(); + if (!ok) { + DBG_ERR("locking_init failed\n"); + goto done; + } + + if (ready_signal_fd != -1) { + pid_t pid = getpid(); + ssize_t written; + + written = sys_write(ready_signal_fd, &pid, sizeof(pid)); + if (written != sizeof(pid)) { + DBG_ERR("Reporting readiness failed\n"); + goto done; + } + close(ready_signal_fd); + ready_signal_fd = -1; + } + + while (!done) { + TALLOC_CTX *tmp = talloc_stackframe(); + ret = tevent_loop_once(ev); + TALLOC_FREE(tmp); + if (ret != 0) { + DBG_ERR("tevent_loop_once failed\n"); + break; + } + } + + exitcode = 0; +done: + TALLOC_FREE(watch_req); + TALLOC_FREE(bq); + TALLOC_FREE(sigterm_handler); + global_messaging_context_free(); + TALLOC_FREE(frame); + return exitcode; +} |