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