1
0
mirror of https://github.com/samba-team/samba.git synced 2025-01-08 21:18:16 +03:00

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>
This commit is contained in:
Volker Lendecke 2021-04-22 13:39:31 +02:00 committed by Jeremy Allison
parent ecf9ba381e
commit bad19e208c
6 changed files with 524 additions and 77 deletions

View File

@ -0,0 +1,48 @@
<?xml version="1.0" encoding="iso-8859-1"?>
<!DOCTYPE refentry PUBLIC "-//Samba-Team//DTD DocBook V4.2-Based Variant V1.0//EN" "http://www.samba.org/samba/DTD/samba-doc">
<refentry id="samba-bgqd.8">
<refmeta>
<refentrytitle>samba-bgqd</refentrytitle>
<manvolnum>8</manvolnum>
<refmiscinfo class="source">Samba</refmiscinfo>
<refmiscinfo class="manual">System Administration tools</refmiscinfo>
<refmiscinfo class="version">&doc.version;</refmiscinfo>
</refmeta>
<refnamediv>
<refname>samba-bgqd</refname>
<refpurpose>This is an internal helper program performing
asynchronous printing-related jobs.</refpurpose>
</refnamediv>
<refsynopsisdiv>
<cmdsynopsis>
<command>samba-bgqd</command>
</cmdsynopsis>
</refsynopsisdiv>
<refsect1>
<title>DESCRIPTION</title>
<para>This tool is part of the
<citerefentry><refentrytitle>samba</refentrytitle>
<manvolnum>7</manvolnum></citerefentry> suite.</para>
<para>samba-bgqd is an helper program to be spawned by smbd or
spoolssd to perform jobs like updating the printer list or
other management tasks asynchronously on demand. It is not
intended to be called by users or administrators.</para>
</refsect1>
<refsect1>
<title>AUTHOR</title>
<para>The original Samba software and related utilities
were created by Andrew Tridgell. Samba is now developed
by the Samba Team as an Open Source project similar
to the way the Linux kernel is developed.</para>
</refsect1>
</refentry>

View File

@ -44,6 +44,7 @@ manpages='''
manpages/smbpasswd.8
manpages/smbspool_krb5_wrapper.8
manpages/smbspool.8
manpages/samba-bgqd.8
manpages/smbstatus.1
manpages/smbtar.1
manpages/smbtree.1

View File

@ -48,6 +48,8 @@ interface messaging
MSG_REQ_RINGBUF_LOG = 0x0033,
MSG_RINGBUF_LOG = 0x0034,
MSG_DAEMON_READY_FD = 0x0035,
/* nmbd messages */
MSG_FORCE_ELECTION = 0x0101,
MSG_WINS_NEW_ENTRY = 0x0102,

View File

@ -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());
}
if (!is_default_dyn_LOGFILEBASE()) {
str_list_add_printf(
&argv, "--log-basename=%s", get_dyn_LOGFILEBASE());
}
str_list_add_printf(&argv, "-F");
if (argv == NULL) {
goto nomem;
}
/* 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);
ret = posix_spawn(&pid, argv[0], NULL, NULL, argv, environ);
if (ret == -1) {
goto fail;
}
TALLOC_FREE(argv);
bq_reopen_logs(logfile);
bq_setup_sig_term_handler();
close(ready_fds[1]);
BlockSignals(false, SIGTERM);
BlockSignals(false, SIGHUP);
if (!printing_subsystem_queue_tasks(state)) {
exit(1);
nread = sys_read(ready_fds[0], &pid, sizeof(pid));
close(ready_fds[0]);
if (nread != sizeof(pid)) {
goto fail;
}
if (!locking_init()) {
exit(1);
}
pcap_cache_reload(ev, msg_ctx, reload_pcap_change_notify);
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);
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,

View File

@ -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;
}

View File

@ -840,6 +840,16 @@ bld.SAMBA3_SUBSYSTEM('PRINTING',
cups
''')
bld.SAMBA_BINARY('samba-bgqd',
source='printing/samba-bgqd.c',
deps='''
samba3core
popt_samba3
AUTH_COMMON
smbd_base
''',
install_path='${LIBEXECDIR}/samba')
bld.SAMBA3_SUBSYSTEM('FNAME_UTIL',
source='lib/filename_util.c',
deps='samba-util')