1 /*
2 * "lpstat" command for CUPS.
3 *
4 * Copyright © 2020-2024 by OpenPrinting.
5 * Copyright © 2007-2018 by Apple Inc.
6 * Copyright © 1997-2006 by Easy Software Products.
7 *
8 * Licensed under Apache License v2.0. See the file "LICENSE" for more
9 * information.
10 */
11
12 /*
13 * Include necessary headers...
14 */
15
16 #include <cups/cups-private.h>
17
18
19 /*
20 * Local functions...
21 */
22
23 static void check_dest(const char *command, const char *name,
24 int *num_dests, cups_dest_t **dests);
25 static int match_list(const char *list, const char *name);
26 static int show_accepting(const char *printers, int num_dests,
27 cups_dest_t *dests);
28 static int show_classes(const char *dests);
29 static void show_default(cups_dest_t *dest);
30 static int show_devices(const char *printers, int num_dests,
31 cups_dest_t *dests);
32 static int show_jobs(const char *dests, const char *users, int long_status,
33 int ranking, const char *which);
34 static int show_printers(const char *printers, int num_dests,
35 cups_dest_t *dests, int long_status);
36 static int show_scheduler(void);
37 static void usage(void) _CUPS_NORETURN;
38
39
40 /*
41 * 'main()' - Parse options and show status information.
42 */
43
44 int
main(int argc,char * argv[])45 main(int argc, /* I - Number of command-line arguments */
46 char *argv[]) /* I - Command-line arguments */
47 {
48 int i, /* Looping var */
49 status; /* Exit status */
50 char *opt; /* Option pointer */
51 int num_dests; /* Number of user destinations */
52 cups_dest_t *dests; /* User destinations */
53 int long_status; /* Long status report? */
54 int ranking; /* Show job ranking? */
55 const char *which; /* Which jobs to show? */
56 char op; /* Last operation on command-line */
57
58
59 _cupsSetLocale(argv);
60
61 /*
62 * Parse command-line options...
63 */
64
65 num_dests = 0;
66 dests = NULL;
67 long_status = 0;
68 ranking = 0;
69 status = 0;
70 which = "not-completed";
71 op = 0;
72
73 for (i = 1; i < argc; i ++)
74 {
75 if (!strcmp(argv[i], "--help"))
76 usage();
77 else if (argv[i][0] == '-')
78 {
79 for (opt = argv[i] + 1; *opt; opt ++)
80 {
81 switch (*opt)
82 {
83 case 'D' : /* Show description */
84 long_status = 1;
85 break;
86
87 case 'E' : /* Encrypt */
88 #ifdef HAVE_TLS
89 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
90 #else
91 _cupsLangPrintf(stderr,
92 _("%s: Sorry, no encryption support."),
93 argv[0]);
94 #endif /* HAVE_TLS */
95 break;
96
97 case 'H' : /* Show server and port */
98 if (cupsServer()[0] == '/')
99 _cupsLangPuts(stdout, cupsServer());
100 else
101 _cupsLangPrintf(stdout, "%s:%d", cupsServer(), ippPort());
102 op = 'H';
103 break;
104
105 case 'P' : /* Show paper types */
106 op = 'P';
107 break;
108
109 case 'R' : /* Show ranking */
110 ranking = 1;
111 break;
112
113 case 'S' : /* Show charsets */
114 op = 'S';
115 if (!argv[i][2])
116 i ++;
117 break;
118
119 case 'U' : /* Username */
120 if (opt[1] != '\0')
121 {
122 cupsSetUser(opt + 1);
123 opt += strlen(opt) - 1;
124 }
125 else
126 {
127 i ++;
128 if (i >= argc)
129 {
130 _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
131 usage();
132 }
133
134 cupsSetUser(argv[i]);
135 }
136 break;
137
138 case 'W' : /* Show which jobs? */
139 if (opt[1] != '\0')
140 {
141 which = opt + 1;
142 opt += strlen(opt) - 1;
143 }
144 else
145 {
146 i ++;
147
148 if (i >= argc)
149 {
150 _cupsLangPrintf(stderr, _("%s: Error - need \"completed\", \"not-completed\", \"successful\", or \"all\" after \"-W\" option."), argv[0]);
151 usage();
152 }
153
154 which = argv[i];
155 }
156
157 if (strcmp(which, "completed") && strcmp(which, "not-completed") && strcmp(which, "all") && strcmp(which, "successful"))
158 {
159 _cupsLangPrintf(stderr, _("%s: Error - need \"completed\", \"not-completed\", \"successful\", or \"all\" after \"-W\" option."), argv[0]);
160 usage();
161 }
162 break;
163
164 case 'a' : /* Show acceptance status */
165 op = 'a';
166
167 if (opt[1] != '\0')
168 {
169 check_dest(argv[0], opt + 1, &num_dests, &dests);
170
171 status |= show_accepting(opt + 1, num_dests, dests);
172 opt += strlen(opt) - 1;
173 }
174 else if ((i + 1) < argc && argv[i + 1][0] != '-')
175 {
176 i ++;
177
178 check_dest(argv[0], argv[i], &num_dests, &dests);
179
180 status |= show_accepting(argv[i], num_dests, dests);
181 }
182 else
183 {
184 if (num_dests <= 1)
185 {
186 cupsFreeDests(num_dests, dests);
187 num_dests = cupsGetDests(&dests);
188
189 if (num_dests == 0 && (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST || cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
190 {
191 _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
192 return (1);
193 }
194 }
195
196 status |= show_accepting(NULL, num_dests, dests);
197 }
198 break;
199
200 case 'c' : /* Show classes and members */
201 op = 'c';
202
203 if (opt[1] != '\0')
204 {
205 check_dest(argv[0], opt + 1, &num_dests, &dests);
206
207 status |= show_classes(opt + 1);
208 opt += strlen(opt) - 1;
209 }
210 else if ((i + 1) < argc && argv[i + 1][0] != '-')
211 {
212 i ++;
213
214 check_dest(argv[0], argv[i], &num_dests, &dests);
215
216 status |= show_classes(argv[i]);
217 }
218 else
219 status |= show_classes(NULL);
220 break;
221
222 case 'd' : /* Show default destination */
223 op = 'd';
224
225 if (num_dests != 1 || !dests[0].is_default)
226 {
227 cupsFreeDests(num_dests, dests);
228
229 dests = cupsGetNamedDest(CUPS_HTTP_DEFAULT, NULL, NULL);
230 num_dests = dests ? 1 : 0;
231
232 if (num_dests == 0 &&
233 (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
234 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
235 {
236 _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
237 return (1);
238 }
239 }
240
241 show_default(dests);
242 break;
243
244 case 'e' : /* List destinations */
245 {
246 cups_dest_t *temp = NULL, *dest;
247 int j, num_temp = cupsGetDests(&temp);
248
249 op = 'e';
250
251 for (j = num_temp, dest = temp; j > 0; j --, dest ++)
252 {
253 if (dest->instance)
254 printf("%s/%s", dest->name, dest->instance);
255 else
256 fputs(dest->name, stdout);
257
258 if (long_status)
259 {
260 const char *printer_uri_supported = cupsGetOption("printer-uri-supported", dest->num_options, dest->options);
261 const char *printer_is_temporary = cupsGetOption("printer-is-temporary", dest->num_options, dest->options);
262 const char *type = "network";
263
264 if (printer_is_temporary && !strcmp(printer_is_temporary, "true"))
265 type = "temporary";
266 else if (printer_uri_supported)
267 type = "permanent";
268
269 printf(" %s %s %s\n", type, printer_uri_supported ? printer_uri_supported : "none", cupsGetOption("device-uri", dest->num_options, dest->options));
270 }
271 else
272 putchar('\n');
273 }
274
275 cupsFreeDests(num_temp, temp);
276 }
277 break;
278
279 case 'f' : /* Show forms */
280 op = 'f';
281 if (opt[1] != '\0')
282 {
283 opt += strlen(opt) - 1;
284 }
285 else
286 {
287 i ++;
288 if (i >= argc)
289 return (1);
290 }
291 break;
292
293 case 'h' : /* Connect to host */
294 if (opt[1] != '\0')
295 {
296 cupsSetServer(opt + 1);
297 opt += strlen(opt) - 1;
298 }
299 else
300 {
301 i ++;
302
303 if (i >= argc)
304 {
305 _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-h\" option."), argv[0]);
306 return (1);
307 }
308
309 cupsSetServer(argv[i]);
310 }
311 break;
312
313 case 'l' : /* Long status or long job status */
314 long_status = 2;
315 break;
316
317 case 'o' : /* Show jobs by destination */
318 op = 'o';
319
320 if (opt[1])
321 {
322 check_dest(argv[0], opt + 1, &num_dests, &dests);
323
324 status |= show_jobs(opt + 1, NULL, long_status, ranking, which);
325 opt += strlen(opt) - 1;
326 }
327 else if ((i + 1) < argc && argv[i + 1][0] != '-')
328 {
329 i ++;
330
331 check_dest(argv[0], argv[i], &num_dests, &dests);
332
333 status |= show_jobs(argv[i], NULL, long_status, ranking, which);
334 }
335 else
336 status |= show_jobs(NULL, NULL, long_status, ranking, which);
337 break;
338
339 case 'p' : /* Show printers */
340 op = 'p';
341
342 if (opt[1] != '\0')
343 {
344 check_dest(argv[0], opt + 1, &num_dests, &dests);
345
346 status |= show_printers(opt + 1, num_dests, dests,
347 long_status);
348 opt += strlen(opt) - 1;
349 }
350 else if ((i + 1) < argc && argv[i + 1][0] != '-')
351 {
352 i ++;
353
354 check_dest(argv[0], argv[i], &num_dests, &dests);
355
356 status |= show_printers(argv[i], num_dests, dests, long_status);
357 }
358 else
359 {
360 if (num_dests <= 1)
361 {
362 cupsFreeDests(num_dests, dests);
363 num_dests = cupsGetDests(&dests);
364
365 if (num_dests == 0 &&
366 (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
367 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
368 {
369 _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
370 return (1);
371 }
372 }
373
374 status |= show_printers(NULL, num_dests, dests, long_status);
375 }
376 break;
377
378 case 'r' : /* Show scheduler status */
379 op = 'r';
380
381 if (!show_scheduler())
382 return (0);
383 break;
384
385 case 's' : /* Show summary */
386 op = 's';
387
388 if (num_dests <= 1)
389 {
390 cupsFreeDests(num_dests, dests);
391 num_dests = cupsGetDests(&dests);
392
393 if (num_dests == 0 &&
394 (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
395 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
396 {
397 _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
398 return (1);
399 }
400 }
401
402 show_default(cupsGetDest(NULL, NULL, num_dests, dests));
403 status |= show_classes(NULL);
404 status |= show_devices(NULL, num_dests, dests);
405 break;
406
407 case 't' : /* Show all info */
408 op = 't';
409
410 if (num_dests <= 1)
411 {
412 cupsFreeDests(num_dests, dests);
413 num_dests = cupsGetDests(&dests);
414
415 if (num_dests == 0 &&
416 (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
417 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
418 {
419 _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
420 return (1);
421 }
422 }
423
424 if (!show_scheduler())
425 return (0);
426
427 show_default(cupsGetDest(NULL, NULL, num_dests, dests));
428 status |= show_classes(NULL);
429 status |= show_devices(NULL, num_dests, dests);
430 status |= show_accepting(NULL, num_dests, dests);
431 status |= show_printers(NULL, num_dests, dests, long_status);
432 status |= show_jobs(NULL, NULL, long_status, ranking, which);
433 break;
434
435 case 'u' : /* Show jobs by user */
436 op = 'u';
437
438 if (opt[1] != '\0')
439 {
440 status |= show_jobs(NULL, opt + 1, long_status, ranking, which);
441 opt += strlen(opt) - 1;
442 }
443 else if ((i + 1) < argc && argv[i + 1][0] != '-')
444 {
445 i ++;
446 status |= show_jobs(NULL, argv[i], long_status, ranking, which);
447 }
448 else
449 status |= show_jobs(NULL, NULL, long_status, ranking, which);
450 break;
451
452 case 'v' : /* Show printer devices */
453 op = 'v';
454
455 if (opt[1] != '\0')
456 {
457 check_dest(argv[0], opt + 1, &num_dests, &dests);
458
459 status |= show_devices(opt + 1, num_dests, dests);
460 opt += strlen(opt) - 1;
461 }
462 else if ((i + 1) < argc && argv[i + 1][0] != '-')
463 {
464 i ++;
465
466 check_dest(argv[0], argv[i], &num_dests, &dests);
467
468 status |= show_devices(argv[i], num_dests, dests);
469 }
470 else
471 {
472 if (num_dests <= 1)
473 {
474 cupsFreeDests(num_dests, dests);
475 num_dests = cupsGetDests(&dests);
476
477 if (num_dests == 0 &&
478 (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
479 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED))
480 {
481 _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
482 return (1);
483 }
484 }
485
486 status |= show_devices(NULL, num_dests, dests);
487 }
488 break;
489
490 default :
491 _cupsLangPrintf(stderr, _("%s: Error - unknown option \"%c\"."), argv[0], argv[i][1]);
492 usage();
493 }
494 }
495 }
496 else
497 {
498 status |= show_jobs(argv[i], NULL, long_status, ranking, which);
499 op = 'o';
500 }
501 }
502
503 if (!op)
504 status |= show_jobs(NULL, cupsUser(), long_status, ranking, which);
505
506 return (status);
507 }
508
509
510 /*
511 * 'check_dest()' - Verify that the named destination(s) exists.
512 */
513
514 static void
check_dest(const char * command,const char * name,int * num_dests,cups_dest_t ** dests)515 check_dest(const char *command, /* I - Command name */
516 const char *name, /* I - List of printer/class names */
517 int *num_dests, /* IO - Number of destinations */
518 cups_dest_t **dests) /* IO - Destinations */
519 {
520 const char *dptr; /* Pointer into name */
521 char *pptr, /* Pointer into printer */
522 printer[1024]; /* Current printer/class name */
523
524
525 /*
526 * Load the destination list as necessary...
527 */
528
529 if (*num_dests <= 1)
530 {
531 if (*num_dests)
532 cupsFreeDests(*num_dests, *dests);
533
534 if (strchr(name, ','))
535 *num_dests = cupsGetDests(dests);
536 else
537 {
538 strlcpy(printer, name, sizeof(printer));
539 if ((pptr = strchr(printer, '/')) != NULL)
540 *pptr++ = '\0';
541
542 if ((*dests = cupsGetNamedDest(CUPS_HTTP_DEFAULT, printer, pptr)) == NULL)
543 {
544 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
545 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
546 _cupsLangPrintf(stderr,
547 _("%s: Error - add '/version=1.1' to server name."),
548 command);
549 else
550 _cupsLangPrintf(stderr,
551 _("%s: Invalid destination name in list \"%s\"."),
552 command, name);
553
554 exit(1);
555 }
556 else
557 {
558 *num_dests = 1;
559 return;
560 }
561 }
562 }
563
564 /*
565 * Scan the name string for printer/class name(s)...
566 */
567
568 for (dptr = name; *dptr;)
569 {
570 /*
571 * Skip leading whitespace and commas...
572 */
573
574 while (isspace(*dptr & 255) || *dptr == ',')
575 dptr ++;
576
577 if (!*dptr)
578 break;
579
580 /*
581 * Extract a single destination name from the name string...
582 */
583
584 for (pptr = printer; !isspace(*dptr & 255) && *dptr != ',' && *dptr;)
585 {
586 if ((size_t)(pptr - printer) < (sizeof(printer) - 1))
587 *pptr++ = *dptr++;
588 else
589 {
590 _cupsLangPrintf(stderr,
591 _("%s: Invalid destination name in list \"%s\"."),
592 command, name);
593 exit(1);
594 }
595 }
596
597 *pptr = '\0';
598
599 /*
600 * Check the destination...
601 */
602
603 if (!cupsGetDest(printer, NULL, *num_dests, *dests))
604 {
605 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
606 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
607 _cupsLangPrintf(stderr,
608 _("%s: Error - add '/version=1.1' to server name."),
609 command);
610 else
611 _cupsLangPrintf(stderr,
612 _("%s: Unknown destination \"%s\"."), command, printer);
613
614 exit(1);
615 }
616 }
617 }
618
619
620 /*
621 * 'match_list()' - Match a name from a list of comma or space-separated names.
622 */
623
624 static int /* O - 1 on match, 0 on no match */
match_list(const char * list,const char * name)625 match_list(const char *list, /* I - List of names */
626 const char *name) /* I - Name to find */
627 {
628 const char *nameptr; /* Pointer into name */
629
630
631 /*
632 * An empty list always matches...
633 */
634
635 if (!list || !*list)
636 return (1);
637
638 if (!name)
639 return (0);
640
641 do
642 {
643 /*
644 * Skip leading whitespace and commas...
645 */
646
647 while (isspace(*list & 255) || *list == ',')
648 list ++;
649
650 if (!*list)
651 break;
652
653 /*
654 * Compare names...
655 */
656
657 for (nameptr = name;
658 *nameptr && *list && tolower(*nameptr & 255) == tolower(*list & 255);
659 nameptr ++, list ++);
660
661 if (!*nameptr && (!*list || *list == ',' || isspace(*list & 255)))
662 return (1);
663
664 while (*list && !isspace(*list & 255) && *list != ',')
665 list ++;
666 }
667 while (*list);
668
669 return (0);
670 }
671
672
673 /*
674 * 'show_accepting()' - Show acceptance status.
675 */
676
677 static int /* O - 0 on success, 1 on fail */
show_accepting(const char * printers,int num_dests,cups_dest_t * dests)678 show_accepting(const char *printers, /* I - Destinations */
679 int num_dests, /* I - Number of user-defined dests */
680 cups_dest_t *dests) /* I - User-defined destinations */
681 {
682 int i; /* Looping var */
683 ipp_t *request, /* IPP Request */
684 *response; /* IPP Response */
685 ipp_attribute_t *attr; /* Current attribute */
686 const char *printer, /* Printer name */
687 *message; /* Printer device URI */
688 int accepting; /* Accepting requests? */
689 time_t ptime; /* Printer state time */
690 char printer_state_time[255];/* Printer state time */
691 static const char *pattrs[] = /* Attributes we need for printers... */
692 {
693 "printer-name",
694 "printer-state-change-time",
695 "printer-state-message",
696 "printer-is-accepting-jobs"
697 };
698
699
700 if (printers != NULL && !strcmp(printers, "all"))
701 printers = NULL;
702
703 /*
704 * Build a CUPS_GET_PRINTERS request, which requires the following
705 * attributes:
706 *
707 * attributes-charset
708 * attributes-natural-language
709 * requested-attributes
710 * requesting-user-name
711 */
712
713 request = ippNewRequest(CUPS_GET_PRINTERS);
714
715 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
716 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
717 NULL, pattrs);
718
719 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
720 NULL, cupsUser());
721
722 /*
723 * Do the request and get back a response...
724 */
725
726 response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
727
728 if (cupsLastError() == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE)
729 {
730 _cupsLangPrintf(stderr, _("%s: Scheduler is not running."), "lpstat");
731 ippDelete(response);
732 return (1);
733 }
734 else if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST || cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
735 {
736 _cupsLangPrintf(stderr,
737 _("%s: Error - add '/version=1.1' to server name."),
738 "lpstat");
739 ippDelete(response);
740 return (1);
741 }
742 else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
743 {
744 _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
745 ippDelete(response);
746 return (1);
747 }
748
749 if (response)
750 {
751 /*
752 * Loop through the printers returned in the list and display
753 * their devices...
754 */
755
756 for (attr = response->attrs; attr != NULL; attr = attr->next)
757 {
758 /*
759 * Skip leading attributes until we hit a printer...
760 */
761
762 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
763 attr = attr->next;
764
765 if (attr == NULL)
766 break;
767
768 /*
769 * Pull the needed attributes from this printer...
770 */
771
772 printer = NULL;
773 message = NULL;
774 accepting = 1;
775 ptime = 0;
776
777 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
778 {
779 if (!strcmp(attr->name, "printer-name") &&
780 attr->value_tag == IPP_TAG_NAME)
781 printer = attr->values[0].string.text;
782 else if (!strcmp(attr->name, "printer-state-change-time") &&
783 attr->value_tag == IPP_TAG_INTEGER)
784 ptime = (time_t)attr->values[0].integer;
785 else if (!strcmp(attr->name, "printer-state-message") &&
786 attr->value_tag == IPP_TAG_TEXT)
787 message = attr->values[0].string.text;
788 else if (!strcmp(attr->name, "printer-is-accepting-jobs") &&
789 attr->value_tag == IPP_TAG_BOOLEAN)
790 accepting = attr->values[0].boolean;
791
792 attr = attr->next;
793 }
794
795 /*
796 * See if we have everything needed...
797 */
798
799 if (printer == NULL)
800 {
801 if (attr == NULL)
802 break;
803 else
804 continue;
805 }
806
807 /*
808 * Display the printer entry if needed...
809 */
810
811 if (match_list(printers, printer))
812 {
813 _cupsStrDate(printer_state_time, sizeof(printer_state_time), ptime);
814
815 if (accepting)
816 _cupsLangPrintf(stdout, _("%s accepting requests since %s"),
817 printer, printer_state_time);
818 else
819 {
820 _cupsLangPrintf(stdout, _("%s not accepting requests since %s -"),
821 printer, printer_state_time);
822 _cupsLangPrintf(stdout, _("\t%s"),
823 (message && *message) ?
824 message : "reason unknown");
825 }
826
827 for (i = 0; i < num_dests; i ++)
828 if (!_cups_strcasecmp(dests[i].name, printer) && dests[i].instance)
829 {
830 if (accepting)
831 _cupsLangPrintf(stdout, _("%s/%s accepting requests since %s"),
832 printer, dests[i].instance, printer_state_time);
833 else
834 {
835 _cupsLangPrintf(stdout,
836 _("%s/%s not accepting requests since %s -"),
837 printer, dests[i].instance, printer_state_time);
838 _cupsLangPrintf(stdout, _("\t%s"),
839 (message && *message) ?
840 message : "reason unknown");
841 }
842 }
843 }
844
845 if (attr == NULL)
846 break;
847 }
848
849 ippDelete(response);
850 }
851
852 return (0);
853 }
854
855
856 /*
857 * 'show_classes()' - Show printer classes.
858 */
859
860 static int /* O - 0 on success, 1 on fail */
show_classes(const char * dests)861 show_classes(const char *dests) /* I - Destinations */
862 {
863 int i; /* Looping var */
864 ipp_t *request, /* IPP Request */
865 *response, /* IPP Response */
866 *response2; /* IPP response from remote server */
867 http_t *http2; /* Remote server */
868 ipp_attribute_t *attr; /* Current attribute */
869 const char *printer, /* Printer class name */
870 *printer_uri; /* Printer class URI */
871 ipp_attribute_t *members; /* Printer members */
872 char method[HTTP_MAX_URI], /* Request method */
873 username[HTTP_MAX_URI], /* Username:password */
874 server[HTTP_MAX_URI], /* Server name */
875 resource[HTTP_MAX_URI]; /* Resource name */
876 int port; /* Port number */
877 static const char *cattrs[] = /* Attributes we need for classes... */
878 {
879 "printer-name",
880 "printer-uri-supported",
881 "member-names"
882 };
883
884
885 if (dests != NULL && !strcmp(dests, "all"))
886 dests = NULL;
887
888 /*
889 * Build a CUPS_GET_CLASSES request, which requires the following
890 * attributes:
891 *
892 * attributes-charset
893 * attributes-natural-language
894 * requested-attributes
895 * requesting-user-name
896 */
897
898 request = ippNewRequest(CUPS_GET_CLASSES);
899
900 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
901 "requested-attributes", sizeof(cattrs) / sizeof(cattrs[0]),
902 NULL, cattrs);
903
904 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
905 NULL, cupsUser());
906
907 /*
908 * Do the request and get back a response...
909 */
910
911 response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
912
913 if (cupsLastError() == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE)
914 {
915 _cupsLangPrintf(stderr, _("%s: Scheduler is not running."), "lpstat");
916 ippDelete(response);
917 return (1);
918 }
919 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
920 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
921 {
922 _cupsLangPrintf(stderr,
923 _("%s: Error - add '/version=1.1' to server name."),
924 "lpstat");
925 ippDelete(response);
926 return (1);
927 }
928 else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
929 {
930 _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
931 ippDelete(response);
932 return (1);
933 }
934
935 if (response)
936 {
937 if (response->request.status.status_code > IPP_OK_CONFLICT)
938 {
939 _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
940 ippDelete(response);
941 return (1);
942 }
943
944 /*
945 * Loop through the printers returned in the list and display
946 * their devices...
947 */
948
949 for (attr = response->attrs; attr != NULL; attr = attr->next)
950 {
951 /*
952 * Skip leading attributes until we hit a job...
953 */
954
955 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
956 attr = attr->next;
957
958 if (attr == NULL)
959 break;
960
961 /*
962 * Pull the needed attributes from this job...
963 */
964
965 printer = NULL;
966 printer_uri = NULL;
967 members = NULL;
968
969 do
970 {
971 if (!strcmp(attr->name, "printer-name") &&
972 attr->value_tag == IPP_TAG_NAME)
973 printer = attr->values[0].string.text;
974
975 if (!strcmp(attr->name, "printer-uri-supported") &&
976 attr->value_tag == IPP_TAG_URI)
977 printer_uri = attr->values[0].string.text;
978
979 if (!strcmp(attr->name, "member-names") &&
980 attr->value_tag == IPP_TAG_NAME)
981 members = attr;
982
983 attr = attr->next;
984 }
985 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER);
986
987 /*
988 * If this is a remote class, grab the class info from the
989 * remote server...
990 */
991
992 response2 = NULL;
993 if (members == NULL && printer_uri != NULL)
994 {
995 httpSeparateURI(HTTP_URI_CODING_ALL, printer_uri, method, sizeof(method),
996 username, sizeof(username), server, sizeof(server),
997 &port, resource, sizeof(resource));
998
999 if (!_cups_strcasecmp(server, cupsServer()))
1000 http2 = CUPS_HTTP_DEFAULT;
1001 else
1002 http2 = httpConnectEncrypt(server, port, cupsEncryption());
1003
1004 /*
1005 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the
1006 * following attributes:
1007 *
1008 * attributes-charset
1009 * attributes-natural-language
1010 * printer-uri
1011 * requested-attributes
1012 */
1013
1014 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
1015
1016 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1017 "printer-uri", NULL, printer_uri);
1018
1019 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1020 "requested-attributes",
1021 sizeof(cattrs) / sizeof(cattrs[0]),
1022 NULL, cattrs);
1023
1024 if ((response2 = cupsDoRequest(http2, request, "/")) != NULL)
1025 members = ippFindAttribute(response2, "member-names", IPP_TAG_NAME);
1026
1027 if (http2)
1028 httpClose(http2);
1029 }
1030
1031 /*
1032 * See if we have everything needed...
1033 */
1034
1035 if (printer == NULL)
1036 {
1037 if (response2)
1038 ippDelete(response2);
1039
1040 if (attr == NULL)
1041 break;
1042 else
1043 continue;
1044 }
1045
1046 /*
1047 * Display the printer entry if needed...
1048 */
1049
1050 if (match_list(dests, printer))
1051 {
1052 _cupsLangPrintf(stdout, _("members of class %s:"), printer);
1053
1054 if (members)
1055 {
1056 for (i = 0; i < members->num_values; i ++)
1057 _cupsLangPrintf(stdout, "\t%s", members->values[i].string.text);
1058 }
1059 else
1060 _cupsLangPuts(stdout, "\tunknown");
1061 }
1062
1063 if (response2)
1064 ippDelete(response2);
1065
1066 if (attr == NULL)
1067 break;
1068 }
1069
1070 ippDelete(response);
1071 }
1072
1073 return (0);
1074 }
1075
1076
1077 /*
1078 * 'show_default()' - Show default destination.
1079 */
1080
1081 static void
show_default(cups_dest_t * dest)1082 show_default(cups_dest_t *dest) /* I - Default destination */
1083 {
1084 const char *printer, /* Printer name */
1085 *val; /* Environment variable name */
1086
1087
1088 if (dest)
1089 {
1090 if (dest->instance)
1091 _cupsLangPrintf(stdout, _("system default destination: %s/%s"),
1092 dest->name, dest->instance);
1093 else
1094 _cupsLangPrintf(stdout, _("system default destination: %s"),
1095 dest->name);
1096 }
1097 else
1098 {
1099 val = NULL;
1100
1101 if ((printer = getenv("LPDEST")) == NULL)
1102 {
1103 if ((printer = getenv("PRINTER")) != NULL)
1104 {
1105 if (!strcmp(printer, "lp"))
1106 printer = NULL;
1107 else
1108 val = "PRINTER";
1109 }
1110 }
1111 else
1112 val = "LPDEST";
1113
1114 if (printer)
1115 _cupsLangPrintf(stdout,
1116 _("lpstat: error - %s environment variable names "
1117 "non-existent destination \"%s\"."),
1118 val, printer);
1119 else
1120 _cupsLangPuts(stdout, _("no system default destination"));
1121 }
1122 }
1123
1124
1125 /*
1126 * 'show_devices()' - Show printer devices.
1127 */
1128
1129 static int /* O - 0 on success, 1 on fail */
show_devices(const char * printers,int num_dests,cups_dest_t * dests)1130 show_devices(const char *printers, /* I - Destinations */
1131 int num_dests, /* I - Number of user-defined dests */
1132 cups_dest_t *dests) /* I - User-defined destinations */
1133 {
1134 int i; /* Looping var */
1135 ipp_t *request, /* IPP Request */
1136 *response; /* IPP Response */
1137 ipp_attribute_t *attr; /* Current attribute */
1138 const char *printer, /* Printer name */
1139 *uri, /* Printer URI */
1140 *device; /* Printer device URI */
1141 static const char *pattrs[] = /* Attributes we need for printers... */
1142 {
1143 "printer-name",
1144 "printer-uri-supported",
1145 "device-uri"
1146 };
1147
1148
1149 if (printers != NULL && !strcmp(printers, "all"))
1150 printers = NULL;
1151
1152 /*
1153 * Build a CUPS_GET_PRINTERS request, which requires the following
1154 * attributes:
1155 *
1156 * attributes-charset
1157 * attributes-natural-language
1158 * requested-attributes
1159 * requesting-user-name
1160 */
1161
1162 request = ippNewRequest(CUPS_GET_PRINTERS);
1163
1164 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1165 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1166 NULL, pattrs);
1167
1168 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1169 NULL, cupsUser());
1170
1171 /*
1172 * Do the request and get back a response...
1173 */
1174
1175 response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
1176
1177 if (cupsLastError() == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE)
1178 {
1179 _cupsLangPrintf(stderr, _("%s: Scheduler is not running."), "lpstat");
1180 ippDelete(response);
1181 return (1);
1182 }
1183 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
1184 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
1185 {
1186 _cupsLangPrintf(stderr,
1187 _("%s: Error - add '/version=1.1' to server name."),
1188 "lpstat");
1189 ippDelete(response);
1190 return (1);
1191 }
1192 else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1193 {
1194 _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
1195 ippDelete(response);
1196 return (1);
1197 }
1198
1199 if (response)
1200 {
1201 /*
1202 * Loop through the printers returned in the list and display
1203 * their devices...
1204 */
1205
1206 for (attr = response->attrs; attr != NULL; attr = attr->next)
1207 {
1208 /*
1209 * Skip leading attributes until we hit a job...
1210 */
1211
1212 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1213 attr = attr->next;
1214
1215 if (attr == NULL)
1216 break;
1217
1218 /*
1219 * Pull the needed attributes from this job...
1220 */
1221
1222 printer = NULL;
1223 device = NULL;
1224 uri = NULL;
1225
1226 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
1227 {
1228 if (!strcmp(attr->name, "printer-name") &&
1229 attr->value_tag == IPP_TAG_NAME)
1230 printer = attr->values[0].string.text;
1231
1232 if (!strcmp(attr->name, "printer-uri-supported") &&
1233 attr->value_tag == IPP_TAG_URI)
1234 uri = attr->values[0].string.text;
1235
1236 if (!strcmp(attr->name, "device-uri") &&
1237 attr->value_tag == IPP_TAG_URI)
1238 device = attr->values[0].string.text;
1239
1240 attr = attr->next;
1241 }
1242
1243 /*
1244 * See if we have everything needed...
1245 */
1246
1247 if (printer == NULL)
1248 {
1249 if (attr == NULL)
1250 break;
1251 else
1252 continue;
1253 }
1254
1255 /*
1256 * Display the printer entry if needed...
1257 */
1258
1259 if (match_list(printers, printer))
1260 {
1261 if (device == NULL)
1262 _cupsLangPrintf(stdout, _("device for %s: %s"),
1263 printer, uri);
1264 else if (!strncmp(device, "file:", 5))
1265 _cupsLangPrintf(stdout, _("device for %s: %s"),
1266 printer, device + 5);
1267 else
1268 _cupsLangPrintf(stdout, _("device for %s: %s"),
1269 printer, device);
1270
1271 for (i = 0; i < num_dests; i ++)
1272 {
1273 if (!_cups_strcasecmp(printer, dests[i].name) && dests[i].instance)
1274 {
1275 if (device == NULL)
1276 _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
1277 printer, dests[i].instance, uri);
1278 else if (!strncmp(device, "file:", 5))
1279 _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
1280 printer, dests[i].instance, device + 5);
1281 else
1282 _cupsLangPrintf(stdout, _("device for %s/%s: %s"),
1283 printer, dests[i].instance, device);
1284 }
1285 }
1286 }
1287
1288 if (attr == NULL)
1289 break;
1290 }
1291
1292 ippDelete(response);
1293 }
1294
1295 return (0);
1296 }
1297
1298
1299 /*
1300 * 'show_jobs()' - Show active print jobs.
1301 */
1302
1303 static int /* O - 0 on success, 1 on fail */
show_jobs(const char * dests,const char * users,int long_status,int ranking,const char * which)1304 show_jobs(const char *dests, /* I - Destinations */
1305 const char *users, /* I - Users */
1306 int long_status, /* I - Show long status? */
1307 int ranking, /* I - Show job ranking? */
1308 const char *which) /* I - Show which jobs? */
1309 {
1310 int i; /* Looping var */
1311 ipp_t *request, /* IPP Request */
1312 *response; /* IPP Response */
1313 ipp_attribute_t *attr, /* Current attribute */
1314 *reasons; /* Job state reasons attribute */
1315 const char *dest, /* Pointer into job-printer-uri */
1316 *username, /* Pointer to job-originating-user-name */
1317 *message, /* Pointer to job-printer-state-message */
1318 *time_at; /* time-at-xxx attribute name to use */
1319 int rank, /* Rank in queue */
1320 jobid, /* job-id */
1321 size; /* job-k-octets */
1322 time_t jobtime; /* time-at-creation */
1323 char temp[255], /* Temporary buffer */
1324 date[255]; /* Date buffer */
1325 static const char *jattrs[] = /* Attributes we need for jobs... */
1326 {
1327 "job-id",
1328 "job-k-octets",
1329 "job-name",
1330 "job-originating-user-name",
1331 "job-printer-state-message",
1332 "job-printer-uri",
1333 "job-state-reasons",
1334 "time-at-creation",
1335 "time-at-completed"
1336 };
1337
1338
1339 if (dests != NULL && !strcmp(dests, "all"))
1340 dests = NULL;
1341
1342 /*
1343 * Build a IPP_GET_JOBS request, which requires the following
1344 * attributes:
1345 *
1346 * attributes-charset
1347 * attributes-natural-language
1348 * printer-uri
1349 * requested-attributes
1350 * requesting-user-name
1351 * which-jobs
1352 */
1353
1354 request = ippNewRequest(IPP_GET_JOBS);
1355
1356 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
1357 NULL, "ipp://localhost/");
1358
1359 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1360 "requested-attributes", sizeof(jattrs) / sizeof(jattrs[0]),
1361 NULL, jattrs);
1362
1363 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1364 NULL, cupsUser());
1365
1366 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD, "which-jobs",
1367 NULL, !strcmp(which, "successful") ? "completed" : which);
1368
1369 /*
1370 * Do the request and get back a response...
1371 */
1372
1373 response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
1374
1375 if (cupsLastError() == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE)
1376 {
1377 _cupsLangPrintf(stderr, _("%s: Scheduler is not running."), "lpstat");
1378 ippDelete(response);
1379 return (1);
1380 }
1381 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
1382 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
1383 {
1384 _cupsLangPrintf(stderr,
1385 _("%s: Error - add '/version=1.1' to server name."),
1386 "lpstat");
1387 ippDelete(response);
1388 return (1);
1389 }
1390 else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1391 {
1392 _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
1393 ippDelete(response);
1394 return (1);
1395 }
1396
1397 if (response)
1398 {
1399 /*
1400 * Loop through the job list and display them...
1401 */
1402
1403 if (!strcmp(which, "aborted") ||
1404 !strcmp(which, "canceled") ||
1405 !strcmp(which, "successful") ||
1406 !strcmp(which, "completed"))
1407 time_at = "time-at-completed";
1408 else
1409 time_at = "time-at-creation";
1410
1411 rank = -1;
1412
1413 for (attr = response->attrs; attr != NULL; attr = attr->next)
1414 {
1415 /*
1416 * Skip leading attributes until we hit a job...
1417 */
1418
1419 while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
1420 attr = attr->next;
1421
1422 if (attr == NULL)
1423 break;
1424
1425 /*
1426 * Pull the needed attributes from this job...
1427 */
1428
1429 jobid = 0;
1430 size = 0;
1431 username = NULL;
1432 dest = NULL;
1433 jobtime = 0;
1434 message = NULL;
1435 reasons = NULL;
1436
1437 while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
1438 {
1439 if (!strcmp(attr->name, "job-id") &&
1440 attr->value_tag == IPP_TAG_INTEGER)
1441 jobid = attr->values[0].integer;
1442 else if (!strcmp(attr->name, "job-k-octets") &&
1443 attr->value_tag == IPP_TAG_INTEGER)
1444 size = attr->values[0].integer;
1445 else if (!strcmp(attr->name, time_at) && attr->value_tag == IPP_TAG_INTEGER)
1446 jobtime = attr->values[0].integer;
1447 else if (!strcmp(attr->name, "job-printer-state-message") &&
1448 attr->value_tag == IPP_TAG_TEXT)
1449 message = attr->values[0].string.text;
1450 else if (!strcmp(attr->name, "job-printer-uri") &&
1451 attr->value_tag == IPP_TAG_URI)
1452 {
1453 if ((dest = strrchr(attr->values[0].string.text, '/')) != NULL)
1454 dest ++;
1455 }
1456 else if (!strcmp(attr->name, "job-originating-user-name") &&
1457 attr->value_tag == IPP_TAG_NAME)
1458 username = attr->values[0].string.text;
1459 else if (!strcmp(attr->name, "job-state-reasons") &&
1460 attr->value_tag == IPP_TAG_KEYWORD)
1461 reasons = attr;
1462
1463 attr = attr->next;
1464 }
1465
1466 /*
1467 * See if we have everything needed...
1468 */
1469
1470 if (dest == NULL || jobid == 0)
1471 {
1472 if (attr == NULL)
1473 break;
1474 else
1475 continue;
1476 }
1477
1478 /*
1479 * Display the job...
1480 */
1481
1482 rank ++;
1483
1484 if (match_list(dests, dest) && match_list(users, username))
1485 {
1486 if (!strcmp(which, "successful") && (!reasons || (reasons &&
1487 strcmp(reasons->values[0].string.text, "job-completed-successfully"))))
1488 continue;
1489
1490 snprintf(temp, sizeof(temp), "%s-%d", dest, jobid);
1491
1492 _cupsStrDate(date, sizeof(date), jobtime);
1493
1494 if (ranking)
1495 _cupsLangPrintf(stdout, "%3d %-21s %-13s %8.0f %s",
1496 rank, temp, username ? username : "unknown",
1497 1024.0 * size, date);
1498 else
1499 _cupsLangPrintf(stdout, "%-23s %-13s %8.0f %s",
1500 temp, username ? username : "unknown",
1501 1024.0 * size, date);
1502 if (long_status)
1503 {
1504 if (message)
1505 _cupsLangPrintf(stdout, _("\tStatus: %s"), message);
1506
1507 if (reasons)
1508 {
1509 char alerts[1024], /* Alerts string */
1510 *aptr; /* Pointer into alerts string */
1511
1512 for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
1513 {
1514 if (i)
1515 snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
1516 else
1517 strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
1518
1519 aptr += strlen(aptr);
1520 }
1521
1522 _cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
1523 }
1524
1525 _cupsLangPrintf(stdout, _("\tqueued for %s"), dest);
1526 }
1527 }
1528
1529 if (attr == NULL)
1530 break;
1531 }
1532
1533 ippDelete(response);
1534 }
1535
1536 return (0);
1537 }
1538
1539
1540 /*
1541 * 'show_printers()' - Show printers.
1542 */
1543
1544 static int /* O - 0 on success, 1 on fail */
show_printers(const char * printers,int num_dests,cups_dest_t * dests,int long_status)1545 show_printers(const char *printers, /* I - Destinations */
1546 int num_dests, /* I - Number of user-defined dests */
1547 cups_dest_t *dests, /* I - User-defined destinations */
1548 int long_status) /* I - Show long status? */
1549 {
1550 int i, j; /* Looping vars */
1551 ipp_t *request, /* IPP Request */
1552 *response, /* IPP Response */
1553 *jobs; /* IPP Get Jobs response */
1554 ipp_attribute_t *attr, /* Current attribute */
1555 *jobattr, /* Job ID attribute */
1556 *reasons; /* Job state reasons attribute */
1557 const char *printer, /* Printer name */
1558 *message, /* Printer state message */
1559 *description, /* Description of printer */
1560 *location, /* Location of printer */
1561 *make_model, /* Make and model of printer */
1562 *uri; /* URI of printer */
1563 ipp_attribute_t *allowed, /* requesting-user-name-allowed */
1564 *denied; /* requestint-user-name-denied */
1565 ipp_pstate_t pstate; /* Printer state */
1566 cups_ptype_t ptype; /* Printer type */
1567 time_t ptime; /* Printer state time */
1568 int jobid; /* Job ID of current job */
1569 char printer_uri[HTTP_MAX_URI],
1570 /* Printer URI */
1571 printer_state_time[255];/* Printer state time */
1572 _cups_globals_t *cg = _cupsGlobals(); /* Global data */
1573 static const char *pattrs[] = /* Attributes we need for printers... */
1574 {
1575 "printer-name",
1576 "printer-state",
1577 "printer-state-message",
1578 "printer-state-reasons",
1579 "printer-state-change-time",
1580 "printer-type",
1581 "printer-info",
1582 "printer-location",
1583 "printer-make-and-model",
1584 "printer-uri-supported",
1585 "requesting-user-name-allowed",
1586 "requesting-user-name-denied"
1587 };
1588 static const char *jattrs[] = /* Attributes we need for jobs... */
1589 {
1590 "job-id",
1591 "job-state"
1592 };
1593
1594
1595 if (printers != NULL && !strcmp(printers, "all"))
1596 printers = NULL;
1597
1598 /*
1599 * Build a CUPS_GET_PRINTERS request, which requires the following
1600 * attributes:
1601 *
1602 * attributes-charset
1603 * attributes-natural-language
1604 * requested-attributes
1605 * requesting-user-name
1606 */
1607
1608 request = ippNewRequest(CUPS_GET_PRINTERS);
1609
1610 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1611 "requested-attributes", sizeof(pattrs) / sizeof(pattrs[0]),
1612 NULL, pattrs);
1613
1614 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME, "requesting-user-name",
1615 NULL, cupsUser());
1616
1617 /*
1618 * Do the request and get back a response...
1619 */
1620
1621 response = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/");
1622
1623 if (cupsLastError() == IPP_STATUS_ERROR_SERVICE_UNAVAILABLE)
1624 {
1625 _cupsLangPrintf(stderr, _("%s: Scheduler is not running."), "lpstat");
1626 ippDelete(response);
1627 return (1);
1628 }
1629 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
1630 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
1631 {
1632 _cupsLangPrintf(stderr,
1633 _("%s: Error - add '/version=1.1' to server name."),
1634 "lpstat");
1635 ippDelete(response);
1636 return (1);
1637 }
1638 else if (cupsLastError() > IPP_STATUS_OK_CONFLICTING)
1639 {
1640 _cupsLangPrintf(stderr, "lpstat: %s", cupsLastErrorString());
1641 ippDelete(response);
1642 return (1);
1643 }
1644
1645 if (response)
1646 {
1647 /*
1648 * Loop through the printers returned in the list and display
1649 * their status...
1650 */
1651
1652 for (attr = response->attrs; attr != NULL; attr = attr->next)
1653 {
1654 /*
1655 * Skip leading attributes until we hit a job...
1656 */
1657
1658 while (attr != NULL && attr->group_tag != IPP_TAG_PRINTER)
1659 attr = attr->next;
1660
1661 if (attr == NULL)
1662 break;
1663
1664 /*
1665 * Pull the needed attributes from this job...
1666 */
1667
1668 printer = NULL;
1669 ptime = 0;
1670 ptype = CUPS_PRINTER_LOCAL;
1671 pstate = IPP_PRINTER_IDLE;
1672 message = NULL;
1673 description = NULL;
1674 location = NULL;
1675 make_model = NULL;
1676 reasons = NULL;
1677 uri = NULL;
1678 jobid = 0;
1679 allowed = NULL;
1680 denied = NULL;
1681
1682 while (attr != NULL && attr->group_tag == IPP_TAG_PRINTER)
1683 {
1684 if (!strcmp(attr->name, "printer-name") &&
1685 attr->value_tag == IPP_TAG_NAME)
1686 printer = attr->values[0].string.text;
1687 else if (!strcmp(attr->name, "printer-state") &&
1688 attr->value_tag == IPP_TAG_ENUM)
1689 pstate = (ipp_pstate_t)attr->values[0].integer;
1690 else if (!strcmp(attr->name, "printer-type") &&
1691 attr->value_tag == IPP_TAG_ENUM)
1692 ptype = (cups_ptype_t)attr->values[0].integer;
1693 else if (!strcmp(attr->name, "printer-state-message") &&
1694 attr->value_tag == IPP_TAG_TEXT)
1695 message = attr->values[0].string.text;
1696 else if (!strcmp(attr->name, "printer-state-change-time") &&
1697 attr->value_tag == IPP_TAG_INTEGER)
1698 ptime = (time_t)attr->values[0].integer;
1699 else if (!strcmp(attr->name, "printer-info") &&
1700 attr->value_tag == IPP_TAG_TEXT)
1701 description = attr->values[0].string.text;
1702 else if (!strcmp(attr->name, "printer-location") &&
1703 attr->value_tag == IPP_TAG_TEXT)
1704 location = attr->values[0].string.text;
1705 else if (!strcmp(attr->name, "printer-make-and-model") &&
1706 attr->value_tag == IPP_TAG_TEXT)
1707 make_model = attr->values[0].string.text;
1708 else if (!strcmp(attr->name, "printer-uri-supported") &&
1709 attr->value_tag == IPP_TAG_URI)
1710 uri = attr->values[0].string.text;
1711 else if (!strcmp(attr->name, "printer-state-reasons") &&
1712 attr->value_tag == IPP_TAG_KEYWORD)
1713 reasons = attr;
1714 else if (!strcmp(attr->name, "requesting-user-name-allowed") &&
1715 attr->value_tag == IPP_TAG_NAME)
1716 allowed = attr;
1717 else if (!strcmp(attr->name, "requesting-user-name-denied") &&
1718 attr->value_tag == IPP_TAG_NAME)
1719 denied = attr;
1720
1721 attr = attr->next;
1722 }
1723
1724 /*
1725 * See if we have everything needed...
1726 */
1727
1728 if (printer == NULL)
1729 {
1730 if (attr == NULL)
1731 break;
1732 else
1733 continue;
1734 }
1735
1736 /*
1737 * Display the printer entry if needed...
1738 */
1739
1740 if (match_list(printers, printer))
1741 {
1742 /*
1743 * If the printer state is "IPP_PRINTER_PROCESSING", then grab the
1744 * current job for the printer.
1745 */
1746
1747 if (pstate == IPP_PRINTER_PROCESSING)
1748 {
1749 /*
1750 * Build an IPP_GET_JOBS request, which requires the following
1751 * attributes:
1752 *
1753 * attributes-charset
1754 * attributes-natural-language
1755 * printer-uri
1756 * limit
1757 * requested-attributes
1758 */
1759
1760 request = ippNewRequest(IPP_GET_JOBS);
1761
1762 request->request.op.operation_id = IPP_GET_JOBS;
1763 request->request.op.request_id = 1;
1764
1765 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
1766 "requested-attributes",
1767 sizeof(jattrs) / sizeof(jattrs[0]), NULL, jattrs);
1768
1769 httpAssembleURIf(HTTP_URI_CODING_ALL, printer_uri, sizeof(printer_uri),
1770 "ipp", NULL, "localhost", 0, "/printers/%s", printer);
1771 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
1772 "printer-uri", NULL, printer_uri);
1773
1774 if ((jobs = cupsDoRequest(CUPS_HTTP_DEFAULT, request, "/")) != NULL)
1775 {
1776 /*
1777 * Get the current active job on this queue...
1778 */
1779
1780 ipp_jstate_t jobstate = IPP_JOB_PENDING;
1781 jobid = 0;
1782
1783 for (jobattr = jobs->attrs; jobattr; jobattr = jobattr->next)
1784 {
1785 if (!jobattr->name)
1786 {
1787 if (jobstate == IPP_JOB_PROCESSING)
1788 break;
1789 else
1790 continue;
1791 }
1792
1793 if (!strcmp(jobattr->name, "job-id") &&
1794 jobattr->value_tag == IPP_TAG_INTEGER)
1795 jobid = jobattr->values[0].integer;
1796 else if (!strcmp(jobattr->name, "job-state") &&
1797 jobattr->value_tag == IPP_TAG_ENUM)
1798 jobstate = (ipp_jstate_t)jobattr->values[0].integer;
1799 }
1800
1801 if (jobstate != IPP_JOB_PROCESSING)
1802 jobid = 0;
1803
1804 ippDelete(jobs);
1805 }
1806 }
1807
1808 /*
1809 * Display it...
1810 */
1811
1812 _cupsStrDate(printer_state_time, sizeof(printer_state_time), ptime);
1813
1814 switch (pstate)
1815 {
1816 case IPP_PRINTER_IDLE :
1817 if (ippContainsString(reasons, "hold-new-jobs"))
1818 _cupsLangPrintf(stdout, _("printer %s is holding new jobs. enabled since %s"), printer, printer_state_time);
1819 else
1820 _cupsLangPrintf(stdout, _("printer %s is idle. enabled since %s"), printer, printer_state_time);
1821 break;
1822 case IPP_PRINTER_PROCESSING :
1823 _cupsLangPrintf(stdout, _("printer %s now printing %s-%d. enabled since %s"), printer, printer, jobid, printer_state_time);
1824 break;
1825 case IPP_PRINTER_STOPPED :
1826 _cupsLangPrintf(stdout, _("printer %s disabled since %s -"), printer, printer_state_time);
1827 break;
1828 }
1829
1830 if ((message && *message) || pstate == IPP_PRINTER_STOPPED)
1831 {
1832 if (message && *message)
1833 _cupsLangPrintf(stdout, "\t%s", message);
1834 else
1835 _cupsLangPuts(stdout, _("\treason unknown"));
1836 }
1837
1838 if (long_status > 1)
1839 {
1840 _cupsLangPuts(stdout, _("\tForm mounted:"));
1841 _cupsLangPuts(stdout, _("\tContent types: any"));
1842 _cupsLangPuts(stdout, _("\tPrinter types: unknown"));
1843 }
1844
1845 if (long_status)
1846 {
1847 _cupsLangPrintf(stdout, _("\tDescription: %s"),
1848 description ? description : "");
1849
1850 if (reasons)
1851 {
1852 char alerts[1024], /* Alerts string */
1853 *aptr; /* Pointer into alerts string */
1854
1855 for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
1856 {
1857 if (i)
1858 snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
1859 else
1860 strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
1861
1862 aptr += strlen(aptr);
1863 }
1864
1865 _cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
1866 }
1867 }
1868 if (long_status > 1)
1869 {
1870 _cupsLangPrintf(stdout, _("\tLocation: %s"),
1871 location ? location : "");
1872
1873 if (ptype & CUPS_PRINTER_REMOTE)
1874 {
1875 _cupsLangPuts(stdout, _("\tConnection: remote"));
1876
1877 if (make_model && !strstr(make_model, "System V Printer") &&
1878 !strstr(make_model, "Raw Printer") && uri)
1879 _cupsLangPrintf(stdout, _("\tInterface: %s.ppd"),
1880 uri);
1881 }
1882 else
1883 {
1884 _cupsLangPuts(stdout, _("\tConnection: direct"));
1885
1886 if (make_model && !strstr(make_model, "Raw Printer"))
1887 _cupsLangPrintf(stdout,
1888 _("\tInterface: %s/ppd/%s.ppd"),
1889 cg->cups_serverroot, printer);
1890 }
1891 _cupsLangPuts(stdout, _("\tOn fault: no alert"));
1892 _cupsLangPuts(stdout, _("\tAfter fault: continue"));
1893 /* TODO update to use printer-error-policy */
1894 if (allowed)
1895 {
1896 _cupsLangPuts(stdout, _("\tUsers allowed:"));
1897 for (j = 0; j < allowed->num_values; j ++)
1898 _cupsLangPrintf(stdout, "\t\t%s",
1899 allowed->values[j].string.text);
1900 }
1901 else if (denied)
1902 {
1903 _cupsLangPuts(stdout, _("\tUsers denied:"));
1904 for (j = 0; j < denied->num_values; j ++)
1905 _cupsLangPrintf(stdout, "\t\t%s",
1906 denied->values[j].string.text);
1907 }
1908 else
1909 {
1910 _cupsLangPuts(stdout, _("\tUsers allowed:"));
1911 _cupsLangPuts(stdout, _("\t\t(all)"));
1912 }
1913 _cupsLangPuts(stdout, _("\tForms allowed:"));
1914 _cupsLangPuts(stdout, _("\t\t(none)"));
1915 _cupsLangPuts(stdout, _("\tBanner required"));
1916 _cupsLangPuts(stdout, _("\tCharset sets:"));
1917 _cupsLangPuts(stdout, _("\t\t(none)"));
1918 _cupsLangPuts(stdout, _("\tDefault pitch:"));
1919 _cupsLangPuts(stdout, _("\tDefault page size:"));
1920 _cupsLangPuts(stdout, _("\tDefault port settings:"));
1921 }
1922
1923 for (i = 0; i < num_dests; i ++)
1924 if (!_cups_strcasecmp(printer, dests[i].name) && dests[i].instance)
1925 {
1926 switch (pstate)
1927 {
1928 case IPP_PRINTER_IDLE :
1929 _cupsLangPrintf(stdout,
1930 _("printer %s/%s is idle. "
1931 "enabled since %s"),
1932 printer, dests[i].instance,
1933 printer_state_time);
1934 break;
1935 case IPP_PRINTER_PROCESSING :
1936 _cupsLangPrintf(stdout,
1937 _("printer %s/%s now printing %s-%d. "
1938 "enabled since %s"),
1939 printer, dests[i].instance, printer, jobid,
1940 printer_state_time);
1941 break;
1942 case IPP_PRINTER_STOPPED :
1943 _cupsLangPrintf(stdout,
1944 _("printer %s/%s disabled since %s -"),
1945 printer, dests[i].instance,
1946 printer_state_time);
1947 break;
1948 }
1949
1950 if ((message && *message) || pstate == IPP_PRINTER_STOPPED)
1951 {
1952 if (message && *message)
1953 _cupsLangPrintf(stdout, "\t%s", message);
1954 else
1955 _cupsLangPuts(stdout, _("\treason unknown"));
1956 }
1957
1958 if (long_status > 1)
1959 {
1960 _cupsLangPuts(stdout, _("\tForm mounted:"));
1961 _cupsLangPuts(stdout, _("\tContent types: any"));
1962 _cupsLangPuts(stdout, _("\tPrinter types: unknown"));
1963 }
1964
1965 if (long_status)
1966 {
1967 _cupsLangPrintf(stdout, _("\tDescription: %s"),
1968 description ? description : "");
1969
1970 if (reasons)
1971 {
1972 char alerts[1024], /* Alerts string */
1973 *aptr; /* Pointer into alerts string */
1974
1975 for (i = 0, aptr = alerts; i < reasons->num_values; i ++)
1976 {
1977 if (i)
1978 snprintf(aptr, sizeof(alerts) - (size_t)(aptr - alerts), " %s", reasons->values[i].string.text);
1979 else
1980 strlcpy(alerts, reasons->values[i].string.text, sizeof(alerts));
1981
1982 aptr += strlen(aptr);
1983 }
1984
1985 _cupsLangPrintf(stdout, _("\tAlerts: %s"), alerts);
1986 }
1987 }
1988 if (long_status > 1)
1989 {
1990 _cupsLangPrintf(stdout, _("\tLocation: %s"),
1991 location ? location : "");
1992
1993 if (ptype & CUPS_PRINTER_REMOTE)
1994 {
1995 _cupsLangPuts(stdout, _("\tConnection: remote"));
1996
1997 if (make_model && !strstr(make_model, "System V Printer") &&
1998 !strstr(make_model, "Raw Printer") && uri)
1999 _cupsLangPrintf(stdout, _("\tInterface: %s.ppd"), uri);
2000 }
2001 else
2002 {
2003 _cupsLangPuts(stdout, _("\tConnection: direct"));
2004
2005 if (make_model && !strstr(make_model, "Raw Printer"))
2006 _cupsLangPrintf(stdout,
2007 _("\tInterface: %s/ppd/%s.ppd"),
2008 cg->cups_serverroot, printer);
2009 }
2010 _cupsLangPuts(stdout, _("\tOn fault: no alert"));
2011 _cupsLangPuts(stdout, _("\tAfter fault: continue"));
2012 /* TODO update to use printer-error-policy */
2013 if (allowed)
2014 {
2015 _cupsLangPuts(stdout, _("\tUsers allowed:"));
2016 for (j = 0; j < allowed->num_values; j ++)
2017 _cupsLangPrintf(stdout, "\t\t%s",
2018 allowed->values[j].string.text);
2019 }
2020 else if (denied)
2021 {
2022 _cupsLangPuts(stdout, _("\tUsers denied:"));
2023 for (j = 0; j < denied->num_values; j ++)
2024 _cupsLangPrintf(stdout, "\t\t%s",
2025 denied->values[j].string.text);
2026 }
2027 else
2028 {
2029 _cupsLangPuts(stdout, _("\tUsers allowed:"));
2030 _cupsLangPuts(stdout, _("\t\t(all)"));
2031 }
2032 _cupsLangPuts(stdout, _("\tForms allowed:"));
2033 _cupsLangPuts(stdout, _("\t\t(none)"));
2034 _cupsLangPuts(stdout, _("\tBanner required"));
2035 _cupsLangPuts(stdout, _("\tCharset sets:"));
2036 _cupsLangPuts(stdout, _("\t\t(none)"));
2037 _cupsLangPuts(stdout, _("\tDefault pitch:"));
2038 _cupsLangPuts(stdout, _("\tDefault page size:"));
2039 _cupsLangPuts(stdout, _("\tDefault port settings:"));
2040 }
2041 }
2042 }
2043
2044 if (attr == NULL)
2045 break;
2046 }
2047
2048 ippDelete(response);
2049 }
2050
2051 return (0);
2052 }
2053
2054
2055 /*
2056 * 'show_scheduler()' - Show scheduler status.
2057 */
2058
2059 static int /* 1 on success, 0 on failure */
show_scheduler(void)2060 show_scheduler(void)
2061 {
2062 http_t *http; /* Connection to server */
2063
2064
2065 if ((http = httpConnectEncrypt(cupsServer(), ippPort(),
2066 cupsEncryption())) != NULL)
2067 {
2068 _cupsLangPuts(stdout, _("scheduler is running"));
2069 httpClose(http);
2070 return (1);
2071 }
2072 else
2073 {
2074 _cupsLangPuts(stdout, _("scheduler is not running"));
2075 return (0);
2076 }
2077 }
2078
2079
2080 /*
2081 * 'usage()' - Show program usage and exit.
2082 */
2083
2084 static void
usage(void)2085 usage(void)
2086 {
2087 _cupsLangPuts(stdout, _("Usage: lpstat [options]"));
2088 _cupsLangPuts(stdout, _("Options:"));
2089 _cupsLangPuts(stdout, _("-E Encrypt the connection to the server"));
2090 _cupsLangPuts(stdout, _("-h server[:port] Connect to the named server and port"));
2091 _cupsLangPuts(stdout, _("-l Show verbose (long) output"));
2092 _cupsLangPuts(stdout, _("-U username Specify the username to use for authentication"));
2093
2094 _cupsLangPuts(stdout, _("-H Show the default server and port"));
2095 _cupsLangPuts(stdout, _("-W completed Show completed jobs"));
2096 _cupsLangPuts(stdout, _("-W not-completed Show pending jobs"));
2097 _cupsLangPuts(stdout, _("-a [destination(s)] Show the accepting state of destinations"));
2098 _cupsLangPuts(stdout, _("-c [class(es)] Show classes and their member printers"));
2099 _cupsLangPuts(stdout, _("-d Show the default destination"));
2100 _cupsLangPuts(stdout, _("-e Show available destinations on the network"));
2101 _cupsLangPuts(stdout, _("-o [destination(s)] Show jobs"));
2102 _cupsLangPuts(stdout, _("-p [printer(s)] Show the processing state of destinations"));
2103 _cupsLangPuts(stdout, _("-r Show whether the CUPS server is running"));
2104 _cupsLangPuts(stdout, _("-R Show the ranking of jobs"));
2105 _cupsLangPuts(stdout, _("-s Show a status summary"));
2106 _cupsLangPuts(stdout, _("-t Show all status information"));
2107 _cupsLangPuts(stdout, _("-u [user(s)] Show jobs queued by the current or specified users"));
2108 _cupsLangPuts(stdout, _("-v [printer(s)] Show the devices for each destination"));
2109
2110 exit(1);
2111 }
2112