mirror of https://gitee.com/openkylin/cups.git
652 lines
15 KiB
C
652 lines
15 KiB
C
/*
|
||
* "lpq" command for CUPS.
|
||
*
|
||
* Copyright © 2007-2018 by Apple Inc.
|
||
* Copyright © 1997-2006 by Easy Software Products.
|
||
*
|
||
* Licensed under Apache License v2.0. See the file "LICENSE" for more
|
||
* information.
|
||
*/
|
||
|
||
/*
|
||
* Include necessary headers...
|
||
*/
|
||
|
||
#include <cups/cups-private.h>
|
||
|
||
|
||
/*
|
||
* Local functions...
|
||
*/
|
||
|
||
static http_t *connect_server(const char *, http_t *);
|
||
static int show_jobs(const char *, http_t *, const char *,
|
||
const char *, const int, const int);
|
||
static void show_printer(const char *, http_t *, const char *);
|
||
static void usage(void) _CUPS_NORETURN;
|
||
|
||
|
||
/*
|
||
* 'main()' - Parse options and commands.
|
||
*/
|
||
|
||
int
|
||
main(int argc, /* I - Number of command-line arguments */
|
||
char *argv[]) /* I - Command-line arguments */
|
||
{
|
||
int i; /* Looping var */
|
||
http_t *http; /* Connection to server */
|
||
const char *opt, /* Option pointer */
|
||
*dest, /* Desired printer */
|
||
*user, /* Desired user */
|
||
*val; /* Environment variable name */
|
||
char *instance; /* Printer instance */
|
||
int id, /* Desired job ID */
|
||
all, /* All printers */
|
||
interval, /* Reporting interval */
|
||
longstatus; /* Show file details */
|
||
cups_dest_t *named_dest; /* Named destination */
|
||
|
||
|
||
_cupsSetLocale(argv);
|
||
|
||
/*
|
||
* Check for command-line options...
|
||
*/
|
||
|
||
http = NULL;
|
||
dest = NULL;
|
||
user = NULL;
|
||
id = 0;
|
||
interval = 0;
|
||
longstatus = 0;
|
||
all = 0;
|
||
|
||
for (i = 1; i < argc; i ++)
|
||
{
|
||
if (argv[i][0] == '+')
|
||
{
|
||
interval = atoi(argv[i] + 1);
|
||
}
|
||
else if (!strcmp(argv[i], "--help"))
|
||
usage();
|
||
else if (argv[i][0] == '-')
|
||
{
|
||
for (opt = argv[i] + 1; *opt; opt ++)
|
||
{
|
||
switch (*opt)
|
||
{
|
||
case 'E' : /* Encrypt */
|
||
#ifdef HAVE_SSL
|
||
cupsSetEncryption(HTTP_ENCRYPT_REQUIRED);
|
||
|
||
if (http)
|
||
httpEncryption(http, HTTP_ENCRYPT_REQUIRED);
|
||
#else
|
||
_cupsLangPrintf(stderr, _("%s: Sorry, no encryption support."), argv[0]);
|
||
#endif /* HAVE_SSL */
|
||
break;
|
||
|
||
case 'U' : /* Username */
|
||
if (opt[1] != '\0')
|
||
{
|
||
cupsSetUser(opt + 1);
|
||
opt += strlen(opt) - 1;
|
||
}
|
||
else
|
||
{
|
||
i ++;
|
||
if (i >= argc)
|
||
{
|
||
_cupsLangPrintf(stderr, _("%s: Error - expected username after \"-U\" option."), argv[0]);
|
||
return (1);
|
||
}
|
||
|
||
cupsSetUser(argv[i]);
|
||
}
|
||
break;
|
||
|
||
case 'P' : /* Printer */
|
||
if (opt[1] != '\0')
|
||
{
|
||
dest = opt + 1;
|
||
opt += strlen(opt) - 1;
|
||
}
|
||
else
|
||
{
|
||
i ++;
|
||
|
||
if (i >= argc)
|
||
{
|
||
httpClose(http);
|
||
|
||
usage();
|
||
}
|
||
|
||
dest = argv[i];
|
||
}
|
||
|
||
if ((instance = strchr(dest, '/')) != NULL)
|
||
*instance++ = '\0';
|
||
|
||
http = connect_server(argv[0], http);
|
||
|
||
if ((named_dest = cupsGetNamedDest(http, dest, instance)) == NULL)
|
||
{
|
||
if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
|
||
cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
|
||
_cupsLangPrintf(stderr, _("%s: Error - add '/version=1.1' to server name."), argv[0]);
|
||
else if (instance)
|
||
_cupsLangPrintf(stderr, _("%s: Error - unknown destination \"%s/%s\"."), argv[0], dest, instance);
|
||
else
|
||
_cupsLangPrintf(stderr, _("%s: Unknown destination \"%s\"."), argv[0], dest);
|
||
|
||
return (1);
|
||
}
|
||
|
||
cupsFreeDests(1, named_dest);
|
||
break;
|
||
|
||
case 'a' : /* All printers */
|
||
all = 1;
|
||
break;
|
||
|
||
case 'h' : /* Connect to host */
|
||
if (http)
|
||
{
|
||
httpClose(http);
|
||
http = NULL;
|
||
}
|
||
|
||
if (opt[1] != '\0')
|
||
{
|
||
cupsSetServer(opt + 1);
|
||
opt += strlen(opt) - 1;
|
||
}
|
||
else
|
||
{
|
||
i ++;
|
||
|
||
if (i >= argc)
|
||
{
|
||
_cupsLangPrintf(stderr, _("%s: Error - expected hostname after \"-h\" option."), argv[0]);
|
||
return (1);
|
||
}
|
||
else
|
||
cupsSetServer(argv[i]);
|
||
}
|
||
break;
|
||
|
||
case 'l' : /* Long status */
|
||
longstatus = 1;
|
||
break;
|
||
|
||
default :
|
||
httpClose(http);
|
||
|
||
usage();
|
||
}
|
||
}
|
||
}
|
||
else if (isdigit(argv[i][0] & 255))
|
||
{
|
||
id = atoi(argv[i]);
|
||
}
|
||
else
|
||
{
|
||
user = argv[i];
|
||
}
|
||
}
|
||
|
||
http = connect_server(argv[0], http);
|
||
|
||
if (dest == NULL && !all)
|
||
{
|
||
if ((named_dest = cupsGetNamedDest(http, NULL, NULL)) == NULL)
|
||
{
|
||
if (cupsLastError() == IPP_STATUS_ERROR_BAD_REQUEST ||
|
||
cupsLastError() == IPP_STATUS_ERROR_VERSION_NOT_SUPPORTED)
|
||
{
|
||
_cupsLangPrintf(stderr,
|
||
_("%s: Error - add '/version=1.1' to server name."),
|
||
argv[0]);
|
||
return (1);
|
||
}
|
||
|
||
val = NULL;
|
||
|
||
if ((dest = getenv("LPDEST")) == NULL)
|
||
{
|
||
if ((dest = getenv("PRINTER")) != NULL)
|
||
{
|
||
if (!strcmp(dest, "lp"))
|
||
dest = NULL;
|
||
else
|
||
val = "PRINTER";
|
||
}
|
||
}
|
||
else
|
||
val = "LPDEST";
|
||
|
||
if (dest && val)
|
||
_cupsLangPrintf(stderr,
|
||
_("%s: Error - %s environment variable names "
|
||
"non-existent destination \"%s\"."), argv[0], val,
|
||
dest);
|
||
else
|
||
_cupsLangPrintf(stderr,
|
||
_("%s: Error - no default destination available."),
|
||
argv[0]);
|
||
httpClose(http);
|
||
return (1);
|
||
}
|
||
|
||
dest = named_dest->name;
|
||
}
|
||
|
||
/*
|
||
* Show the status in a loop...
|
||
*/
|
||
|
||
for (;;)
|
||
{
|
||
if (dest)
|
||
show_printer(argv[0], http, dest);
|
||
|
||
i = show_jobs(argv[0], http, dest, user, id, longstatus);
|
||
|
||
if (i && interval)
|
||
{
|
||
fflush(stdout);
|
||
sleep((unsigned)interval);
|
||
}
|
||
else
|
||
break;
|
||
}
|
||
|
||
/*
|
||
* Close the connection to the server and return...
|
||
*/
|
||
|
||
httpClose(http);
|
||
|
||
return (0);
|
||
}
|
||
|
||
|
||
/*
|
||
* 'connect_server()' - Connect to the server as necessary...
|
||
*/
|
||
|
||
static http_t * /* O - New HTTP connection */
|
||
connect_server(const char *command, /* I - Command name */
|
||
http_t *http) /* I - Current HTTP connection */
|
||
{
|
||
if (!http)
|
||
{
|
||
http = httpConnectEncrypt(cupsServer(), ippPort(),
|
||
cupsEncryption());
|
||
|
||
if (http == NULL)
|
||
{
|
||
_cupsLangPrintf(stderr, _("%s: Unable to connect to server."), command);
|
||
exit(1);
|
||
}
|
||
}
|
||
|
||
return (http);
|
||
}
|
||
|
||
|
||
/*
|
||
* 'show_jobs()' - Show jobs.
|
||
*/
|
||
|
||
static int /* O - Number of jobs in queue */
|
||
show_jobs(const char *command, /* I - Command name */
|
||
http_t *http, /* I - HTTP connection to server */
|
||
const char *dest, /* I - Destination */
|
||
const char *user, /* I - User */
|
||
const int id, /* I - Job ID */
|
||
const int longstatus) /* I - 1 if long report desired */
|
||
{
|
||
ipp_t *request, /* IPP Request */
|
||
*response; /* IPP Response */
|
||
ipp_attribute_t *attr; /* Current attribute */
|
||
const char *jobdest, /* Pointer into job-printer-uri */
|
||
*jobuser, /* Pointer to job-originating-user-name */
|
||
*jobname; /* Pointer to job-name */
|
||
ipp_jstate_t jobstate; /* job-state */
|
||
int jobid, /* job-id */
|
||
jobsize, /* job-k-octets */
|
||
jobcount, /* Number of jobs */
|
||
jobcopies, /* Number of copies */
|
||
rank; /* Rank of job */
|
||
char resource[1024]; /* Resource string */
|
||
char rankstr[255]; /* Rank string */
|
||
char namestr[1024]; /* Job name string */
|
||
static const char * const jobattrs[] =/* Job attributes we want to see */
|
||
{
|
||
"copies",
|
||
"job-id",
|
||
"job-k-octets",
|
||
"job-name",
|
||
"job-originating-user-name",
|
||
"job-printer-uri",
|
||
"job-priority",
|
||
"job-state"
|
||
};
|
||
static const char * const ranks[10] = /* Ranking strings */
|
||
{
|
||
"th",
|
||
"st",
|
||
"nd",
|
||
"rd",
|
||
"th",
|
||
"th",
|
||
"th",
|
||
"th",
|
||
"th",
|
||
"th"
|
||
};
|
||
|
||
|
||
if (http == NULL)
|
||
return (0);
|
||
|
||
/*
|
||
* Build an IPP_GET_JOBS or IPP_GET_JOB_ATTRIBUTES request, which requires
|
||
* the following attributes:
|
||
*
|
||
* attributes-charset
|
||
* attributes-natural-language
|
||
* job-uri or printer-uri
|
||
* requested-attributes
|
||
* requesting-user-name
|
||
*/
|
||
|
||
request = ippNewRequest(id ? IPP_GET_JOB_ATTRIBUTES : IPP_GET_JOBS);
|
||
|
||
if (id)
|
||
{
|
||
snprintf(resource, sizeof(resource), "ipp://localhost/jobs/%d", id);
|
||
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "job-uri",
|
||
NULL, resource);
|
||
}
|
||
else if (dest)
|
||
{
|
||
httpAssembleURIf(HTTP_URI_CODING_ALL, resource, sizeof(resource), "ipp",
|
||
NULL, "localhost", 0, "/printers/%s", dest);
|
||
|
||
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
|
||
NULL, resource);
|
||
}
|
||
else
|
||
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI, "printer-uri",
|
||
NULL, "ipp://localhost/");
|
||
|
||
if (user)
|
||
{
|
||
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
|
||
"requesting-user-name", NULL, user);
|
||
ippAddBoolean(request, IPP_TAG_OPERATION, "my-jobs", 1);
|
||
}
|
||
else
|
||
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_NAME,
|
||
"requesting-user-name", NULL, cupsUser());
|
||
|
||
ippAddStrings(request, IPP_TAG_OPERATION, IPP_TAG_KEYWORD,
|
||
"requested-attributes",
|
||
(int)(sizeof(jobattrs) / sizeof(jobattrs[0])), NULL, jobattrs);
|
||
|
||
/*
|
||
* Do the request and get back a response...
|
||
*/
|
||
|
||
jobcount = 0;
|
||
|
||
if ((response = cupsDoRequest(http, request, "/")) != NULL)
|
||
{
|
||
if (response->request.status.status_code > IPP_OK_CONFLICT)
|
||
{
|
||
_cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
|
||
ippDelete(response);
|
||
return (0);
|
||
}
|
||
|
||
rank = 1;
|
||
|
||
/*
|
||
* Loop through the job list and display them...
|
||
*/
|
||
|
||
for (attr = response->attrs; attr != NULL; attr = attr->next)
|
||
{
|
||
/*
|
||
* Skip leading attributes until we hit a job...
|
||
*/
|
||
|
||
while (attr != NULL && attr->group_tag != IPP_TAG_JOB)
|
||
attr = attr->next;
|
||
|
||
if (attr == NULL)
|
||
break;
|
||
|
||
/*
|
||
* Pull the needed attributes from this job...
|
||
*/
|
||
|
||
jobid = 0;
|
||
jobsize = 0;
|
||
jobstate = IPP_JOB_PENDING;
|
||
jobname = "unknown";
|
||
jobuser = "unknown";
|
||
jobdest = NULL;
|
||
jobcopies = 1;
|
||
|
||
while (attr != NULL && attr->group_tag == IPP_TAG_JOB)
|
||
{
|
||
if (!strcmp(attr->name, "job-id") &&
|
||
attr->value_tag == IPP_TAG_INTEGER)
|
||
jobid = attr->values[0].integer;
|
||
|
||
if (!strcmp(attr->name, "job-k-octets") &&
|
||
attr->value_tag == IPP_TAG_INTEGER)
|
||
jobsize = attr->values[0].integer;
|
||
|
||
if (!strcmp(attr->name, "job-state") &&
|
||
attr->value_tag == IPP_TAG_ENUM)
|
||
jobstate = (ipp_jstate_t)attr->values[0].integer;
|
||
|
||
if (!strcmp(attr->name, "job-printer-uri") &&
|
||
attr->value_tag == IPP_TAG_URI)
|
||
if ((jobdest = strrchr(attr->values[0].string.text, '/')) != NULL)
|
||
jobdest ++;
|
||
|
||
if (!strcmp(attr->name, "job-originating-user-name") &&
|
||
attr->value_tag == IPP_TAG_NAME)
|
||
jobuser = attr->values[0].string.text;
|
||
|
||
if (!strcmp(attr->name, "job-name") &&
|
||
attr->value_tag == IPP_TAG_NAME)
|
||
jobname = attr->values[0].string.text;
|
||
|
||
if (!strcmp(attr->name, "copies") &&
|
||
attr->value_tag == IPP_TAG_INTEGER)
|
||
jobcopies = attr->values[0].integer;
|
||
|
||
attr = attr->next;
|
||
}
|
||
|
||
/*
|
||
* See if we have everything needed...
|
||
*/
|
||
|
||
if (jobdest == NULL || jobid == 0)
|
||
{
|
||
if (attr == NULL)
|
||
break;
|
||
else
|
||
continue;
|
||
}
|
||
|
||
if (!longstatus && jobcount == 0)
|
||
_cupsLangPuts(stdout,
|
||
_("Rank Owner Job File(s)"
|
||
" Total Size"));
|
||
|
||
jobcount ++;
|
||
|
||
/*
|
||
* Display the job...
|
||
*/
|
||
|
||
if (jobstate == IPP_JOB_PROCESSING)
|
||
strlcpy(rankstr, "active", sizeof(rankstr));
|
||
else
|
||
{
|
||
/*
|
||
* Make the rank show the "correct" suffix for each number
|
||
* (11-13 are the only special cases, for English anyways...)
|
||
*/
|
||
|
||
if ((rank % 100) >= 11 && (rank % 100) <= 13)
|
||
snprintf(rankstr, sizeof(rankstr), "%dth", rank);
|
||
else
|
||
snprintf(rankstr, sizeof(rankstr), "%d%s", rank, ranks[rank % 10]);
|
||
|
||
rank ++;
|
||
}
|
||
|
||
if (longstatus)
|
||
{
|
||
_cupsLangPuts(stdout, "\n");
|
||
|
||
if (jobcopies > 1)
|
||
snprintf(namestr, sizeof(namestr), "%d copies of %s", jobcopies,
|
||
jobname);
|
||
else
|
||
strlcpy(namestr, jobname, sizeof(namestr));
|
||
|
||
_cupsLangPrintf(stdout, _("%s: %-33.33s [job %d localhost]"),
|
||
jobuser, rankstr, jobid);
|
||
_cupsLangPrintf(stdout, _(" %-39.39s %.0f bytes"),
|
||
namestr, 1024.0 * jobsize);
|
||
}
|
||
else
|
||
_cupsLangPrintf(stdout,
|
||
_("%-7s %-7.7s %-7d %-31.31s %.0f bytes"),
|
||
rankstr, jobuser, jobid, jobname, 1024.0 * jobsize);
|
||
|
||
if (attr == NULL)
|
||
break;
|
||
}
|
||
|
||
ippDelete(response);
|
||
}
|
||
else
|
||
{
|
||
_cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
|
||
return (0);
|
||
}
|
||
|
||
if (jobcount == 0)
|
||
_cupsLangPuts(stdout, _("no entries"));
|
||
|
||
return (jobcount);
|
||
}
|
||
|
||
|
||
/*
|
||
* 'show_printer()' - Show printer status.
|
||
*/
|
||
|
||
static void
|
||
show_printer(const char *command, /* I - Command name */
|
||
http_t *http, /* I - HTTP connection to server */
|
||
const char *dest) /* I - Destination */
|
||
{
|
||
ipp_t *request, /* IPP Request */
|
||
*response; /* IPP Response */
|
||
ipp_attribute_t *attr; /* Current attribute */
|
||
ipp_pstate_t state; /* Printer state */
|
||
char uri[HTTP_MAX_URI]; /* Printer URI */
|
||
|
||
|
||
if (http == NULL)
|
||
return;
|
||
|
||
/*
|
||
* Build an IPP_GET_PRINTER_ATTRIBUTES request, which requires the following
|
||
* attributes:
|
||
*
|
||
* attributes-charset
|
||
* attributes-natural-language
|
||
* printer-uri
|
||
*/
|
||
|
||
request = ippNewRequest(IPP_GET_PRINTER_ATTRIBUTES);
|
||
|
||
httpAssembleURIf(HTTP_URI_CODING_ALL, uri, sizeof(uri), "ipp", NULL,
|
||
"localhost", 0, "/printers/%s", dest);
|
||
ippAddString(request, IPP_TAG_OPERATION, IPP_TAG_URI,
|
||
"printer-uri", NULL, uri);
|
||
|
||
/*
|
||
* Do the request and get back a response...
|
||
*/
|
||
|
||
if ((response = cupsDoRequest(http, request, "/")) != NULL)
|
||
{
|
||
if (response->request.status.status_code > IPP_OK_CONFLICT)
|
||
{
|
||
_cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
|
||
ippDelete(response);
|
||
return;
|
||
}
|
||
|
||
if ((attr = ippFindAttribute(response, "printer-state", IPP_TAG_ENUM)) != NULL)
|
||
state = (ipp_pstate_t)attr->values[0].integer;
|
||
else
|
||
state = IPP_PRINTER_STOPPED;
|
||
|
||
switch (state)
|
||
{
|
||
case IPP_PRINTER_IDLE :
|
||
_cupsLangPrintf(stdout, _("%s is ready"), dest);
|
||
break;
|
||
case IPP_PRINTER_PROCESSING :
|
||
_cupsLangPrintf(stdout, _("%s is ready and printing"),
|
||
dest);
|
||
break;
|
||
case IPP_PRINTER_STOPPED :
|
||
_cupsLangPrintf(stdout, _("%s is not ready"), dest);
|
||
break;
|
||
}
|
||
|
||
ippDelete(response);
|
||
}
|
||
else
|
||
_cupsLangPrintf(stderr, "%s: %s", command, cupsLastErrorString());
|
||
}
|
||
|
||
|
||
/*
|
||
* 'usage()' - Show program usage.
|
||
*/
|
||
|
||
static void
|
||
usage(void)
|
||
{
|
||
_cupsLangPuts(stderr, _("Usage: lpq [options] [+interval]"));
|
||
_cupsLangPuts(stdout, _("Options:"));
|
||
_cupsLangPuts(stdout, _("-a Show jobs on all destinations"));
|
||
_cupsLangPuts(stdout, _("-E Encrypt the connection to the server"));
|
||
_cupsLangPuts(stdout, _("-h server[:port] Connect to the named server and port"));
|
||
_cupsLangPuts(stdout, _("-l Show verbose (long) output"));
|
||
_cupsLangPuts(stdout, _("-P destination Show status for the specified destination"));
|
||
_cupsLangPuts(stdout, _("-U username Specify the username to use for authentication"));
|
||
|
||
exit(1);
|
||
}
|