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