summaryrefslogtreecommitdiff
path: root/usr/src/cmd/smbsrv
diff options
context:
space:
mode:
authorjoyce mcintosh <Joyce.McIntosh@Sun.COM>2010-08-11 16:48:54 -0700
committerjoyce mcintosh <Joyce.McIntosh@Sun.COM>2010-08-11 16:48:54 -0700
commitfd9ee8b58485b20072eeef1310a88ff348d5e7fa (patch)
tree74e8f3fc5f5409cdc6be3dae5631f8d0c672260a /usr/src/cmd/smbsrv
parente2c5185af3c50d9510e5df68aa37abdc6c0d3aac (diff)
downloadillumos-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.d8
-rw-r--r--usr/src/cmd/smbsrv/smbd/Makefile1
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd.h10
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd_join.c49
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd_main.c226
-rw-r--r--usr/src/cmd/smbsrv/smbd/smbd_spool.c510
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);
+}