dmake: do not set MAKEFLAGS=k
[unleashed/tickless.git] / usr / src / cmd / smbsrv / smbd / smbd_spool.c
blob5fc3d1099ed3c822783f70276630211afcdbbe04
1 /*
2 * CDDL HEADER START
4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
19 * CDDL HEADER END
22 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
23 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
27 * CUPS support for the SMB and SPOOLSS print services.
30 #include <sys/types.h>
31 #include <sys/stat.h>
32 #include <sys/atomic.h>
33 #include <strings.h>
34 #include <syslog.h>
35 #include <signal.h>
36 #include <pthread.h>
37 #include <synch.h>
38 #include <dlfcn.h>
39 #include <errno.h>
40 #include <sys/cfgparam.h>
41 #include <smbsrv/smb.h>
42 #include <smbsrv/smb_share.h>
43 #include "smbd.h"
45 #ifdef CONFIG_SMB_PRINTING
46 #define _IPP_PRIVATE_STRUCTURES 1
47 #include <cups/cups.h>
49 #define SMB_SPOOL_WAIT 2
50 #define SMBD_PJOBLEN 256
51 #define SMBD_PRINTER "Postscript"
52 #define SMBD_FN_PREFIX "cifsprintjob-"
53 #define SMBD_CUPS_SPOOL_DIR "//var//spool//cups"
54 #define SMBD_CUPS_DOCNAME "generic_doc"
56 typedef struct smbd_printjob {
57 pid_t pj_pid;
58 int pj_sysjob;
59 int pj_fd;
60 time_t pj_start_time;
61 int pj_status;
62 size_t pj_size;
63 int pj_page_count;
64 boolean_t pj_isspooled;
65 boolean_t pj_jobnum;
66 char pj_filename[SMBD_PJOBLEN];
67 char pj_jobname[SMBD_PJOBLEN];
68 char pj_username[SMBD_PJOBLEN];
69 char pj_queuename[SMBD_PJOBLEN];
70 } smbd_printjob_t;
72 typedef struct smb_cups_ops {
73 void *cups_hdl;
74 cups_lang_t *(*cupsLangDefault)();
75 const char *(*cupsLangEncoding)(cups_lang_t *);
76 void (*cupsLangFree)(cups_lang_t *);
77 ipp_status_t (*cupsLastError)();
78 int (*cupsGetDests)(cups_dest_t **);
79 void (*cupsFreeDests)(int, cups_dest_t *);
80 ipp_t *(*cupsDoFileRequest)(http_t *, ipp_t *,
81 const char *, const char *);
82 ipp_t *(*ippNew)();
83 void (*ippDelete)();
84 char *(*ippErrorString)();
85 ipp_attribute_t *(*ippAddString)();
86 void (*httpClose)(http_t *);
87 http_t *(*httpConnect)(const char *, int);
88 } smb_cups_ops_t;
90 static uint32_t smbd_cups_jobnum = 1;
91 static smb_cups_ops_t smb_cups;
92 static mutex_t smbd_cups_mutex;
94 static void *smbd_spool_monitor(void *);
95 static smb_cups_ops_t *smbd_cups_ops(void);
96 static void smbd_print_share_comment(smb_share_t *, cups_dest_t *);
97 static void *smbd_share_printers(void *);
98 static void smbd_spool_copyfile(smb_inaddr_t *, char *, char *, char *);
100 extern smbd_t smbd;
103 * Start the spool thread.
104 * Returns 0 on success, an error number if thread creation fails.
106 void
107 smbd_spool_start(void)
109 pthread_attr_t attr;
110 int rc;
112 if (!smb_config_getbool(SMB_CI_PRINT_ENABLE))
113 return;
115 (void) pthread_attr_init(&attr);
116 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
117 rc = pthread_create(&smbd.s_spool_tid, &attr, smbd_spool_monitor, NULL);
118 (void) pthread_attr_destroy(&attr);
120 if (rc != 0)
121 syslog(LOG_NOTICE,
122 "failed to start print monitor: %s", strerror(errno));
126 * A single pthread_kill should be sufficient but we include
127 * a couple of retries to avoid implementation idiosyncrasies
128 * around signal delivery.
130 void
131 smbd_spool_stop(void)
133 int i;
135 if (pthread_self() == smbd.s_spool_tid)
136 return;
138 for (i = 0; i < 3 && smbd.s_spool_tid != 0; ++i) {
139 if (pthread_kill(smbd.s_spool_tid, SIGTERM) == ESRCH)
140 break;
142 (void) sleep(1);
147 * This thread blocks waiting for close print file in the kernel.
148 * It then uses the data returned from the ioctl to copy the spool file
149 * into the cups spooler.
151 * This mechanism is really only used by Windows Vista and Windows 7.
152 * Other versions of Windows create a zero size file, which is removed
153 * by smbd_spool_copyfile.
155 /*ARGSUSED*/
156 static void *
157 smbd_spool_monitor(void *arg)
159 uint32_t spool_num;
160 char username[MAXNAMELEN];
161 char path[MAXPATHLEN];
162 smb_inaddr_t ipaddr;
163 int error_retry_cnt = 5;
165 smbd_online_wait("smbd_spool_monitor");
167 spoolss_register_copyfile(smbd_spool_copyfile);
169 while (!smbd.s_shutting_down && (error_retry_cnt > 0)) {
170 errno = 0;
172 if (smb_kmod_get_spool_doc(&spool_num, username,
173 path, &ipaddr) == 0) {
174 smbd_spool_copyfile(&ipaddr,
175 username, path, SMBD_CUPS_DOCNAME);
176 error_retry_cnt = 5;
177 } else {
178 if (errno == ECANCELED)
179 break;
180 if ((errno != EINTR) && (errno != EAGAIN))
181 error_retry_cnt--;
182 (void) sleep(SMB_SPOOL_WAIT);
186 spoolss_register_copyfile(NULL);
187 smbd.s_spool_tid = 0;
188 return (NULL);
192 * All versions of windows use this function to spool files to a printer
193 * via the cups interface
195 static void
196 smbd_spool_copyfile(smb_inaddr_t *ipaddr, char *username, char *path,
197 char *doc_name)
199 smb_cups_ops_t *cups;
200 http_t *http = NULL; /* HTTP connection to server */
201 ipp_t *request = NULL; /* IPP Request */
202 ipp_t *response = NULL; /* IPP Response */
203 cups_lang_t *language = NULL; /* Default language */
204 char uri[HTTP_MAX_URI]; /* printer-uri attribute */
205 char new_jobname[SMBD_PJOBLEN];
206 smbd_printjob_t pjob;
207 char clientname[INET6_ADDRSTRLEN];
208 struct stat sbuf;
209 int rc = 1;
211 if (stat(path, &sbuf)) {
212 syslog(LOG_INFO, "smbd_spool_copyfile: %s: %s",
213 path, strerror(errno));
214 return;
218 * Remove zero size files and return; these were inadvertantly
219 * created by XP or 2000.
221 if (sbuf.st_size == 0) {
222 if (remove(path) != 0)
223 syslog(LOG_INFO,
224 "smbd_spool_copyfile: cannot remove %s: %s",
225 path, strerror(errno));
226 return;
229 if ((cups = smbd_cups_ops()) == NULL)
230 return;
232 if ((http = cups->httpConnect("localhost", 631)) == NULL) {
233 syslog(LOG_INFO,
234 "smbd_spool_copyfile: cupsd not running");
235 return;
238 if ((request = cups->ippNew()) == NULL) {
239 syslog(LOG_INFO,
240 "smbd_spool_copyfile: ipp not running");
241 return;
244 request->request.op.operation_id = IPP_PRINT_JOB;
245 request->request.op.request_id = 1;
246 language = cups->cupsLangDefault();
248 cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_CHARSET,
249 "attributes-charset", NULL, cups->cupsLangEncoding(language));
251 cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_LANGUAGE,
252 "attributes-natural-language", NULL, language->language);
254 (void) snprintf(uri, sizeof (uri), "ipp://localhost/printers/%s",
255 SMBD_PRINTER);
256 pjob.pj_pid = pthread_self();
257 pjob.pj_sysjob = 10;
258 (void) strlcpy(pjob.pj_filename, path, SMBD_PJOBLEN);
259 pjob.pj_start_time = time(NULL);
260 pjob.pj_status = 2;
261 pjob.pj_size = sbuf.st_blocks * 512;
262 pjob.pj_page_count = 1;
263 pjob.pj_isspooled = B_TRUE;
264 pjob.pj_jobnum = smbd_cups_jobnum;
266 (void) strlcpy(pjob.pj_jobname, doc_name, SMBD_PJOBLEN);
267 (void) strlcpy(pjob.pj_username, username, SMBD_PJOBLEN);
268 (void) strlcpy(pjob.pj_queuename, SMBD_CUPS_SPOOL_DIR, SMBD_PJOBLEN);
270 cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
271 "printer-uri", NULL, uri);
273 cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
274 "requesting-user-name", NULL, pjob.pj_username);
276 if (smb_inet_ntop(ipaddr, clientname,
277 SMB_IPSTRLEN(ipaddr->a_family)) == NULL) {
278 syslog(LOG_INFO,
279 "smbd_spool_copyfile: %s: unknown client", clientname);
280 goto out;
283 cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
284 "job-originating-host-name", NULL, clientname);
286 (void) snprintf(new_jobname, SMBD_PJOBLEN, "%s%d",
287 SMBD_FN_PREFIX, pjob.pj_jobnum);
288 cups->ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
289 "job-name", NULL, new_jobname);
291 (void) snprintf(uri, sizeof (uri) - 1, "/printers/%s", SMBD_PRINTER);
293 response = cups->cupsDoFileRequest(http, request, uri,
294 pjob.pj_filename);
295 if (response != NULL) {
296 if (response->request.status.status_code >= IPP_OK_CONFLICT) {
297 syslog(LOG_ERR,
298 "smbd_spool_copyfile: printer %s: %s",
299 SMBD_PRINTER,
300 cups->ippErrorString(cups->cupsLastError()));
301 } else {
302 atomic_inc_32(&smbd_cups_jobnum);
303 rc = 0;
305 } else {
306 syslog(LOG_ERR,
307 "smbd_spool_copyfile: unable to print to %s",
308 cups->ippErrorString(cups->cupsLastError()));
311 if (rc == 0)
312 (void) unlink(pjob.pj_filename);
314 out:
315 if (response)
316 cups->ippDelete(response);
318 if (language)
319 cups->cupsLangFree(language);
321 if (http)
322 cups->httpClose(http);
326 smbd_cups_init(void)
328 (void) mutex_lock(&smbd_cups_mutex);
330 if (smb_cups.cups_hdl != NULL) {
331 (void) mutex_unlock(&smbd_cups_mutex);
332 return (0);
335 if ((smb_cups.cups_hdl = dlopen("libcups.so.2", RTLD_NOW)) == NULL) {
336 (void) mutex_unlock(&smbd_cups_mutex);
337 syslog(LOG_DEBUG,
338 "smbd_cups_init: cannot open libcups");
339 return (ENOENT);
342 smb_cups.cupsLangDefault =
343 (cups_lang_t *(*)())dlsym(smb_cups.cups_hdl, "cupsLangDefault");
344 smb_cups.cupsLangEncoding = (const char *(*)(cups_lang_t *))
345 dlsym(smb_cups.cups_hdl, "cupsLangEncoding");
346 smb_cups.cupsDoFileRequest =
347 (ipp_t *(*)(http_t *, ipp_t *, const char *, const char *))
348 dlsym(smb_cups.cups_hdl, "cupsDoFileRequest");
349 smb_cups.cupsLastError = (ipp_status_t (*)())
350 dlsym(smb_cups.cups_hdl, "cupsLastError");
351 smb_cups.cupsLangFree = (void (*)(cups_lang_t *))
352 dlsym(smb_cups.cups_hdl, "cupsLangFree");
353 smb_cups.cupsGetDests = (int (*)(cups_dest_t **))
354 dlsym(smb_cups.cups_hdl, "cupsGetDests");
355 smb_cups.cupsFreeDests = (void (*)(int, cups_dest_t *))
356 dlsym(smb_cups.cups_hdl, "cupsFreeDests");
358 smb_cups.httpClose = (void (*)(http_t *))
359 dlsym(smb_cups.cups_hdl, "httpClose");
360 smb_cups.httpConnect = (http_t *(*)(const char *, int))
361 dlsym(smb_cups.cups_hdl, "httpConnect");
363 smb_cups.ippNew = (ipp_t *(*)())dlsym(smb_cups.cups_hdl, "ippNew");
364 smb_cups.ippDelete = (void (*)())dlsym(smb_cups.cups_hdl, "ippDelete");
365 smb_cups.ippErrorString = (char *(*)())
366 dlsym(smb_cups.cups_hdl, "ippErrorString");
367 smb_cups.ippAddString = (ipp_attribute_t *(*)())
368 dlsym(smb_cups.cups_hdl, "ippAddString");
370 if (smb_cups.cupsLangDefault == NULL ||
371 smb_cups.cupsLangEncoding == NULL ||
372 smb_cups.cupsDoFileRequest == NULL ||
373 smb_cups.cupsLastError == NULL ||
374 smb_cups.cupsLangFree == NULL ||
375 smb_cups.cupsGetDests == NULL ||
376 smb_cups.cupsFreeDests == NULL ||
377 smb_cups.ippNew == NULL ||
378 smb_cups.httpClose == NULL ||
379 smb_cups.httpConnect == NULL ||
380 smb_cups.ippDelete == NULL ||
381 smb_cups.ippErrorString == NULL ||
382 smb_cups.ippAddString == NULL) {
383 (void) dlclose(smb_cups.cups_hdl);
384 smb_cups.cups_hdl = NULL;
385 (void) mutex_unlock(&smbd_cups_mutex);
386 syslog(LOG_DEBUG,
387 "smbd_cups_init: cannot load libcups");
388 return (ENOENT);
391 (void) mutex_unlock(&smbd_cups_mutex);
392 return (0);
395 void
396 smbd_cups_fini(void)
398 (void) mutex_lock(&smbd_cups_mutex);
400 if (smb_cups.cups_hdl != NULL) {
401 (void) dlclose(smb_cups.cups_hdl);
402 smb_cups.cups_hdl = NULL;
405 (void) mutex_unlock(&smbd_cups_mutex);
408 static smb_cups_ops_t *
409 smbd_cups_ops(void)
411 if (smb_cups.cups_hdl == NULL)
412 return (NULL);
414 return (&smb_cups);
417 void
418 smbd_load_printers(void)
420 pthread_t tid;
421 pthread_attr_t attr;
422 int rc;
424 if (!smb_config_getbool(SMB_CI_PRINT_ENABLE))
425 return;
427 (void) pthread_attr_init(&attr);
428 (void) pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
429 rc = pthread_create(&tid, &attr, smbd_share_printers, &tid);
430 (void) pthread_attr_destroy(&attr);
432 if (rc != 0)
433 syslog(LOG_NOTICE,
434 "unable to load printer shares: %s", strerror(errno));
438 * All print shares use the path from print$.
440 /*ARGSUSED*/
441 static void *
442 smbd_share_printers(void *arg)
444 cups_dest_t *dests;
445 cups_dest_t *dest;
446 smb_cups_ops_t *cups;
447 smb_share_t si;
448 uint32_t nerr;
449 int num_dests;
450 int i;
452 if (!smb_config_getbool(SMB_CI_PRINT_ENABLE))
453 return (NULL);
455 if ((cups = smbd_cups_ops()) == NULL)
456 return (NULL);
458 if (smb_shr_get(SMB_SHARE_PRINT, &si) != NERR_Success) {
459 syslog(LOG_DEBUG,
460 "smbd_share_printers unable to load %s", SMB_SHARE_PRINT);
461 return (NULL);
464 num_dests = cups->cupsGetDests(&dests);
466 for (i = num_dests, dest = dests; i > 0; i--, dest++) {
467 if (dest->instance != NULL)
468 continue;
470 (void) strlcpy(si.shr_name, dest->name, MAXPATHLEN);
471 smbd_print_share_comment(&si, dest);
472 si.shr_type = STYPE_PRINTQ;
474 nerr = smb_shr_add(&si);
475 if (nerr == NERR_Success || nerr == NERR_DuplicateShare)
476 syslog(LOG_DEBUG,
477 "shared printer: %s", si.shr_name);
478 else
479 syslog(LOG_DEBUG,
480 "smbd_share_printers: unable to add share %s: %u",
481 si.shr_name, nerr);
484 cups->cupsFreeDests(num_dests, dests);
485 return (NULL);
488 static void
489 smbd_print_share_comment(smb_share_t *si, cups_dest_t *dest)
491 cups_option_t *options;
492 char *comment;
493 char *name;
494 char *value;
495 int i;
497 comment = "Print Share";
499 if ((options = dest->options) == NULL) {
500 (void) strlcpy(si->shr_cmnt, comment, SMB_SHARE_CMNT_MAX);
501 return;
504 for (i = 0; i < dest->num_options; ++i) {
505 name = options[i].name;
506 value = options[i].value;
508 if (name == NULL || value == NULL ||
509 *name == '\0' || *value == '\0')
510 continue;
512 if (strcasecmp(name, "printer-info") == 0) {
513 comment = value;
514 break;
518 (void) strlcpy(si->shr_cmnt, comment, SMB_SHARE_CMNT_MAX);
521 #else /* CONFIG_SMB_PRINTING */
524 * If not CONFIG_SMB_PRINTING, just provide a few "stubs".
528 smbd_cups_init(void)
530 return (ENOENT);
533 void
534 smbd_cups_fini(void)
538 void
539 smbd_load_printers(void)
543 void
544 smbd_spool_init(void)
548 void
549 smbd_spool_fini(void)
553 void
554 smbd_spool_start(void)
558 void
559 smbd_spool_stop(void)
563 #endif /* CONFIG_SMB_PRINTING */