2011-08-02 00:50:51 +04:00
/*
Unix SMB / Netbios implementation .
Version 3.0
printing backend routines
Copyright ( C ) Andrew Tridgell 1992 - 2000
Copyright ( C ) Jeremy Allison 2002
Copyright ( C ) Simo Sorce 2011
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 "includes.h"
# include "smbd/globals.h"
# include "include/messages.h"
2013-02-25 13:41:18 +04:00
# include "lib/util/util_process.h"
2011-08-02 00:50:51 +04:00
# include "printing.h"
# include "printing/pcap.h"
2011-08-03 14:59:17 +04:00
# include "printing/queue_process.h"
2011-08-02 00:50:51 +04:00
# include "serverid.h"
# include "locking/proto.h"
2012-06-05 16:53:01 +04:00
# include "smbd/smbd.h"
2011-08-10 23:23:20 +04:00
# include "rpc_server/rpc_config.h"
2011-08-10 17:27:24 +04:00
# include "printing/load.h"
2014-02-12 22:13:19 +04:00
# include "rpc_server/spoolss/srv_spoolss_nt.h"
2014-08-01 18:25:59 +04:00
# include "auth.h"
# include "nt_printing.h"
2011-05-18 19:24:30 +04:00
2013-02-18 12:58:10 +04:00
extern pid_t start_spoolssd ( struct tevent_context * ev_ctx ,
2011-05-18 19:24:30 +04:00
struct messaging_context * msg_ctx ) ;
2011-08-02 00:50:51 +04:00
2014-08-01 18:25:59 +04:00
/**
* @ brief Purge stale printers and reload from pre - populated pcap cache .
*
* This function should normally only be called as a callback on a successful
* pcap_cache_reload ( ) .
*
* This function can cause DELETION of printers and drivers from our registry ,
* so calling it on a failed pcap reload may REMOVE permanently all printers
* and drivers .
*
* @ param [ in ] ev The event context .
*
* @ param [ in ] msg_ctx The messaging context .
*/
static void delete_and_reload_printers_full ( struct tevent_context * ev ,
struct messaging_context * msg_ctx )
{
struct auth_session_info * session_info = NULL ;
struct spoolss_PrinterInfo2 * pinfo2 = NULL ;
int n_services ;
int pnum ;
int snum ;
const char * pname ;
const char * sname ;
NTSTATUS status ;
n_services = lp_numservices ( ) ;
pnum = lp_servicenumber ( PRINTERS_NAME ) ;
status = make_session_info_system ( talloc_tos ( ) , & session_info ) ;
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 3 , ( " reload_printers: "
" Could not create system session_info \n " ) ) ;
/* can't remove stale printers before we
* are fully initilized */
return ;
}
/*
* Add default config for printers added to smb . conf file and remove
* stale printers
*/
for ( snum = 0 ; snum < n_services ; snum + + ) {
/* avoid removing PRINTERS_NAME */
if ( snum = = pnum ) {
continue ;
}
/* skip no-printer services */
if ( ! snum_is_shared_printer ( snum ) ) {
continue ;
}
sname = lp_const_servicename ( snum ) ;
pname = lp_printername ( session_info , snum ) ;
/* check printer, but avoid removing non-autoloaded printers */
if ( lp_autoloaded ( snum ) & & ! pcap_printername_ok ( pname ) ) {
DEBUG ( 3 , ( " removing stale printer %s \n " , pname ) ) ;
if ( is_printer_published ( session_info , session_info ,
msg_ctx ,
NULL ,
lp_servicename ( session_info ,
snum ) ,
& pinfo2 ) ) {
nt_printer_publish ( session_info ,
session_info ,
msg_ctx ,
pinfo2 ,
DSPRINT_UNPUBLISH ) ;
TALLOC_FREE ( pinfo2 ) ;
}
nt_printer_remove ( session_info , session_info , msg_ctx ,
pname ) ;
} else {
DEBUG ( 8 , ( " Adding default registry entry for printer "
" [%s], if it doesn't exist. \n " , sname ) ) ;
nt_printer_add ( session_info , session_info , msg_ctx ,
sname ) ;
}
}
/* finally, purge old snums */
delete_and_reload_printers ( ev , msg_ctx ) ;
TALLOC_FREE ( session_info ) ;
}
2011-08-02 00:50:51 +04:00
/****************************************************************************
Notify smbds of new printcap data
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
static void reload_pcap_change_notify ( struct tevent_context * ev ,
struct messaging_context * msg_ctx )
{
2012-09-27 21:01:15 +04:00
/*
* Reload the printers first in the background process so that
* newly added printers get default values created in the registry .
*
* This will block the process for some time ( ~ 1 sec per printer ) , but
* it doesn ' t block smbd ' s servering clients .
*/
2014-08-01 18:25:59 +04:00
delete_and_reload_printers_full ( ev , msg_ctx ) ;
2012-09-27 21:01:15 +04:00
2011-08-02 00:50:51 +04:00
message_send_all ( msg_ctx , MSG_PRINTER_PCAP , NULL , 0 , NULL ) ;
}
2012-01-16 18:54:41 +04:00
struct printing_queue_housekeeping_state {
struct tevent_context * ev ;
struct messaging_context * msg ;
} ;
2011-08-02 00:50:51 +04:00
static bool print_queue_housekeeping ( const struct timeval * now , void * pvt )
{
2012-01-16 18:54:41 +04:00
struct printing_queue_housekeeping_state * state =
talloc_get_type_abort ( pvt ,
struct printing_queue_housekeeping_state ) ;
2011-08-02 00:50:51 +04:00
time_t printcap_cache_time = ( time_t ) lp_printcap_cache_time ( ) ;
time_t t = time_mono ( NULL ) ;
DEBUG ( 5 , ( " print queue housekeeping \n " ) ) ;
/* if periodic printcap rescan is enabled,
* see if it ' s time to reload */
if ( ( printcap_cache_time ! = 0 ) & &
( t > = ( last_printer_reload_time + printcap_cache_time ) ) ) {
DEBUG ( 3 , ( " Printcap cache time expired. \n " ) ) ;
2012-01-16 18:54:41 +04:00
pcap_cache_reload ( state - > ev , state - > msg ,
2011-08-02 00:50:51 +04:00
& reload_pcap_change_notify ) ;
last_printer_reload_time = t ;
}
return true ;
}
static bool printing_subsystem_queue_tasks ( struct tevent_context * ev_ctx ,
struct messaging_context * msg_ctx )
{
2012-01-16 18:54:41 +04:00
struct printing_queue_housekeeping_state * state ;
state = talloc_zero ( ev_ctx , struct printing_queue_housekeeping_state ) ;
if ( state = = NULL ) {
DEBUG ( 0 , ( " Could not talloc printing_queue_housekeeping_state \n " ) ) ;
return false ;
}
state - > ev = ev_ctx ;
state - > msg = msg_ctx ;
2011-08-02 00:50:51 +04:00
if ( ! ( event_add_idle ( ev_ctx , NULL ,
timeval_set ( SMBD_HOUSEKEEPING_INTERVAL , 0 ) ,
" print_queue_housekeeping " ,
print_queue_housekeeping ,
2012-01-16 18:54:41 +04:00
state ) ) ) {
DEBUG ( 0 , ( " Could not add print_queue_housekeeping event \n " ) ) ;
2011-08-02 00:50:51 +04:00
return false ;
}
return true ;
}
2011-08-05 23:14:26 +04:00
static void bq_reopen_logs ( char * logfile )
{
if ( logfile ) {
lp_set_logfile ( logfile ) ;
}
reopen_logs ( ) ;
}
2011-08-02 00:50:51 +04:00
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 " ) ;
}
2011-08-03 14:59:17 +04:00
static void bq_setup_sig_term_handler ( void )
2011-08-02 00:50:51 +04:00
{
struct tevent_signal * se ;
se = tevent_add_signal ( server_event_context ( ) ,
server_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 ,
int count ,
void * siginfo ,
void * pvt )
{
struct messaging_context * msg_ctx ;
msg_ctx = talloc_get_type_abort ( pvt , struct messaging_context ) ;
change_to_root_user ( ) ;
DEBUG ( 1 , ( " Reloading pcap cache after SIGHUP \n " ) ) ;
pcap_cache_reload ( ev , msg_ctx , & reload_pcap_change_notify ) ;
2011-08-05 23:14:26 +04:00
bq_reopen_logs ( NULL ) ;
2011-08-02 00:50:51 +04:00
}
static void bq_setup_sig_hup_handler ( struct tevent_context * ev ,
struct messaging_context * msg_ctx )
{
struct tevent_signal * se ;
se = tevent_add_signal ( ev , ev , SIGHUP , 0 , bq_sig_hup_handler ,
msg_ctx ) ;
if ( ! se ) {
exit_server ( " failed to setup SIGHUP handler " ) ;
}
}
2011-08-10 16:59:44 +04:00
static void bq_sig_chld_handler ( struct tevent_context * ev_ctx ,
struct tevent_signal * se ,
int signum , int count ,
void * siginfo , void * pvt )
{
int status ;
pid_t pid ;
pid = sys_waitpid ( - 1 , & status , WNOHANG ) ;
if ( WIFEXITED ( status ) ) {
DEBUG ( 6 , ( " Bq child process %d terminated with %d \n " ,
( int ) pid , WEXITSTATUS ( status ) ) ) ;
} else {
DEBUG ( 3 , ( " Bq child process %d terminated abnormally \n " ,
( int ) pid ) ) ;
}
}
static void bq_setup_sig_chld_handler ( struct tevent_context * ev_ctx )
{
struct tevent_signal * se ;
se = tevent_add_signal ( ev_ctx , ev_ctx , SIGCHLD , 0 ,
bq_sig_chld_handler , NULL ) ;
if ( ! se ) {
exit_server ( " failed to setup SIGCHLD handler " ) ;
}
}
2011-08-02 00:50:51 +04:00
static void bq_smb_conf_updated ( struct messaging_context * msg_ctx ,
void * private_data ,
uint32_t msg_type ,
struct server_id server_id ,
DATA_BLOB * data )
{
struct tevent_context * ev_ctx =
talloc_get_type_abort ( private_data , struct tevent_context ) ;
DEBUG ( 10 , ( " smb_conf_updated: Got message saying smb.conf was "
" updated. Reloading. \n " ) ) ;
change_to_root_user ( ) ;
pcap_cache_reload ( ev_ctx , msg_ctx , & reload_pcap_change_notify ) ;
}
static void printing_pause_fd_handler ( struct tevent_context * ev ,
struct tevent_fd * fde ,
uint16_t flags ,
void * private_data )
{
/*
* If pause_pipe [ 1 ] is closed it means the parent smbd
* and children exited or aborted .
*/
exit_server_cleanly ( NULL ) ;
}
/****************************************************************************
main thread of the background lpq updater
* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * */
2011-08-04 01:04:50 +04:00
pid_t start_background_queue ( struct tevent_context * ev ,
2011-08-05 23:14:26 +04:00
struct messaging_context * msg_ctx ,
char * logfile )
2011-08-02 00:50:51 +04:00
{
2011-08-04 01:04:50 +04:00
pid_t pid ;
2011-08-02 00:50:51 +04:00
/* Use local variables for this as we don't
* need to save the parent side of this , just
* ensure it closes when the process exits .
*/
int pause_pipe [ 2 ] ;
DEBUG ( 3 , ( " start_background_queue: Starting background LPQ thread \n " ) ) ;
if ( pipe ( pause_pipe ) = = - 1 ) {
DEBUG ( 5 , ( " start_background_queue: cannot create pipe. %s \n " , strerror ( errno ) ) ) ;
exit ( 1 ) ;
}
2011-08-08 18:08:46 +04:00
/*
* 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 ) ;
2012-03-24 23:17:08 +04:00
pid = fork ( ) ;
2011-08-02 00:50:51 +04:00
2011-08-08 18:08:46 +04:00
/* parent or error */
if ( pid ! = 0 ) {
/* Re-enable SIGHUP before returnig */
BlockSignals ( false , SIGTERM ) ;
BlockSignals ( false , SIGHUP ) ;
return pid ;
}
2011-08-04 01:04:50 +04:00
if ( pid = = - 1 ) {
2011-08-02 00:50:51 +04:00
DEBUG ( 5 , ( " start_background_queue: background LPQ thread failed to start. %s \n " , strerror ( errno ) ) ) ;
exit ( 1 ) ;
}
2011-08-04 01:04:50 +04:00
if ( pid = = 0 ) {
2011-08-02 00:50:51 +04:00
struct tevent_fd * fde ;
int ret ;
NTSTATUS status ;
/* Child. */
DEBUG ( 5 , ( " start_background_queue: background LPQ thread started \n " ) ) ;
close ( pause_pipe [ 0 ] ) ;
pause_pipe [ 0 ] = - 1 ;
2015-04-22 13:00:10 +03:00
status = smbd_reinit_after_fork ( msg_ctx , ev , true ) ;
2011-08-02 00:50:51 +04:00
if ( ! NT_STATUS_IS_OK ( status ) ) {
DEBUG ( 0 , ( " reinit_after_fork() failed \n " ) ) ;
smb_panic ( " reinit_after_fork() failed " ) ;
}
2013-02-25 13:41:18 +04:00
prctl_set_comment ( " lpqd " ) ;
2011-08-05 23:14:26 +04:00
bq_reopen_logs ( logfile ) ;
2011-08-02 00:50:51 +04:00
bq_setup_sig_term_handler ( ) ;
bq_setup_sig_hup_handler ( ev , msg_ctx ) ;
2011-08-10 16:59:44 +04:00
bq_setup_sig_chld_handler ( ev ) ;
2011-08-02 00:50:51 +04:00
2011-08-08 18:08:46 +04:00
BlockSignals ( false , SIGTERM ) ;
BlockSignals ( false , SIGHUP ) ;
2011-08-02 00:50:51 +04:00
if ( ! printing_subsystem_queue_tasks ( ev , msg_ctx ) ) {
exit ( 1 ) ;
}
2011-12-13 13:56:27 +04:00
if ( ! serverid_register ( messaging_server_id ( msg_ctx ) ,
2011-08-11 07:28:09 +04:00
FLAG_MSG_GENERAL |
FLAG_MSG_PRINT_GENERAL ) ) {
2011-08-02 00:50:51 +04:00
exit ( 1 ) ;
}
if ( ! locking_init ( ) ) {
exit ( 1 ) ;
}
messaging_register ( msg_ctx , ev , MSG_SMB_CONF_UPDATED ,
bq_smb_conf_updated ) ;
messaging_register ( msg_ctx , NULL , MSG_PRINTER_UPDATE ,
print_queue_receive ) ;
2014-02-12 22:13:19 +04:00
/* Remove previous forwarder message set in parent. */
messaging_deregister ( msg_ctx , MSG_PRINTER_DRVUPGRADE , NULL ) ;
messaging_register ( msg_ctx , NULL , MSG_PRINTER_DRVUPGRADE ,
do_drv_upgrade_printer ) ;
2011-08-02 00:50:51 +04:00
fde = tevent_add_fd ( ev , ev , pause_pipe [ 1 ] , TEVENT_FD_READ ,
printing_pause_fd_handler ,
NULL ) ;
if ( ! fde ) {
DEBUG ( 0 , ( " tevent_add_fd() failed for pause_pipe \n " ) ) ;
smb_panic ( " tevent_add_fd() failed for pause_pipe " ) ;
}
2011-08-10 16:59:44 +04:00
pcap_cache_reload ( ev , msg_ctx , & reload_pcap_change_notify ) ;
2011-08-02 00:50:51 +04:00
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 ) ;
}
close ( pause_pipe [ 1 ] ) ;
2011-08-04 01:04:50 +04:00
return pid ;
}
2011-08-02 00:50:51 +04:00
/* Run before the parent forks */
bool printing_subsystem_init ( struct tevent_context * ev_ctx ,
struct messaging_context * msg_ctx ,
2011-05-18 19:24:30 +04:00
bool start_daemons ,
2011-08-02 00:50:51 +04:00
bool background_queue )
{
2011-08-04 01:04:50 +04:00
pid_t pid = - 1 ;
2011-08-02 00:50:51 +04:00
2011-08-02 18:06:31 +04:00
if ( ! print_backend_init ( msg_ctx ) ) {
return false ;
}
2011-05-18 19:24:30 +04:00
/* start spoolss daemon */
/* start as a separate daemon only if enabled */
2011-08-10 23:23:20 +04:00
if ( start_daemons & & rpc_spoolss_daemon ( ) = = RPC_DAEMON_FORK ) {
2011-05-18 19:24:30 +04:00
pid = start_spoolssd ( ev_ctx , msg_ctx ) ;
} else if ( start_daemons & & background_queue ) {
2011-08-04 01:04:50 +04:00
2011-08-05 23:14:26 +04:00
pid = start_background_queue ( ev_ctx , msg_ctx , NULL ) ;
2011-08-04 01:04:50 +04:00
2011-08-02 00:50:51 +04:00
} else {
2011-08-04 01:04:50 +04:00
bool ret ;
2011-08-02 00:50:51 +04:00
ret = printing_subsystem_queue_tasks ( ev_ctx , msg_ctx ) ;
2011-08-04 01:04:50 +04:00
/* Publish nt printers, this requires a working winreg pipe */
2014-08-01 18:25:59 +04:00
pcap_cache_reload ( ev_ctx , msg_ctx ,
& delete_and_reload_printers_full ) ;
2011-08-04 01:04:50 +04:00
return ret ;
2011-08-02 00:50:51 +04:00
}
2011-08-04 01:04:50 +04:00
if ( pid = = - 1 ) {
return false ;
}
background_lpq_updater_pid = pid ;
return true ;
2011-08-02 00:50:51 +04:00
}
void printing_subsystem_update ( struct tevent_context * ev_ctx ,
2011-08-04 16:56:01 +04:00
struct messaging_context * msg_ctx ,
bool force )
2011-08-02 00:50:51 +04:00
{
2011-08-04 16:56:01 +04:00
if ( background_lpq_updater_pid ! = - 1 ) {
2014-07-23 14:12:34 +04:00
if ( pcap_cache_loaded ( NULL ) ) {
2011-08-10 17:27:24 +04:00
load_printers ( ev_ctx , msg_ctx ) ;
}
2011-08-04 16:56:01 +04:00
if ( force ) {
/* Send a sighup to the background process.
* this will force it to reload printers */
kill ( background_lpq_updater_pid , SIGHUP ) ;
}
return ;
}
2011-08-02 00:50:51 +04:00
2014-08-01 18:25:59 +04:00
pcap_cache_reload ( ev_ctx , msg_ctx ,
& delete_and_reload_printers_full ) ;
2011-08-02 00:50:51 +04:00
}