]> git.ipfire.org Git - thirdparty/cups.git/blob - berkeley/lpq.c
f01168ca5b7c29b0baeb7bcd9fdfc5ccb1666561
[thirdparty/cups.git] / berkeley / lpq.c
1 /*
2 * "lpq" command for CUPS.
3 *
4 * Copyright 2007-2016 by Apple Inc.
5 * Copyright 1997-2006 by Easy Software Products.
6 *
7 * These coded instructions, statements, and computer programs are the
8 * property of Apple Inc. and are protected by Federal copyright
9 * law. Distribution and use rights are outlined in the file "LICENSE.txt"
10 * which should have been included with this file. If this file is
11 * file is missing or damaged, see the license at "http://www.cups.org/".
12 */
13
14 /*
15 * Include necessary headers...
16 */
17
18 #include <cups/cups-private.h>
19
20
21 /*
22 * Local functions...
23 */
24
25 static http_t *connect_server(const char *, http_t *);
26 static int show_jobs(const char *, http_t *, const char *,
27 const char *, const int, const int);
28 static void show_printer(const char *, http_t *, const char *);
29 static void usage(void) __attribute__((noreturn));
30
31
32 /*
33 * 'main()' - Parse options and commands.
34 */
35
36 int
37 main(int argc, /* I - Number of command-line arguments */
38 char *argv[]) /* I - Command-line arguments */
39 {
40 int i; /* Looping var */
41 http_t *http; /* Connection to server */
42 const char *opt, /* Option pointer */
43 *dest, /* Desired printer */
44 *user, /* Desired user */
45 *val; /* Environment variable name */
46 char *instance; /* Printer instance */
47 int id, /* Desired job ID */
48 all, /* All printers */
49 interval, /* Reporting interval */
50 longstatus; /* Show file details */
51 cups_dest_t *named_dest; /* Named destination */
52
53
54 _cupsSetLocale(argv);
55
56 /*
57 * Check for command-line options...
58 */
59
60 http = NULL;
61 dest = NULL;
62 user = NULL;
63 id = 0;
64 interval = 0;
65 longstatus = 0;
66 all = 0;
67
68 for (i = 1; i < argc; i ++)
69 {
70 if (argv[i][0] == '+')
71 {
72 interval = atoi(argv[i] + 1);
73 }
74 else if (argv[i][0] == '-')
75 {
76 for (opt = argv[i] + 1; *opt; opt ++)
77 {
78 switch (*opt)
79 {
80 case 'E' : /* Encrypt */
81 #ifdef HAVE_SSL
82 cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
83
84 if (http)
85 httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
86 #else
87 _cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
88 #endif /* HAVE_SSL */
89 break;
90
91 case 'U' : /* Username */
92 if (opt[1] != '\0')
93 {
94 cupsSetUser(opt + 1);
95 opt += strlen(opt) - 1;
96 }
97 else
98 {
99 i ++;
100 if (i >= argc)
101 {
102 _cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
103 return (1);
104 }
105
106 cupsSetUser(argv[i]);
107 }
108 break;
109
110 case 'P' : /* Printer */
111 if (opt[1] != '\0')
112 {
113 dest = opt + 1;
114 opt += strlen(opt) - 1;
115 }
116 else
117 {
118 i ++;
119
120 if (i >= argc)
121 {
122 httpClose(http);
123
124 usage();
125 }
126
127 dest = argv[i];
128 }
129
130 if ((instance = strchr(dest, '/')) != NULL)
131 *instance++ = '\0';
132
133 http = connect_server(argv[0], http);
134
135 if ((named_dest = cupsGetNamedDest(http, dest, instance)) == NULL)
136 {
137 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
138 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
139 _cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
140 else if (instance)
141 _cupsLangPrintf(stderr, _("%s: Error - unknown destination \"%s/%s\"."), argv[0], dest, instance);
142 else
143 _cupsLangPrintf(stderr, _("%s: Unknown destination \"%s\"."), argv[0], dest);
144
145 return (1);
146 }
147
148 cupsFreeDests(1, named_dest);
149 break;
150
151 case 'a' : /* All printers */
152 all = 1;
153 break;
154
155 case 'h' : /* Connect to host */
156 if (http)
157 {
158 httpClose(http);
159 http = NULL;
160 }
161
162 if (opt[1] != '\0')
163 {
164 cupsSetServer(opt + 1);
165 opt += strlen(opt) - 1;
166 }
167 else
168 {
169 i ++;
170
171 if (i >= argc)
172 {
173 _cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-h\" option."), argv[0]);
174 return (1);
175 }
176 else
177 cupsSetServer(argv[i]);
178 }
179 break;
180
181 case 'l' : /* Long status */
182 longstatus = 1;
183 break;
184
185 default :
186 httpClose(http);
187
188 usage();
189 }
190 }
191 }
192 else if (isdigit(argv[i][0] & 255))
193 {
194 id = atoi(argv[i]);
195 }
196 else
197 {
198 user = argv[i];
199 }
200 }
201
202 http = connect_server(argv[0], http);
203
204 if (dest == NULL && !all)
205 {
206 if ((named_dest = cupsGetNamedDest(http, NULL, NULL)) == NULL)
207 {
208 if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
209 cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
210 {
211 _cupsLangPrintf(stderr,
212 _("%s: Error - add '/version=1.1' to server name."),
213 argv[0]);
214 return (1);
215 }
216
217 val = NULL;
218
219 if ((dest = getenv("LPDEST")) == NULL)
220 {
221 if ((dest = getenv("PRINTER")) != NULL)
222 {
223 if (!strcmp(dest, "lp"))
224 dest = NULL;
225 else
226 val = "PRINTER";
227 }
228 }
229 else
230 val = "LPDEST";
231
232 if (dest && val)
233 _cupsLangPrintf(stderr,
234 _("%s: Error - %s environment variable names "
235 "non-existent destination \"%s\"."), argv[0], val,
236 dest);
237 else
238 _cupsLangPrintf(stderr,
239 _("%s: Error - no default destination available."),
240 argv[0]);
241 httpClose(http);
242 return (1);
243 }
244
245 dest = named_dest->name;
246 }
247
248 /*
249 * Show the status in a loop...
250 */
251
252 for (;;)
253 {
254 if (dest)
255 show_printer(argv[0], http, dest);
256
257 i = show_jobs(argv[0], http, dest, user, id, longstatus);
258
259 if (i && interval)
260 {
261 fflush(stdout);
262 sleep((unsigned)interval);
263 }
264 else
265 break;
266 }
267
268 /*
269 * Close the connection to the server and return...
270 */
271
272 httpClose(http);
273
274 return (0);
275 }
276
277
278 /*
279 * 'connect_server()' - Connect to the server as necessary...
280 */
281
282 static http_t * /* O - New HTTP connection */
283 connect_server(const char *command, /* I - Command name */
284 http_t *http) /* I - Current HTTP connection */
285 {
286 if (!http)
287 {
288 http = httpConnectEncrypt(cupsServer(), ippPort(),
289 cupsEncryption());
290
291 if (http == NULL)
292 {
293 _cupsLangPrintf(stderr, _("%s: Unable to connect to server."), command);
294 exit(1);
295 }
296 }
297
298 return (http);
299 }
300
301
302 /*
303 * 'show_jobs()' - Show jobs.
304 */
305
306 static int /* O - Number of jobs in queue */
307 show_jobs(const char *command, /* I - Command name */
308 http_t *http, /* I - HTTP connection to server */
309 const char *dest, /* I - Destination */
310 const char *user, /* I - User */
311 const int id, /* I - Job ID */
312 const int longstatus) /* I - 1 if long report desired */
313 {
314 ipp_t *request, /* IPP Request */
315 *response; /* IPP Response */
316 ipp_attribute_t *attr; /* Current attribute */
317 const char *jobdest, /* Pointer into job-printer-uri */
318 *jobuser, /* Pointer to job-originating-user-name */
319 *jobname; /* Pointer to job-name */
320 ipp_jstate_t jobstate; /* job-state */
321 int jobid, /* job-id */
322 jobsize, /* job-k-octets */
323 jobcount, /* Number of jobs */
324 jobcopies, /* Number of copies */
325 rank; /* Rank of job */
326 char resource[1024]; /* Resource string */
327 char rankstr[255]; /* Rank string */
328 char namestr[1024]; /* Job name string */
329 static const char * const jobattrs[] =/* Job attributes we want to see */
330 {
331 "copies",
332 "job-id",
333 "job-k-octets",
334 "job-name",
335 "job-originating-user-name",
336 "job-printer-uri",
337 "job-priority",
338 "job-state"
339 };
340 static const char * const ranks[10] = /* Ranking strings */
341 {
342 "th",
343 "st",
344 "nd",
345 "rd",
346 "th",
347 "th",
348 "th",
349 "th",
350 "th",
351 "th"
352 };
353
354
355 DEBUG_printf(("show_jobs(http=%p, dest=%p, user=%p, id=%d, longstatus%d)\n",
356 http, dest, user, id, longstatus));
357
358 if (http == NULL)
359 return (0);
360
361 /*
362 * Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
363 * the following attributes:
364 *
365 * attributes-charset
366 * attributes-natural-language
367 * job-uri or printer-uri
368 * requested-attributes
369 * requesting-user-name
370 */
371
372 request = ippNewRequest(id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS);
373
374 if (id)
375 {
376 snprintf(resource, sizeof(resource), "ipp://localhost/jobs/%d", id);
377 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
378 NULL, resource);
379 }
380 else if (dest)
381 {
382 httpAssembleURIf(HTTP_URI_CODING_ALL, resource, sizeof(resource), "ipp",
383 NULL, "localhost", 0, "/printers/%s", dest);
384
385 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
386 NULL, resource);
387 }
388 else
389 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
390 NULL, "ipp://localhost/");
391
392 if (user)
393 {
394 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
395 "requesting-user-name", NULL, user);
396 ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
397 }
398 else
399 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
400 "requesting-user-name", NULL, cupsUser());
401
402 ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
403 "requested-attributes",
404 (int)(sizeof(jobattrs) / sizeof(jobattrs[0])), NULL, jobattrs);
405
406 /*
407 * Do the request and get back a response...
408 */
409
410 jobcount = 0;
411
412 if ((response = cupsDoRequest(http, request, "/")) != NULL)
413 {
414 if (response->request.status.status_code > IPP_OK_CONFLICT)
415 {
416 _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
417 ippDelete(response);
418 return (0);
419 }
420
421 rank = 1;
422
423 /*
424 * Loop through the job list and display them...
425 */
426
427 for (attr = response->attrs; attr != NULL; attr = attr->next)
428 {
429 /*
430 * Skip leading attributes until we hit a job...
431 */
432
433 while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
434 attr = attr->next;
435
436 if (attr == NULL)
437 break;
438
439 /*
440 * Pull the needed attributes from this job...
441 */
442
443 jobid = 0;
444 jobsize = 0;
445 jobstate = IPP_JOB_PENDING;
446 jobname = "unknown";
447 jobuser = "unknown";
448 jobdest = NULL;
449 jobcopies = 1;
450
451 while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
452 {
453 if (!strcmp(attr->name, "job-id") &&
454 attr->value_tag == IPP_TAG_INTEGER)
455 jobid = attr->values[0].integer;
456
457 if (!strcmp(attr->name, "job-k-octets") &&
458 attr->value_tag == IPP_TAG_INTEGER)
459 jobsize = attr->values[0].integer;
460
461 if (!strcmp(attr->name, "job-state") &&
462 attr->value_tag == IPP_TAG_ENUM)
463 jobstate = (ipp_jstate_t)attr->values[0].integer;
464
465 if (!strcmp(attr->name, "job-printer-uri") &&
466 attr->value_tag == IPP_TAG_URI)
467 if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
468 jobdest ++;
469
470 if (!strcmp(attr->name, "job-originating-user-name") &&
471 attr->value_tag == IPP_TAG_NAME)
472 jobuser = attr->values[0].string.text;
473
474 if (!strcmp(attr->name, "job-name") &&
475 attr->value_tag == IPP_TAG_NAME)
476 jobname = attr->values[0].string.text;
477
478 if (!strcmp(attr->name, "copies") &&
479 attr->value_tag == IPP_TAG_INTEGER)
480 jobcopies = attr->values[0].integer;
481
482 attr = attr->next;
483 }
484
485 /*
486 * See if we have everything needed...
487 */
488
489 if (jobdest == NULL || jobid == 0)
490 {
491 if (attr == NULL)
492 break;
493 else
494 continue;
495 }
496
497 if (!longstatus && jobcount == 0)
498 _cupsLangPuts(stdout,
499 _("Rank Owner Job File(s)"
500 " Total Size"));
501
502 jobcount ++;
503
504 /*
505 * Display the job...
506 */
507
508 if (jobstate == IPP_JOB_PROCESSING)
509 strlcpy(rankstr, "active", sizeof(rankstr));
510 else
511 {
512 /*
513 * Make the rank show the "correct" suffix for each number
514 * (11-13 are the only special cases, for English anyways...)
515 */
516
517 if ((rank % 100) >= 11 && (rank % 100) <= 13)
518 snprintf(rankstr, sizeof(rankstr), "%dth", rank);
519 else
520 snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
521
522 rank ++;
523 }
524
525 if (longstatus)
526 {
527 _cupsLangPuts(stdout, "\n");
528
529 if (jobcopies > 1)
530 snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
531 jobname);
532 else
533 strlcpy(namestr, jobname, sizeof(namestr));
534
535 _cupsLangPrintf(stdout, _("%s: %-33.33s [job %d localhost]"),
536 jobuser, rankstr, jobid);
537 _cupsLangPrintf(stdout, _(" %-39.39s %.0f bytes"),
538 namestr, 1024.0 * jobsize);
539 }
540 else
541 _cupsLangPrintf(stdout,
542 _("%-7s %-7.7s %-7d %-31.31s %.0f bytes"),
543 rankstr, jobuser, jobid, jobname, 1024.0 * jobsize);
544
545 if (attr == NULL)
546 break;
547 }
548
549 ippDelete(response);
550 }
551 else
552 {
553 _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
554 return (0);
555 }
556
557 if (jobcount == 0)
558 _cupsLangPuts(stdout, _("no entries"));
559
560 return (jobcount);
561 }
562
563
564 /*
565 * 'show_printer()' - Show printer status.
566 */
567
568 static void
569 show_printer(const char *command, /* I - Command name */
570 http_t *http, /* I - HTTP connection to server */
571 const char *dest) /* I - Destination */
572 {
573 ipp_t *request, /* IPP Request */
574 *response; /* IPP Response */
575 ipp_attribute_t *attr; /* Current attribute */
576 ipp_pstate_t state; /* Printer state */
577 char uri[HTTP_MAX_URI]; /* Printer URI */
578
579
580 if (http == NULL)
581 return;
582
583 /*
584 * Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
585 * attributes:
586 *
587 * attributes-charset
588 * attributes-natural-language
589 * printer-uri
590 */
591
592 request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
593
594 httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
595 "localhost", 0, "/printers/%s", dest);
596 ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
597 "printer-uri", NULL, uri);
598
599 /*
600 * Do the request and get back a response...
601 */
602
603 if ((response = cupsDoRequest(http, request, "/")) != NULL)
604 {
605 if (response->request.status.status_code > IPP_OK_CONFLICT)
606 {
607 _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
608 ippDelete(response);
609 return;
610 }
611
612 if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
613 state = (ipp_pstate_t)attr->values[0].integer;
614 else
615 state = IPP_PRINTER_STOPPED;
616
617 switch (state)
618 {
619 case IPP_PRINTER_IDLE :
620 _cupsLangPrintf(stdout, _("%s is ready"), dest);
621 break;
622 case IPP_PRINTER_PROCESSING :
623 _cupsLangPrintf(stdout, _("%s is ready and printing"),
624 dest);
625 break;
626 case IPP_PRINTER_STOPPED :
627 _cupsLangPrintf(stdout, _("%s is not ready"), dest);
628 break;
629 }
630
631 ippDelete(response);
632 }
633 else
634 _cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
635 }
636
637
638 /*
639 * 'usage()' - Show program usage.
640 */
641
642 static void
643 usage(void)
644 {
645 _cupsLangPuts(stderr,
646 _("Usage: lpq [-P dest] [-U username] [-h hostname[:port]] "
647 "[-l] [+interval]"));
648 exit(1);
649 }