]> git.ipfire.org Git - thirdparty/cups.git/blob - systemv/lp.c
Import CUPS 1.4svn-r7153.
[thirdparty/cups.git] / systemv / lp.c
1 /*
2 * "$Id: lp.c 6649 2007-07-11 21:46:42Z mike $"
3 *
4 * "lp" command for the Common UNIX Printing System (CUPS).
5 *
6 * Copyright 2007 by Apple Inc.
7 * Copyright 1997-2007 by Easy Software Products.
8 *
9 * These coded instructions, statements, and computer programs are the
10 * property of Apple Inc. and are protected by Federal copyright
11 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
12 * which should have been included with this file. If this file is
13 * file is missing or damaged, see the license at "http://www.cups.org/".
14 *
15 * Contents:
16 *
17 * main() - Parse options and send files for printing.
18 * restart_job() - Restart a job.
19 * set_job_attrs() - Set job attributes.
20 * sighandler() - Signal catcher for when we print from stdin...
21 */
22
23 /*
24 * Include necessary headers...
25 */
26
27 #include <stdio.h>
28 #include <stdlib.h>
29 #include <errno.h>
30 #include <cups/string.h>
31 #include <cups/cups.h>
32 #include <cups/i18n.h>
33
34
35 #ifndef WIN32
36 # include <unistd.h>
37 # include <signal.h>
38
39
40 /*
41 * Local functions.
42 */
43
44 void sighandler(int);
45 #endif /* !WIN32 */
46 int restart_job(const char *command, int job_id);
47 int set_job_attrs(const char *command, int job_id, int num_options,
48 cups_option_t *options);
49
50
51 /*
52 * Globals...
53 */
54
55 char tempfile[1024]; /* Temporary file for printing from stdin */
56
57
58 /*
59 * 'main()' - Parse options and send files for printing.
60 */
61
62 int
63 main(int argc, /* I - Number of command-line arguments */
64 char *argv[]) /* I - Command-line arguments */
65 {
66 int i, j; /* Looping vars */
67 int job_id; /* Job ID */
68 char *printer, /* Printer name */
69 *instance, /* Instance name */
70 *val, /* Option value */
71 *title; /* Job title */
72 int priority; /* Job priority (1-100) */
73 int num_copies; /* Number of copies per file */
74 int num_files; /* Number of files to print */
75 const char *files[1000]; /* Files to print */
76 cups_dest_t *dest; /* Selected destination */
77 int num_options; /* Number of options */
78 cups_option_t *options; /* Options */
79 int end_options; /* No more options? */
80 int silent; /* Silent or verbose output? */
81 char buffer[8192]; /* Copy buffer */
82 ssize_t bytes; /* Bytes copied */
83 off_t filesize; /* Size of temp file */
84 int temp; /* Temporary file descriptor */
85 #if defined(HAVE_SIGACTION) && !defined(HAVE_SIGSET)
86 struct sigaction action; /* Signal action */
87 struct sigaction oldaction; /* Old signal action */
88 #endif /* HAVE_SIGACTION && !HAVE_SIGSET*/
89
90
91 #ifdef __sun
92 /*
93 * Solaris does some rather strange things to re-queue remote print
94 * jobs. On bootup, the "lp" command is run as "printd" to re-spool
95 * any remote jobs in /var/spool/print. Since CUPS doesn't need this
96 * nonsense, we just need to add the necessary check here to prevent
97 * lp from causing boot problems...
98 */
99
100 if ((val = strrchr(argv[0], '/')) != NULL)
101 val ++;
102 else
103 val = argv[0];
104
105 if (!strcmp(val, "printd"))
106 return (0);
107 #endif /* __sun */
108
109 _cupsSetLocale(argv);
110
111 silent = 0;
112 printer = NULL;
113 dest = NULL;
114 num_options = 0;
115 options = NULL;
116 num_files = 0;
117 title = NULL;
118 job_id = 0;
119 end_options = 0;
120
121 for (i = 1; i < argc; i ++)
122 if (argv[i][0] == '-' && argv[i][1] && !end_options)
123 switch (argv[i][1])
124 {
125 case 'E' : /* Encrypt */
126 #ifdef HAVE_SSL
127 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
128 #else
129 _cupsLangPrintf(stderr,
130 _("%s: Sorry, no encryption support compiled in!\n"),
131 argv[0]);
132 #endif /* HAVE_SSL */
133 break;
134
135 case 'U' : /* Username */
136 if (argv[i][2] != '\0')
137 cupsSetUser(argv[i] + 2);
138 else
139 {
140 i ++;
141 if (i >= argc)
142 {
143 _cupsLangPrintf(stderr,
144 _("%s: Error - expected username after "
145 "\'-U\' option!\n"),
146 argv[0]);
147 return (1);
148 }
149
150 cupsSetUser(argv[i]);
151 }
152 break;
153
154 case 'c' : /* Copy to spool dir (always enabled) */
155 break;
156
157 case 'd' : /* Destination printer or class */
158 if (argv[i][2] != '\0')
159 printer = argv[i] + 2;
160 else
161 {
162 i ++;
163
164 if (i >= argc)
165 {
166 _cupsLangPrintf(stderr,
167 _("%s: Error - expected destination after "
168 "\'-d\' option!\n"),
169 argv[0]);
170 return (1);
171 }
172
173 printer = argv[i];
174 }
175
176 if ((instance = strrchr(printer, '/')) != NULL)
177 *instance++ = '\0';
178
179 if ((dest = cupsGetNamedDest(NULL, printer, instance)) != NULL)
180 {
181 for (j = 0; j < dest->num_options; j ++)
182 if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
183 num_options = cupsAddOption(dest->options[j].name,
184 dest->options[j].value,
185 num_options, &options);
186 }
187 break;
188
189 case 'f' : /* Form */
190 if (!argv[i][2])
191 {
192 i ++;
193
194 if (i >= argc)
195 {
196 _cupsLangPrintf(stderr,
197 _("%s: Error - expected form after \'-f\' "
198 "option!\n"),
199 argv[0]);
200 return (1);
201 }
202 }
203
204 _cupsLangPrintf(stderr, _("%s: Warning - form option ignored!\n"),
205 argv[0]);
206 break;
207
208 case 'h' : /* Destination host */
209 if (argv[i][2] != '\0')
210 cupsSetServer(argv[i] + 2);
211 else
212 {
213 i ++;
214
215 if (i >= argc)
216 {
217 _cupsLangPrintf(stderr,
218 _("%s: Error - expected hostname after "
219 "\'-h\' option!\n"),
220 argv[0]);
221 return (1);
222 }
223
224 cupsSetServer(argv[i]);
225 }
226 break;
227
228 case 'i' : /* Change job */
229 if (argv[i][2])
230 val = argv[i] + 2;
231 else
232 {
233 i ++;
234
235 if (i >= argc)
236 {
237 _cupsLangPrintf(stderr,
238 _("%s: Expected job ID after \'-i\' option!\n"),
239 argv[0]);
240 return (1);
241 }
242
243 val = argv[i];
244 }
245
246 if (num_files > 0)
247 {
248 _cupsLangPrintf(stderr,
249 _("%s: Error - cannot print files and alter "
250 "jobs simultaneously!\n"),
251 argv[0]);
252 return (1);
253 }
254
255 if (strrchr(val, '-') != NULL)
256 job_id = atoi(strrchr(val, '-') + 1);
257 else
258 job_id = atoi(val);
259
260 if (job_id < 0)
261 {
262 _cupsLangPrintf(stderr, _("%s: Error - bad job ID!\n"), argv[0]);
263 break;
264 }
265 break;
266
267 case 'm' : /* Send email when job is done */
268 #ifdef __sun
269 case 'p' : /* Notify on completion */
270 #endif /* __sun */
271 case 'w' : /* Write to console or email */
272 {
273 char email[1024]; /* EMail address */
274
275
276 snprintf(email, sizeof(email), "mailto:%s@%s", cupsUser(),
277 httpGetHostname(NULL, buffer, sizeof(buffer)));
278 num_options = cupsAddOption("notify-recipient-uri", email,
279 num_options, &options);
280 }
281
282 silent = 1;
283 break;
284
285 case 'n' : /* Number of copies */
286 if (argv[i][2] != '\0')
287 num_copies = atoi(argv[i] + 2);
288 else
289 {
290 i ++;
291
292 if (i >= argc)
293 {
294 _cupsLangPrintf(stderr,
295 _("%s: Error - expected copies after "
296 "\'-n\' option!\n"),
297 argv[0]);
298 return (1);
299 }
300
301 num_copies = atoi(argv[i]);
302 }
303
304 sprintf(buffer, "%d", num_copies);
305 num_options = cupsAddOption("copies", buffer, num_options, &options);
306 break;
307
308 case 'o' : /* Option */
309 if (argv[i][2] != '\0')
310 num_options = cupsParseOptions(argv[i] + 2, num_options, &options);
311 else
312 {
313 i ++;
314
315 if (i >= argc)
316 {
317 _cupsLangPrintf(stderr,
318 _("%s: Error - expected option string after "
319 "\'-o\' option!\n"),
320 argv[0]);
321 return (1);
322 }
323
324 num_options = cupsParseOptions(argv[i], num_options, &options);
325 }
326 break;
327
328 #ifndef __sun
329 case 'p' : /* Queue priority */
330 #endif /* !__sun */
331 case 'q' : /* Queue priority */
332 if (argv[i][2] != '\0')
333 priority = atoi(argv[i] + 2);
334 else
335 {
336 if ((i + 1) >= argc)
337 {
338 _cupsLangPrintf(stderr,
339 _("%s: Error - expected priority after "
340 "\'-%c\' option!\n"),
341 argv[0], argv[i][1]);
342 return (1);
343 }
344
345 i ++;
346
347 priority = atoi(argv[i]);
348 }
349
350 /*
351 * For 100% Solaris compatibility, need to add:
352 *
353 * priority = 99 * (39 - priority) / 39 + 1;
354 *
355 * However, to keep CUPS lp the same across all platforms
356 * we will break compatibility this far...
357 */
358
359 if (priority < 1 || priority > 100)
360 {
361 _cupsLangPrintf(stderr,
362 _("%s: Error - priority must be between 1 and "
363 "100.\n"),
364 argv[0]);
365 return (1);
366 }
367
368 sprintf(buffer, "%d", priority);
369 num_options = cupsAddOption("job-priority", buffer, num_options, &options);
370 break;
371
372 case 's' : /* Silent */
373 silent = 1;
374 break;
375
376 case 't' : /* Title */
377 if (argv[i][2] != '\0')
378 title = argv[i] + 2;
379 else
380 {
381 i ++;
382
383 if (i >= argc)
384 {
385 _cupsLangPrintf(stderr,
386 _("%s: Error - expected title after "
387 "\'-t\' option!\n"),
388 argv[0]);
389 return (1);
390 }
391
392 title = argv[i];
393 }
394 break;
395
396 case 'y' : /* mode-list */
397 if (!argv[i][2])
398 {
399 i ++;
400
401 if (i >= argc)
402 {
403 _cupsLangPrintf(stderr,
404 _("%s: Error - expected mode list after "
405 "\'-y\' option!\n"),
406 argv[0]);
407 return (1);
408 }
409 }
410
411 _cupsLangPrintf(stderr,
412 _("%s: Warning - mode option ignored!\n"),
413 argv[0]);
414 break;
415
416 case 'H' : /* Hold job */
417 if (argv[i][2])
418 val = argv[i] + 2;
419 else
420 {
421 i ++;
422
423 if (i >= argc)
424 {
425 _cupsLangPrintf(stderr,
426 _("%s: Error - expected hold name after "
427 "\'-H\' option!\n"),
428 argv[0]);
429 return (1);
430 }
431
432 val = argv[i];
433 }
434
435 if (!strcmp(val, "hold"))
436 num_options = cupsAddOption("job-hold-until", "indefinite",
437 num_options, &options);
438 else if (!strcmp(val, "resume") ||
439 !strcmp(val, "release"))
440 num_options = cupsAddOption("job-hold-until", "no-hold",
441 num_options, &options);
442 else if (!strcmp(val, "immediate"))
443 num_options = cupsAddOption("job-priority", "100",
444 num_options, &options);
445 else if (!strcmp(val, "restart"))
446 {
447 if (job_id < 1)
448 {
449 _cupsLangPrintf(stderr,
450 _("%s: Need job ID (\'-i jobid\') before "
451 "\'-H restart\'!\n"),
452 argv[0]);
453 return (1);
454 }
455
456 if (restart_job(argv[0], job_id))
457 return (1);
458 }
459 else
460 num_options = cupsAddOption("job-hold-until", val,
461 num_options, &options);
462 break;
463
464 case 'P' : /* Page list */
465 if (argv[i][2])
466 val = argv[i] + 2;
467 else
468 {
469 i ++;
470
471 if (i >= argc)
472 {
473 _cupsLangPrintf(stderr,
474 _("%s: Error - expected page list after "
475 "\'-P\' option!\n"),
476 argv[0]);
477 return (1);
478 }
479
480 val = argv[i];
481 }
482
483 num_options = cupsAddOption("page-ranges", val, num_options,
484 &options);
485 break;
486
487 case 'S' : /* character set */
488 if (!argv[i][2])
489 {
490 i ++;
491
492 if (i >= argc)
493 {
494 _cupsLangPrintf(stderr,
495 _("%s: Error - expected character set after "
496 "\'-S\' option!\n"),
497 argv[0]);
498 return (1);
499 }
500 }
501
502 _cupsLangPrintf(stderr,
503 _("%s: Warning - character set option ignored!\n"),
504 argv[0]);
505 break;
506
507 case 'T' : /* Content-Type */
508 if (!argv[i][2])
509 {
510 i ++;
511
512 if (i >= argc)
513 {
514 _cupsLangPrintf(stderr,
515 _("%s: Error - expected content type after "
516 "\'-T\' option!\n"),
517 argv[0]);
518 return (1);
519 }
520 }
521
522 _cupsLangPrintf(stderr,
523 _("%s: Warning - content type option ignored!\n"),
524 argv[0]);
525 break;
526
527 case '-' : /* Stop processing options */
528 end_options = 1;
529 break;
530
531 default :
532 _cupsLangPrintf(stderr, _("%s: Error - unknown option \'%c\'!\n"),
533 argv[0], argv[i][1]);
534 return (1);
535 }
536 else if (!strcmp(argv[i], "-"))
537 {
538 if (num_files || job_id)
539 {
540 _cupsLangPrintf(stderr,
541 _("%s: Error - cannot print from stdin if files or a "
542 "job ID are provided!\n"),
543 argv[0]);
544 return (1);
545 }
546
547 break;
548 }
549 else if (num_files < 1000 && job_id == 0)
550 {
551 /*
552 * Print a file...
553 */
554
555 if (access(argv[i], R_OK) != 0)
556 {
557 _cupsLangPrintf(stderr, _("%s: Error - unable to access \"%s\" - %s\n"),
558 argv[0], argv[i], strerror(errno));
559 return (1);
560 }
561
562 files[num_files] = argv[i];
563 num_files ++;
564
565 if (title == NULL)
566 {
567 if ((title = strrchr(argv[i], '/')) != NULL)
568 title ++;
569 else
570 title = argv[i];
571 }
572 }
573 else
574 _cupsLangPrintf(stderr, _("%s: Error - too many files - \"%s\"\n"),
575 argv[0], argv[i]);
576
577 /*
578 * See if we are altering an existing job...
579 */
580
581 if (job_id)
582 return (set_job_attrs(argv[0], job_id, num_options, options));
583
584 /*
585 * See if we have any files to print; if not, print from stdin...
586 */
587
588 if (printer == NULL)
589 {
590 if ((dest = cupsGetNamedDest(NULL, NULL, NULL)) != NULL)
591 {
592 printer = dest->name;
593
594 for (j = 0; j < dest->num_options; j ++)
595 if (cupsGetOption(dest->options[j].name, num_options, options) == NULL)
596 num_options = cupsAddOption(dest->options[j].name,
597 dest->options[j].value,
598 num_options, &options);
599 }
600 }
601
602 if (printer == NULL)
603 {
604 val = NULL;
605
606 if ((printer = getenv("LPDEST")) == NULL)
607 {
608 if ((printer = getenv("PRINTER")) != NULL)
609 {
610 if (!strcmp(printer, "lp"))
611 printer = NULL;
612 else
613 val = "PRINTER";
614 }
615 }
616 else
617 val = "LPDEST";
618
619 if (printer && !cupsGetNamedDest(NULL, printer, NULL))
620 _cupsLangPrintf(stderr,
621 _("%s: Error - %s environment variable names "
622 "non-existent destination \"%s\"!\n"),
623 argv[0], val, printer);
624 else if (cupsLastError() == IPP_NOT_FOUND)
625 _cupsLangPrintf(stderr,
626 _("%s: Error - no default destination available.\n"),
627 argv[0]);
628 else
629 _cupsLangPrintf(stderr,
630 _("%s: Error - scheduler not responding!\n"),
631 argv[0]);
632
633 return (1);
634 }
635
636 if (num_files > 0)
637 job_id = cupsPrintFiles(printer, num_files, files, title, num_options, options);
638 else
639 {
640 num_files = 1;
641
642 #ifndef WIN32
643 # if defined(HAVE_SIGSET)
644 sigset(SIGHUP, sighandler);
645 if (sigset(SIGINT, sighandler) == SIG_IGN)
646 sigset(SIGINT, SIG_IGN);
647 sigset(SIGTERM, sighandler);
648 # elif defined(HAVE_SIGACTION)
649 memset(&action, 0, sizeof(action));
650 action.sa_handler = sighandler;
651
652 sigaction(SIGHUP, &action, NULL);
653 sigaction(SIGINT, NULL, &oldaction);
654 if (oldaction.sa_handler != SIG_IGN)
655 sigaction(SIGINT, &action, NULL);
656 sigaction(SIGTERM, &action, NULL);
657 # else
658 signal(SIGHUP, sighandler);
659 if (signal(SIGINT, sighandler) == SIG_IGN)
660 signal(SIGINT, SIG_IGN);
661 signal(SIGTERM, sighandler);
662 # endif
663 #endif /* !WIN32 */
664
665 temp = cupsTempFd(tempfile, sizeof(tempfile));
666
667 if (temp < 0)
668 {
669 _cupsLangPrintf(stderr,
670 _("%s: Error - unable to create temporary file \"%s\" - %s\n"),
671 argv[0], tempfile, strerror(errno));
672 return (1);
673 }
674
675 while ((bytes = read(0, buffer, sizeof(buffer))) > 0)
676 if (write(temp, buffer, bytes) < 0)
677 {
678 _cupsLangPrintf(stderr,
679 _("%s: Error - unable to write to temporary file "
680 "\"%s\" - %s\n"),
681 argv[0], tempfile, strerror(errno));
682 close(temp);
683 unlink(tempfile);
684 return (1);
685 }
686
687 filesize = lseek(temp, 0, SEEK_CUR);
688 close(temp);
689
690 if (filesize <= 0)
691 {
692 _cupsLangPrintf(stderr,
693 _("%s: Error - stdin is empty, so no job has been sent.\n"),
694 argv[0]);
695 unlink(tempfile);
696 return (1);
697 }
698
699 if (title)
700 job_id = cupsPrintFile(printer, tempfile, title, num_options, options);
701 else
702 job_id = cupsPrintFile(printer, tempfile, "(stdin)", num_options, options);
703
704 unlink(tempfile);
705 }
706
707 if (job_id < 1)
708 {
709 _cupsLangPrintf(stderr, "%s: %s\n", argv[0], cupsLastErrorString());
710 return (1);
711 }
712 else if (!silent)
713 _cupsLangPrintf(stdout, _("request id is %s-%d (%d file(s))\n"),
714 printer, job_id, num_files);
715
716 return (0);
717 }
718
719
720 /*
721 * 'restart_job()' - Restart a job.
722 */
723
724 int /* O - Exit status */
725 restart_job(const char *command, /* I - Command name */
726 int job_id) /* I - Job ID */
727 {
728 http_t *http; /* HTTP connection to server */
729 ipp_t *request; /* IPP request */
730 char uri[HTTP_MAX_URI]; /* URI for job */
731
732
733 http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
734
735 request = ippNewRequest(IPP_RESTART_JOB);
736
737 sprintf(uri, "ipp://localhost/jobs/%d", job_id);
738
739 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
740 "job-uri", NULL, uri);
741
742 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
743 "requesting-user-name", NULL, cupsUser());
744
745 ippDelete(cupsDoRequest(http, request, "/jobs"));
746
747 if (cupsLastError() > IPP_OK_CONFLICT)
748 {
749 _cupsLangPrintf(stderr, "%s: %s\n", command, cupsLastErrorString());
750 return (1);
751 }
752
753 return (0);
754 }
755
756
757 /*
758 * 'set_job_attrs()' - Set job attributes.
759 */
760
761 int /* O - Exit status */
762 set_job_attrs(const char *command, /* I - Command name */
763 int job_id, /* I - Job ID */
764 int num_options,/* I - Number of options */
765 cups_option_t *options) /* I - Options */
766 {
767 http_t *http; /* HTTP connection to server */
768 ipp_t *request; /* IPP request */
769 char uri[HTTP_MAX_URI]; /* URI for job */
770
771
772 if (num_options == 0)
773 return (0);
774
775 http = httpConnectEncrypt(cupsServer(), ippPort(), cupsEncryption());
776
777 request = ippNewRequest(IPP_SET_JOB_ATTRIBUTES);
778
779 sprintf(uri, "ipp://localhost/jobs/%d", job_id);
780
781 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
782 "job-uri", NULL, uri);
783
784 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
785 "requesting-user-name", NULL, cupsUser());
786
787 cupsEncodeOptions(request, num_options, options);
788
789 ippDelete(cupsDoRequest(http, request, "/jobs"));
790
791 if (cupsLastError() > IPP_OK_CONFLICT)
792 {
793 _cupsLangPrintf(stderr, "%s: %s\n", command, cupsLastErrorString());
794 return (1);
795 }
796
797 return (0);
798 }
799
800
801 #ifndef WIN32
802 /*
803 * 'sighandler()' - Signal catcher for when we print from stdin...
804 */
805
806 void
807 sighandler(int s) /* I - Signal number */
808 {
809 /*
810 * Remove the temporary file we're using to print from stdin...
811 */
812
813 unlink(tempfile);
814
815 /*
816 * Exit...
817 */
818
819 exit(s);
820 }
821 #endif /* !WIN32 */
822
823
824 /*
825 * End of "$Id: lp.c 6649 2007-07-11 21:46:42Z mike $".
826 */