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