diff options
author | joyce mcintosh <Joyce.McIntosh@Sun.COM> | 2010-08-11 16:48:54 -0700 |
---|---|---|
committer | joyce mcintosh <Joyce.McIntosh@Sun.COM> | 2010-08-11 16:48:54 -0700 |
commit | fd9ee8b58485b20072eeef1310a88ff348d5e7fa (patch) | |
tree | 74e8f3fc5f5409cdc6be3dae5631f8d0c672260a /usr/src/cmd/smbsrv | |
parent | e2c5185af3c50d9510e5df68aa37abdc6c0d3aac (diff) | |
download | illumos-gate-fd9ee8b58485b20072eeef1310a88ff348d5e7fa.tar.gz |
6972305 Preferred DC not selected after setting pdc via sharectl
6971047 smbd hang during FVT regression test
6711195 Sparc:Get error "Windows Explorer has stopped working" once click on Details fr Properties on Vista
PSARC/2009/464 Offline attribute
6972515 Offline attribute - PSARC 2009/464
PSARC/2010/037 Windows Sparse Attribute
6972519 Windows Sparse Attribute - PSARC 2010/037
6719444 [CLI] - idmap help's output is not consistent with man pages's
6975449 idmap test suite needs idmap_cache_get_data() from libidmap
6974351 OpenSSL still taking smbd down
--HG--
rename : usr/src/lib/libidmap/common/idmap_priv.h => usr/src/cmd/idmap/idmap/namemaps.h
Diffstat (limited to 'usr/src/cmd/smbsrv')
-rw-r--r-- | usr/src/cmd/smbsrv/dtrace/msrpc.d | 8 | ||||
-rw-r--r-- | usr/src/cmd/smbsrv/smbd/Makefile | 1 | ||||
-rw-r--r-- | usr/src/cmd/smbsrv/smbd/smbd.h | 10 | ||||
-rw-r--r-- | usr/src/cmd/smbsrv/smbd/smbd_join.c | 49 | ||||
-rw-r--r-- | usr/src/cmd/smbsrv/smbd/smbd_main.c | 226 | ||||
-rw-r--r-- | usr/src/cmd/smbsrv/smbd/smbd_spool.c | 510 |
6 files changed, 671 insertions, 133 deletions
diff --git a/usr/src/cmd/smbsrv/dtrace/msrpc.d b/usr/src/cmd/smbsrv/dtrace/msrpc.d index 13f80bac20..bf744df7cc 100644 --- a/usr/src/cmd/smbsrv/dtrace/msrpc.d +++ b/usr/src/cmd/smbsrv/dtrace/msrpc.d @@ -327,6 +327,14 @@ pid$target::samr_s_EnumDomainGroups:return } /* + * SPOOLSS + */ +pid$target::spoolss_*:entry, +pid$target::spoolss_*:return +{ +} + +/* * SVCCTL */ pid$target::svcctl_s_*:entry, diff --git a/usr/src/cmd/smbsrv/smbd/Makefile b/usr/src/cmd/smbsrv/smbd/Makefile index dca4858474..fd3e41d8ad 100644 --- a/usr/src/cmd/smbsrv/smbd/Makefile +++ b/usr/src/cmd/smbsrv/smbd/Makefile @@ -32,6 +32,7 @@ SRCS= \ smbd_nicmon.c \ smbd_opipe_doorsvc.c \ smbd_share_doorsvc.c \ + smbd_spool.c \ smbd_vss.c include ../../Makefile.cmd diff --git a/usr/src/cmd/smbsrv/smbd/smbd.h b/usr/src/cmd/smbsrv/smbd/smbd.h index cd17d19af2..c5130d6986 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd.h +++ b/usr/src/cmd/smbsrv/smbd/smbd.h @@ -46,6 +46,7 @@ int smbd_nicmon_start(const char *); void smbd_nicmon_stop(void); int smbd_nicmon_refresh(void); int smbd_dc_monitor_init(void); +void smbd_dc_monitor_refresh(void); smb_token_t *smbd_user_auth_logon(smb_logon_t *); void smbd_user_nonauth_logon(uint32_t); void smbd_user_auth_logoff(uint32_t); @@ -54,6 +55,12 @@ void smbd_set_secmode(int); boolean_t smbd_online(void); void smbd_online_wait(const char *); +void smbd_spool_init(void); +void smbd_spool_fini(void); +int smbd_cups_init(void); +void smbd_cups_fini(void); +void smbd_load_printers(void); + int smbd_vss_get_count(const char *, uint32_t *); void smbd_vss_get_snapshots(const char *, uint32_t, uint32_t *, uint32_t *, char **); @@ -75,6 +82,9 @@ typedef struct smbd { int s_door_srv; int s_door_opipe; int s_secmode; /* Current security mode */ + char s_site[MAXHOSTNAMELEN]; + smb_inaddr_t s_pdc; + boolean_t s_pdc_changed; pthread_t s_refresh_tid; pthread_t s_localtime_tid; pthread_t s_spool_tid; diff --git a/usr/src/cmd/smbsrv/smbd/smbd_join.c b/usr/src/cmd/smbsrv/smbd/smbd_join.c index d0030a8c0a..54fcaccfbb 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd_join.c +++ b/usr/src/cmd/smbsrv/smbd/smbd_join.c @@ -43,6 +43,9 @@ extern smbd_t smbd; +static mutex_t smbd_dc_mutex; +static cond_t smbd_dc_cv; + static void *smbd_dc_monitor(void *); static void smbd_dc_update(void); static boolean_t smbd_set_netlogon_cred(void); @@ -59,6 +62,9 @@ smbd_dc_monitor_init(void) pthread_attr_t attr; int rc; + (void) smb_config_getstr(SMB_CI_ADS_SITE, smbd.s_site, + MAXHOSTNAMELEN); + (void) smb_config_getip(SMB_CI_DOMAIN_SRV, &smbd.s_pdc); smb_ads_init(); if (smbd.s_secmode != SMB_SECMODE_DOMAIN) @@ -72,18 +78,55 @@ smbd_dc_monitor_init(void) return (rc); } +void +smbd_dc_monitor_refresh(void) +{ + char site[MAXHOSTNAMELEN]; + smb_inaddr_t pdc; + + site[0] = '\0'; + bzero(&pdc, sizeof (smb_inaddr_t)); + (void) smb_config_getstr(SMB_CI_ADS_SITE, site, MAXHOSTNAMELEN); + (void) smb_config_getip(SMB_CI_DOMAIN_SRV, &pdc); + + (void) mutex_lock(&smbd_dc_mutex); + + if ((bcmp(&smbd.s_pdc, &pdc, sizeof (smb_inaddr_t)) != 0) || + (smb_strcasecmp(smbd.s_site, site, 0) != 0)) { + bcopy(&pdc, &smbd.s_pdc, sizeof (smb_inaddr_t)); + (void) strlcpy(smbd.s_site, site, MAXHOSTNAMELEN); + smbd.s_pdc_changed = B_TRUE; + (void) cond_signal(&smbd_dc_cv); + } + + (void) mutex_unlock(&smbd_dc_mutex); +} + /*ARGSUSED*/ static void * smbd_dc_monitor(void *arg) { boolean_t ds_not_responding = B_FALSE; + boolean_t ds_cfg_changed = B_FALSE; + timestruc_t delay; int i; smbd_dc_update(); smbd_online_wait("smbd_dc_monitor"); while (smbd_online()) { - (void) sleep(SMBD_DC_MONITOR_INTERVAL); + delay.tv_sec = SMBD_DC_MONITOR_INTERVAL; + delay.tv_nsec = 0; + + (void) mutex_lock(&smbd_dc_mutex); + (void) cond_reltimedwait(&smbd_dc_cv, &smbd_dc_mutex, &delay); + + if (smbd.s_pdc_changed) { + smbd.s_pdc_changed = B_FALSE; + ds_cfg_changed = B_TRUE; + } + + (void) mutex_unlock(&smbd_dc_mutex); for (i = 0; i < SMBD_DC_MONITOR_ATTEMPTS; ++i) { if (dssetup_check_service() == 0) { @@ -95,10 +138,12 @@ smbd_dc_monitor(void *arg) (void) sleep(SMBD_DC_MONITOR_RETRY_INTERVAL); } - if (ds_not_responding) { + if (ds_not_responding) smb_log(smbd.s_loghd, LOG_NOTICE, "smbd_dc_monitor: domain service not responding"); + if (ds_not_responding || ds_cfg_changed) { + ds_cfg_changed = B_FALSE; smb_ads_refresh(); smbd_dc_update(); } diff --git a/usr/src/cmd/smbsrv/smbd/smbd_main.c b/usr/src/cmd/smbsrv/smbd/smbd_main.c index 3bca31d9a5..a7f3051233 100644 --- a/usr/src/cmd/smbsrv/smbd/smbd_main.c +++ b/usr/src/cmd/smbsrv/smbd/smbd_main.c @@ -59,10 +59,9 @@ #include "smbd.h" #define SMBD_ONLINE_WAIT_INTERVAL 10 -#define SMB_CUPS_DOCNAME "generic_doc" +#define SMBD_REFRESH_INTERVAL 10 #define DRV_DEVICE_PATH "/devices/pseudo/smbsrv@0:smbsrv" #define SMB_DBDIR "/var/smb" -#define SMB_SPOOL_WAIT 2 static void *smbd_nbt_listener(void *); static void *smbd_tcp_listener(void *); @@ -87,25 +86,16 @@ static void smbd_report(const char *fmt, ...); static void smbd_sig_handler(int sig); static int32_t smbd_gmtoff(void); -static int smbd_localtime_init(void); +static void smbd_localtime_init(void); static void *smbd_localtime_monitor(void *arg); -static int smbd_spool_init(void); -static void *smbd_spool_monitor(void *arg); - -static int smbd_spool_init(void); -static void *smbd_spool_monitor(void *arg); +static void smbd_dyndns_init(void); +static void smbd_load_shares(void); static int smbd_refresh_init(void); static void smbd_refresh_fini(void); static void *smbd_refresh_monitor(void *); -static void *smbd_nbt_receiver(void *); -static void *smbd_nbt_listener(void *); - -static void *smbd_tcp_receiver(void *); -static void *smbd_tcp_listener(void *); - static int smbd_start_listeners(void); static void smbd_stop_listeners(void); static int smbd_kernel_start(void); @@ -494,10 +484,14 @@ smbd_service_init(void) smbd.s_loghd = smb_log_create(SMBD_LOGSIZE, SMBD_LOGNAME); smb_codepage_init(); + rc = smbd_cups_init(); + if (smb_config_getbool(SMB_CI_PRINT_ENABLE)) + smbd_report("print service %savailable", (rc == 0) ? "" : "un"); + if (smbd_nicmon_start(SMBD_DEFAULT_INSTANCE_FMRI) != 0) smbd_report("NIC monitor failed to start"); - (void) dyndns_start(); + smbd_dyndns_init(); smb_ipc_init(); if (smb_netbios_start() != 0) @@ -539,7 +533,7 @@ smbd_service_init(void) } dyndns_update_zones(); - (void) smbd_localtime_init(); + smbd_localtime_init(); (void) smb_lgrp_start(); smb_pwd_init(B_TRUE); @@ -558,19 +552,8 @@ smbd_service_init(void) return (-1); } - if (smb_shr_load() != 0) { - smbd_report("failed to start loading shares: %s", - strerror(errno)); - (void) mutex_unlock(&smbd_service_mutex); - return (-1); - } - - if (smbd_spool_init() != 0) { - smbd_report("failed to start print monitor: %s", - strerror(errno)); - (void) mutex_unlock(&smbd_service_mutex); - return (-1); - } + smbd_load_shares(); + smbd_load_printers(); smbd.s_initialized = B_TRUE; smbd_report("service initialized"); @@ -622,6 +605,7 @@ smbd_service_fini(void) smb_domain_fini(); mlsvc_fini(); smb_netbios_stop(); + smbd_cups_fini(); smbd.s_initialized = B_FALSE; smbd_report("service terminated"); @@ -655,6 +639,9 @@ smbd_refresh_init() NULL); (void) pthread_attr_destroy(&tattr); + if (rc != 0) + smbd_report("unable to start refresh monitor: %s", + strerror(errno)); return (rc); } @@ -666,7 +653,8 @@ smbd_refresh_init() static void smbd_refresh_fini() { - if (pthread_self() != smbd.s_refresh_tid) { + if ((pthread_self() != smbd.s_refresh_tid) && + (smbd.s_refresh_tid != 0)) { (void) pthread_cancel(smbd.s_refresh_tid); (void) pthread_cond_destroy(&refresh_cond); (void) pthread_mutex_destroy(&refresh_mutex); @@ -674,20 +662,20 @@ smbd_refresh_fini() } /* - * smbd_refresh_monitor() - * - * Wait for a refresh event. When this thread wakes up, update the - * smbd configuration from the SMF config information then go back to - * wait for the next refresh. + * Wait for refresh events. When woken up, update the smbd configuration + * from SMF and check for changes that require service reconfiguration. + * Throttling is applied to coallesce multiple refresh events when the + * service is being refreshed repeatedly. */ /*ARGSUSED*/ static void * smbd_refresh_monitor(void *arg) { - smb_kmod_cfg_t cfg; - int error; + smbd_online_wait("smbd_refresh_monitor"); while (!smbd.s_shutting_down) { + (void) sleep(SMBD_REFRESH_INTERVAL); + (void) pthread_mutex_lock(&refresh_mutex); while ((atomic_swap_uint(&smbd.s_refreshes, 0) == 0) && (!smbd.s_shutting_down)) @@ -701,45 +689,29 @@ smbd_refresh_monitor(void *arg) (void) mutex_lock(&smbd_service_mutex); - /* - * We've been woken up by a refresh event so go do - * what is necessary. - */ - smb_ads_refresh(); + smbd_dc_monitor_refresh(); smb_ccache_remove(SMB_CCACHE_PATH); /* - * Start the dyndns thread, if required. * Clear the DNS zones for the existing interfaces * before updating the NIC interface list. */ - (void) dyndns_start(); dyndns_clear_zones(); if (smbd_nicmon_refresh() != 0) smbd_report("NIC monitor refresh failed"); + smb_netbios_name_reconfig(); smb_browser_reconfig(); dyndns_update_zones(); + (void) smbd_kernel_bind(); + smbd_load_shares(); + smbd_load_printers(); (void) mutex_unlock(&smbd_service_mutex); - - if (!smbd.s_kbound) { - if ((error = smbd_kernel_bind()) == 0) - (void) smb_shr_load(); - - continue; - } - - (void) smb_shr_load(); - - smb_load_kconfig(&cfg); - error = smb_kmod_setcfg(&cfg); - if (error < 0) - smbd_report("configuration update failed: %s", - strerror(error)); } + smbd.s_refresh_tid = 0; return (NULL); } @@ -778,16 +750,13 @@ smbd_online(void) void smbd_online_wait(const char *text) { - while (!smbd_online()) { - if (text != NULL) - smb_log(smbd.s_loghd, LOG_DEBUG, - "%s: waiting for online", text); - + while (!smbd_online()) (void) sleep(SMBD_ONLINE_WAIT_INTERVAL); - } - if (text != NULL) + if (text != NULL) { smb_log(smbd.s_loghd, LOG_DEBUG, "%s: online", text); + (void) fprintf(stderr, "%s: online\n", text); + } } /* @@ -821,14 +790,26 @@ smbd_already_running(void) /* * smbd_kernel_bind * - * This function open the smbsrv device and start the kernel service. + * If smbsrv is already bound, reload the configuration and update smbsrv. + * Otherwise, open the smbsrv device and start the kernel service. */ static int smbd_kernel_bind(void) { - int rc; + smb_kmod_cfg_t cfg; + int rc; - smbd_kernel_unbind(); + if (smbd.s_kbound) { + smb_load_kconfig(&cfg); + rc = smb_kmod_setcfg(&cfg); + if (rc < 0) + smbd_report("kernel configuration update failed: %s", + strerror(errno)); + return (rc); + } + + if (smb_kmod_isbound()) + smbd_kernel_unbind(); if ((rc = smb_kmod_bind()) == 0) { rc = smbd_kernel_start(); @@ -867,6 +848,7 @@ smbd_kernel_start(void) if (rc != 0) return (rc); + smbd_spool_init(); return (0); } @@ -876,69 +858,52 @@ smbd_kernel_start(void) static void smbd_kernel_unbind(void) { + smbd_spool_fini(); smbd_stop_listeners(); smb_kmod_unbind(); smbd.s_kbound = B_FALSE; } /* - * Initialization of the spool thread. - * Returns 0 on success, an error number if thread creation fails. + * Create the Dynamic DNS publisher thread. */ - -static int -smbd_spool_init(void) +static void +smbd_dyndns_init(void) { - pthread_attr_t tattr; - int rc; + pthread_t tid; + pthread_attr_t attr; + int rc; - (void) pthread_attr_init(&tattr); - (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); - rc = pthread_create(&smbd.s_spool_tid, &tattr, smbd_spool_monitor, - NULL); - (void) pthread_attr_destroy(&tattr); + dyndns_start(); - return (rc); + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&tid, &attr, dyndns_publisher, NULL); + (void) pthread_attr_destroy(&attr); + + if (rc != 0) + smbd_report("unable to start dyndns publisher: %s", + strerror(errno)); } /* - * This user thread blocks waiting for close print file - * in the kernel. It then uses the data returned from the ioctl - * to copy the spool file into the cups spooler. - * This function is really only used by Vista and Win7 clients, - * other versions of windows create only a zero size file and this - * be removed by spoolss_copy_spool_file function. + * Launches a thread to populate the share cache by share information + * stored in sharemgr */ - -/*ARGSUSED*/ -static void * -smbd_spool_monitor(void *arg) +static void +smbd_load_shares(void) { - uint32_t spool_num; - char username[MAXNAMELEN]; - char path[MAXPATHLEN]; - smb_inaddr_t ipaddr; - int error_retry_cnt = 5; - - smbd_online_wait("smbd_spool_monitor"); - - while (!smbd.s_shutting_down && (error_retry_cnt > 0)) { - errno = 0; - - if (smb_kmod_get_spool_doc(&spool_num, username, - path, &ipaddr) == 0) { - spoolss_copy_spool_file(&ipaddr, - username, path, SMB_CUPS_DOCNAME); - error_retry_cnt = 5; - } else { - if (errno == ECANCELED) - break; + pthread_t tid; + pthread_attr_t attr; + int rc; - (void) sleep(SMB_SPOOL_WAIT); - error_retry_cnt--; - } - } - return (NULL); + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&tid, &attr, smb_shr_load, NULL); + (void) pthread_attr_destroy(&attr); + + if (rc != 0) + smbd_report("unable to load disk shares: %s", strerror(errno)); } /* @@ -946,29 +911,28 @@ smbd_spool_monitor(void *arg) * Returns 0 on success, an error number if thread creation fails. */ -int +static void smbd_localtime_init(void) { - pthread_attr_t tattr; - int rc; + pthread_attr_t attr; + int rc; - (void) pthread_attr_init(&tattr); - (void) pthread_attr_setdetachstate(&tattr, PTHREAD_CREATE_DETACHED); - rc = pthread_create(&smbd.s_localtime_tid, &tattr, + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&smbd.s_localtime_tid, &attr, smbd_localtime_monitor, NULL); - (void) pthread_attr_destroy(&tattr); - return (rc); + (void) pthread_attr_destroy(&attr); + + if (rc != 0) + smbd_report("unable to monitor localtime: %s", strerror(errno)); } /* - * Local time thread to kernel land. - * Send local gmtoff to kernel module one time at startup - * and each time it changes (up to twice a year). - * Local gmtoff is checked once every 15 minutes and - * since some timezones are aligned on half and qtr hour boundaries, - * once an hour would likely suffice. + * Send local gmtoff to the kernel module one time at startup and each + * time it changes (up to twice a year). + * Local gmtoff is checked once every 15 minutes since some timezones + * are aligned on half and quarter hour boundaries. */ - /*ARGSUSED*/ static void * smbd_localtime_monitor(void *arg) diff --git a/usr/src/cmd/smbsrv/smbd/smbd_spool.c b/usr/src/cmd/smbsrv/smbd/smbd_spool.c new file mode 100644 index 0000000000..b479c3caa0 --- /dev/null +++ b/usr/src/cmd/smbsrv/smbd/smbd_spool.c @@ -0,0 +1,510 @@ +/* + * CDDL HEADER START + * + * The contents of this file are subject to the terms of the + * Common Development and Distribution License (the "License"). + * You may not use this file except in compliance with the License. + * + * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE + * or http://www.opensolaris.org/os/licensing. + * See the License for the specific language governing permissions + * and limitations under the License. + * + * When distributing Covered Code, include this CDDL HEADER in each + * file and include the License file at usr/src/OPENSOLARIS.LICENSE. + * If applicable, add the following below this CDDL HEADER, with the + * fields enclosed by brackets "[]" replaced with your own identifying + * information: Portions Copyright [yyyy] [name of copyright owner] + * + * CDDL HEADER END + */ +/* + * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. + */ + +/* + * CUPS support for the SMB and SPOOLSS print services. + */ + +#include <sys/types.h> +#include <sys/stat.h> +#include <cups/cups.h> +#include <strings.h> +#include <syslog.h> +#include <signal.h> +#include <pthread.h> +#include <synch.h> +#include <dlfcn.h> +#include <errno.h> +#include <smbsrv/smb.h> +#include <smbsrv/smb_share.h> +#include "smbd.h" + +#define SMB_SPOOL_WAIT 2 +#define SMBD_PJOBLEN 256 +#define SMBD_PRINTER "Postscript" +#define SMBD_FN_PREFIX "cifsprintjob-" +#define SMBD_CUPS_SPOOL_DIR "//var//spool//cups" +#define SMBD_CUPS_DOCNAME "generic_doc" + +typedef struct smbd_printjob { + pid_t pj_pid; + int pj_sysjob; + int pj_fd; + time_t pj_start_time; + int pj_status; + size_t pj_size; + int pj_page_count; + boolean_t pj_isspooled; + boolean_t pj_jobnum; + char pj_filename[SMBD_PJOBLEN]; + char pj_jobname[SMBD_PJOBLEN]; + char pj_username[SMBD_PJOBLEN]; + char pj_queuename[SMBD_PJOBLEN]; +} smbd_printjob_t; + +typedef struct smb_cups_ops { + void *cups_hdl; + cups_lang_t *(*cupsLangDefault)(); + const char *(*cupsLangEncoding)(cups_lang_t *); + void (*cupsLangFree)(cups_lang_t *); + ipp_status_t (*cupsLastError)(); + int (*cupsGetDests)(cups_dest_t **); + void (*cupsFreeDests)(int, cups_dest_t *); + ipp_t *(*cupsDoFileRequest)(http_t *, ipp_t *, + const char *, const char *); + ipp_t *(*ippNew)(); + void (*ippDelete)(); + char *(*ippErrorString)(); + ipp_attribute_t *(*ippAddString)(); + void (*httpClose)(http_t *); + http_t *(*httpConnect)(const char *, int); +} smb_cups_ops_t; + +static uint32_t smbd_cups_jobnum = 1; +static smb_cups_ops_t smb_cups; +static mutex_t smbd_cups_mutex; + +static void *smbd_spool_monitor(void *); +static smb_cups_ops_t *smbd_cups_ops(void); +static void smbd_print_share_comment(smb_share_t *, cups_dest_t *); +static void *smbd_share_printers(void *); +static void smbd_spool_copyfile(smb_inaddr_t *, char *, char *, char *); + +extern smbd_t smbd; + +/* + * Initialize the spool thread. + * Returns 0 on success, an error number if thread creation fails. + */ +void +smbd_spool_init(void) +{ + pthread_attr_t attr; + int rc; + + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&smbd.s_spool_tid, &attr, smbd_spool_monitor, NULL); + (void) pthread_attr_destroy(&attr); + + if (rc != 0) + smb_log(smbd.s_loghd, LOG_NOTICE, + "failed to start print monitor: %s", strerror(errno)); +} + +/* + * A single pthread_kill should be sufficient but we include + * a couple of retries to avoid implementation idiosyncrasies + * around signal delivery. + */ +void +smbd_spool_fini(void) +{ + int i; + + if (pthread_self() == smbd.s_spool_tid) + return; + + for (i = 0; i < 3 && smbd.s_spool_tid != 0; ++i) { + if (pthread_kill(smbd.s_spool_tid, SIGTERM) == ESRCH) + break; + + (void) sleep(1); + } +} + +/* + * This thread blocks waiting for close print file in the kernel. + * It then uses the data returned from the ioctl to copy the spool file + * into the cups spooler. + * + * This mechanism is really only used by Windows Vista and Windows 7. + * Other versions of Windows create a zero size file, which is removed + * by smbd_spool_copyfile. + */ +/*ARGSUSED*/ +static void * +smbd_spool_monitor(void *arg) +{ + uint32_t spool_num; + char username[MAXNAMELEN]; + char path[MAXPATHLEN]; + smb_inaddr_t ipaddr; + int error_retry_cnt = 5; + + smbd_online_wait("smbd_spool_monitor"); + + spoolss_register_copyfile(smbd_spool_copyfile); + + while (!smbd.s_shutting_down && (error_retry_cnt > 0)) { + errno = 0; + + if (smb_kmod_get_spool_doc(&spool_num, username, + path, &ipaddr) == 0) { + smbd_spool_copyfile(&ipaddr, + username, path, SMBD_CUPS_DOCNAME); + error_retry_cnt = 5; + } else { + if (errno == ECANCELED) + break; + + (void) sleep(SMB_SPOOL_WAIT); + error_retry_cnt--; + } + } + + spoolss_register_copyfile(NULL); + smbd.s_spool_tid = 0; + return (NULL); +} + +/* + * All versions of windows use this function to spool files to a printer + * via the cups interface + */ +static void +smbd_spool_copyfile(smb_inaddr_t *ipaddr, char *username, char *path, + char *doc_name) +{ + smb_cups_ops_t *cups; + http_t *http = NULL; /* HTTP connection to server */ + ipp_t *request = NULL; /* IPP Request */ + ipp_t *response = NULL; /* IPP Response */ + cups_lang_t *language = NULL; /* Default language */ + char uri[HTTP_MAX_URI]; /* printer-uri attribute */ + char new_jobname[SMBD_PJOBLEN]; + smbd_printjob_t pjob; + char clientname[INET6_ADDRSTRLEN]; + struct stat sbuf; + int rc = 1; + + if (stat(path, &sbuf)) { + smb_log(smbd.s_loghd, LOG_INFO, "smbd_spool_copyfile: %s: %s", + path, strerror(errno)); + return; + } + + /* + * Remove zero size files and return; these were inadvertantly + * created by XP or 2000. + */ + if (sbuf.st_size == 0) { + if (remove(path) != 0) + smb_log(smbd.s_loghd, LOG_INFO, + "smbd_spool_copyfile: cannot remove %s: %s", + path, strerror(errno)); + return; + } + + if ((cups = smbd_cups_ops()) == NULL) + return; + + if ((http = cups->httpConnect("localhost", 631)) == NULL) { + smb_log(smbd.s_loghd, LOG_INFO, + "smbd_spool_copyfile: cupsd not running"); + return; + } + + if ((request = cups->ippNew()) == NULL) { + smb_log(smbd.s_loghd, LOG_INFO, + "smbd_spool_copyfile: ipp not running"); + return; + } + + request->request.op.operation_id = IPP_PRINT_JOB; + request->request.op.request_id = 1; + language = cups->cupsLangDefault(); + + cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET, + "attributes-charset", NULL, cups->cupsLangEncoding(language)); + + cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE, + "attributes-natural-language", NULL, language->language); + + (void) snprintf(uri, sizeof (uri), "ipp://localhost/printers/%s", + SMBD_PRINTER); + pjob.pj_pid = pthread_self(); + pjob.pj_sysjob = 10; + (void) strlcpy(pjob.pj_filename, path, SMBD_PJOBLEN); + pjob.pj_start_time = time(NULL); + pjob.pj_status = 2; + pjob.pj_size = sbuf.st_blocks * 512; + pjob.pj_page_count = 1; + pjob.pj_isspooled = B_TRUE; + pjob.pj_jobnum = smbd_cups_jobnum; + + (void) strlcpy(pjob.pj_jobname, doc_name, SMBD_PJOBLEN); + (void) strlcpy(pjob.pj_username, username, SMBD_PJOBLEN); + (void) strlcpy(pjob.pj_queuename, SMBD_CUPS_SPOOL_DIR, SMBD_PJOBLEN); + + cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, + "printer-uri", NULL, uri); + + cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "requesting-user-name", NULL, pjob.pj_username); + + if (smb_inet_ntop(ipaddr, clientname, + SMB_IPSTRLEN(ipaddr->a_family)) == NULL) { + smb_log(smbd.s_loghd, LOG_INFO, + "smbd_spool_copyfile: %s: unknown client", clientname); + goto out; + } + + cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "job-originating-host-name", NULL, clientname); + + (void) snprintf(new_jobname, SMBD_PJOBLEN, "%s%d", + SMBD_FN_PREFIX, pjob.pj_jobnum); + cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, + "job-name", NULL, new_jobname); + + (void) snprintf(uri, sizeof (uri) - 1, "/printers/%s", SMBD_PRINTER); + + response = cups->cupsDoFileRequest(http, request, uri, + pjob.pj_filename); + if (response != NULL) { + if (response->request.status.status_code >= IPP_OK_CONFLICT) { + smb_log(smbd.s_loghd, LOG_ERR, + "smbd_spool_copyfile: printer %s: %s", + SMBD_PRINTER, + cups->ippErrorString(cups->cupsLastError())); + } else { + atomic_inc_32(&smbd_cups_jobnum); + rc = 0; + } + } else { + smb_log(smbd.s_loghd, LOG_ERR, + "smbd_spool_copyfile: unable to print to %s", + cups->ippErrorString(cups->cupsLastError())); + } + + if (rc == 0) + (void) unlink(pjob.pj_filename); + +out: + if (response) + cups->ippDelete(response); + + if (language) + cups->cupsLangFree(language); + + if (http) + cups->httpClose(http); +} + +int +smbd_cups_init(void) +{ + (void) mutex_lock(&smbd_cups_mutex); + + if (smb_cups.cups_hdl != NULL) { + (void) mutex_unlock(&smbd_cups_mutex); + return (0); + } + + if ((smb_cups.cups_hdl = dlopen("libcups.so.2", RTLD_NOW)) == NULL) { + (void) mutex_unlock(&smbd_cups_mutex); + smb_log(smbd.s_loghd, LOG_DEBUG, + "smbd_cups_init: cannot open libcups"); + return (ENOENT); + } + + smb_cups.cupsLangDefault = + (cups_lang_t *(*)())dlsym(smb_cups.cups_hdl, "cupsLangDefault"); + smb_cups.cupsLangEncoding = (const char *(*)(cups_lang_t *)) + dlsym(smb_cups.cups_hdl, "cupsLangEncoding"); + smb_cups.cupsDoFileRequest = + (ipp_t *(*)(http_t *, ipp_t *, const char *, const char *)) + dlsym(smb_cups.cups_hdl, "cupsDoFileRequest"); + smb_cups.cupsLastError = (ipp_status_t (*)()) + dlsym(smb_cups.cups_hdl, "cupsLastError"); + smb_cups.cupsLangFree = (void (*)(cups_lang_t *)) + dlsym(smb_cups.cups_hdl, "cupsLangFree"); + smb_cups.cupsGetDests = (int (*)(cups_dest_t **)) + dlsym(smb_cups.cups_hdl, "cupsGetDests"); + smb_cups.cupsFreeDests = (void (*)(int, cups_dest_t *)) + dlsym(smb_cups.cups_hdl, "cupsFreeDests"); + + smb_cups.httpClose = (void (*)(http_t *)) + dlsym(smb_cups.cups_hdl, "httpClose"); + smb_cups.httpConnect = (http_t *(*)(const char *, int)) + dlsym(smb_cups.cups_hdl, "httpConnect"); + + smb_cups.ippNew = (ipp_t *(*)())dlsym(smb_cups.cups_hdl, "ippNew"); + smb_cups.ippDelete = (void (*)())dlsym(smb_cups.cups_hdl, "ippDelete"); + smb_cups.ippErrorString = (char *(*)()) + dlsym(smb_cups.cups_hdl, "ippErrorString"); + smb_cups.ippAddString = (ipp_attribute_t *(*)()) + dlsym(smb_cups.cups_hdl, "ippAddString"); + + if (smb_cups.cupsLangDefault == NULL || + smb_cups.cupsLangEncoding == NULL || + smb_cups.cupsDoFileRequest == NULL || + smb_cups.cupsLastError == NULL || + smb_cups.cupsLangFree == NULL || + smb_cups.cupsGetDests == NULL || + smb_cups.cupsFreeDests == NULL || + smb_cups.ippNew == NULL || + smb_cups.httpClose == NULL || + smb_cups.httpConnect == NULL || + smb_cups.ippDelete == NULL || + smb_cups.ippErrorString == NULL || + smb_cups.ippAddString == NULL) { + (void) dlclose(smb_cups.cups_hdl); + smb_cups.cups_hdl = NULL; + (void) mutex_unlock(&smbd_cups_mutex); + smb_log(smbd.s_loghd, LOG_DEBUG, + "smbd_cups_init: cannot load libcups"); + return (ENOENT); + } + + (void) mutex_unlock(&smbd_cups_mutex); + return (0); +} + +void +smbd_cups_fini(void) +{ + (void) mutex_lock(&smbd_cups_mutex); + + if (smb_cups.cups_hdl != NULL) { + (void) dlclose(smb_cups.cups_hdl); + smb_cups.cups_hdl = NULL; + } + + (void) mutex_unlock(&smbd_cups_mutex); +} + +static smb_cups_ops_t * +smbd_cups_ops(void) +{ + if (smb_cups.cups_hdl == NULL) + return (NULL); + + return (&smb_cups); +} + +void +smbd_load_printers(void) +{ + pthread_t tid; + pthread_attr_t attr; + int rc; + + if (!smb_config_getbool(SMB_CI_PRINT_ENABLE)) + return; + + (void) pthread_attr_init(&attr); + (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); + rc = pthread_create(&tid, &attr, smbd_share_printers, &tid); + (void) pthread_attr_destroy(&attr); + + if (rc != 0) + smb_log(smbd.s_loghd, LOG_NOTICE, + "unable to load printer shares: %s", strerror(errno)); +} + +/* + * All print shares use the path from print$. + */ +/*ARGSUSED*/ +static void * +smbd_share_printers(void *arg) +{ + cups_dest_t *dests; + cups_dest_t *dest; + smb_cups_ops_t *cups; + smb_share_t si; + uint32_t nerr; + int num_dests; + int i; + + if (!smb_config_getbool(SMB_CI_PRINT_ENABLE)) + return (NULL); + + if ((cups = smbd_cups_ops()) == NULL) + return (NULL); + + if (smb_shr_get(SMB_SHARE_PRINT, &si) != NERR_Success) { + smb_log(smbd.s_loghd, LOG_DEBUG, + "smbd_share_printers unable to load %s", SMB_SHARE_PRINT); + return (NULL); + } + + num_dests = cups->cupsGetDests(&dests); + + for (i = num_dests, dest = dests; i > 0; i--, dest++) { + if (dest->instance != NULL) + continue; + + (void) strlcpy(si.shr_name, dest->name, MAXPATHLEN); + smbd_print_share_comment(&si, dest); + si.shr_type = STYPE_PRINTQ; + + nerr = smb_shr_add(&si); + if (nerr == NERR_Success || nerr == NERR_DuplicateShare) + smb_log(smbd.s_loghd, LOG_DEBUG, + "shared printer: %s", si.shr_name); + else + smb_log(smbd.s_loghd, LOG_DEBUG, + "smbd_share_printers: unable to add share %s: %u", + si.shr_name, nerr); + } + + cups->cupsFreeDests(num_dests, dests); + return (NULL); +} + +static void +smbd_print_share_comment(smb_share_t *si, cups_dest_t *dest) +{ + cups_option_t *options; + char *comment; + char *name; + char *value; + int i; + + comment = "Print Share"; + + if ((options = dest->options) == NULL) { + (void) strlcpy(si->shr_cmnt, comment, SMB_SHARE_CMNT_MAX); + return; + } + + for (i = 0; i < dest->num_options; ++i) { + name = options[i].name; + value = options[i].value; + + if (name == NULL || value == NULL || + *name == '\0' || *value == '\0') + continue; + + if (strcasecmp(name, "printer-info") == 0) { + comment = value; + break; + } + } + + (void) strlcpy(si->shr_cmnt, comment, SMB_SHARE_CMNT_MAX); +} |