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