4 * The contents of this file are subject to the terms of the
5 * Common Development and Distribution License (the "License").
6 * You may not use this file except in compliance with the License.
8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9 * or http://www.opensolaris.org/os/licensing.
10 * See the License for the specific language governing permissions
11 * and limitations under the License.
13 * When distributing Covered Code, include this CDDL HEADER in each
14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15 * If applicable, add the following below this CDDL HEADER, with the
16 * fields enclosed by brackets "[]" replaced with your own identifying
17 * information: Portions Copyright [yyyy] [name of copyright owner]
23 * Copyright (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved.
26 /* $Id: lpd-query.c 155 2006-04-26 02:34:54Z ktou $ */
31 #include <sys/types.h>
33 #include <sys/fcntl.h>
40 #include <papi_impl.h>
42 /* The string is modified by this call */
44 regvalue(regmatch_t match
, char *string
)
48 if (match
.rm_so
!= match
.rm_eo
) {
49 result
= string
+ match
.rm_so
;
50 *(result
+ (match
.rm_eo
- match
.rm_so
)) = '\0';
57 * Print job entries start with:
58 * (user): (rank) [job (number) (...)]
59 * (user) is the job-owner's user name
60 * (rank) is the rank in queue. (active, 1st, 2nd, ...)
61 * (number) is the job number
62 * (...) is an optional hostname
63 * some servers will use whitespace a little differently than is displayed
64 * above. The regular expression below makes whitespace optional in some
67 static char *job_expr
= "^(.*[[:alnum:]]):[[:space:]]+([[:alnum:]]+)"\
68 "[[:space:]]+[[][[:space:]]*job[[:space:]]*([[:digit:]]+)"\
70 static regex_t job_re
;
73 * Print job entries for remote windows printer start with:
74 * Owner Status Jobname Job-Id Size Pages Priority
76 * Owner Status Jobname Job-Id Size Pages Priority
77 * ------------------------------------------------------------
78 * root (10.3. Waiting /etc/release 2 240 1 4
80 * Owner is the job-owner's user name
81 * Status is the job-status (printing, waiting, error)
82 * Jobname is the name of the job to be printed
83 * Job-Id is the id of the job queued to be printed
84 * Size is the size of the job in bytes
85 * Pages is the number of pages of the job
86 * Priority is the job-priority
88 static char *wjob_expr
= "^([[:alnum:]]+)[[:space:]]*[(](.*)[)]*[[:space:]]"\
89 "+([[:alnum:]]+)[[:space:]]+(.*)([[:alnum:]]+)(.*)[[:space:]]+"\
90 "([[:digit:]]+)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)"\
91 "[[:space:]]+([[:digit:]]+)";
92 static regex_t wjob_re
;
95 * Windows job header is in the following format
96 * Owner Status Jobname Job-Id Size Pages Priority
97 * --------------------------------------------------------------
99 static char *whjob_expr
= "Owner Status Jobname Job-Id"\
100 " Size Pages Priority";
101 static regex_t whjob_re
;
103 static char *wline_expr
= "----------";
104 static regex_t wline_re
;
107 * status line(s) for "processing" printers will contain one of the following:
112 static char *proc_expr
= "(ready and printing|printing|processing)";
113 static regex_t proc_re
;
116 * status line(s) for "idle" printers will contain one of the following:
121 static char *idle_expr
= "(no entries|is ready| idle)";
122 static regex_t idle_re
;
125 * Printer state reason (For Windows remote printers)
128 static char *state_reason_expr
= "(Paused)";
129 static regex_t state_reason_re
;
133 * (copies) copies of (name) (size) bytes
134 * (name) (size) bytes
135 * document lines can be in either format above.
136 * (copies) is the number of copies of the document to print
137 * (name) is the name of the document: /etc/motd, ...
138 * (size) is the number of bytes in the document data
140 static char *doc1_expr
= "[[:space:]]+(([[:digit:]]+) copies of )"\
141 "([^[:space:]]+)[[:space:]]*([[:digit:]]+) bytes";
142 static char *doc2_expr
= "[[:space:]]+()([^[:space:]]+)[[:space:]]*"\
143 "([[:digit:]]+) bytes";
144 static regex_t doc1_re
;
145 static regex_t doc2_re
;
147 /* Printer-state for Windows */
148 static int win_state
= 0x03; /* Idle */
151 parse_lpd_job(service_t
*svc
, job_t
**job
, int fd
, char *line
, int len
)
153 papi_attribute_t
**attributes
= NULL
;
154 regmatch_t matches
[10];
160 * job_re and wjob_re were compiled in the calling function
161 * first check for solaris jobs
162 * if there is no-match check for windows jobs
165 if (regexec(&job_re
, line
, (size_t)5, matches
, 0) == REG_NOMATCH
) {
166 if (regexec(&wjob_re
, line
, (size_t)10, matches
, 0)
175 /* first match is job-id */
177 if ((s
= regvalue(matches
[1], line
)) == NULL
)
179 papiAttributeListAddString(&attributes
, PAPI_ATTR_REPLACE
,
180 "job-originating-user-name", s
);
182 if ((s
= regvalue(matches
[4], line
)) == NULL
)
184 papiAttributeListAddString(&attributes
, PAPI_ATTR_APPEND
,
186 papiAttributeListAddString(&attributes
, PAPI_ATTR_APPEND
,
187 "job-file-names", s
);
189 if ((s
= regvalue(matches
[7], line
)) == NULL
)
191 papiAttributeListAddInteger(&attributes
, PAPI_ATTR_REPLACE
,
194 if ((s
= regvalue(matches
[8], line
)) == NULL
)
197 papiAttributeListAddInteger(&attributes
,
198 PAPI_ATTR_APPEND
, "job-file-sizes", atoi(s
));
201 * Since a job has been found so the printer state is either
202 * 'stopped' or 'processing'
203 * By default it is "processing"
208 if ((s
= regvalue(matches
[1], line
)) == NULL
)
210 papiAttributeListAddString(&attributes
, PAPI_ATTR_REPLACE
,
211 "job-originating-user-name", s
);
213 if ((s
= regvalue(matches
[2], line
)) == NULL
)
215 papiAttributeListAddInteger(&attributes
, PAPI_ATTR_REPLACE
,
216 "number-of-intervening-jobs", atoi(s
) - 1);
218 if ((s
= regvalue(matches
[3], line
)) == NULL
)
220 papiAttributeListAddInteger(&attributes
, PAPI_ATTR_REPLACE
,
223 if ((s
= regvalue(matches
[4], line
)) == NULL
)
225 papiAttributeListAddString(&attributes
, PAPI_ATTR_REPLACE
,
226 "job-originating-host-name", s
);
229 while ((fdgets(line
, len
, fd
) != NULL
) &&
230 (regexec(&job_re
, line
, (size_t)0, NULL
, 0) == REG_NOMATCH
) &&
231 (regexec(&wjob_re
, line
, (size_t)0, NULL
, 0) == REG_NOMATCH
)) {
232 int size
= 0, copies
= 1;
233 /* process copies/documents */
235 /* doc1_re and doc2_re were compiled in the calling function */
236 if ((regexec(&doc1_re
, line
, (size_t)4, matches
, 0) != 0) &&
237 (regexec(&doc2_re
, line
, (size_t)4, matches
, 0) != 0))
240 if ((s
= regvalue(matches
[1], line
)) == NULL
)
242 if ((copies
= atoi(s
)) < 1)
245 if ((s
= regvalue(matches
[2], line
)) == NULL
)
247 papiAttributeListAddString(&attributes
,
248 PAPI_ATTR_APPEND
, "job-name", s
);
249 papiAttributeListAddString(&attributes
,
250 PAPI_ATTR_APPEND
, "job-file-names", s
);
252 if ((s
= regvalue(matches
[3], line
)) == NULL
)
256 papiAttributeListAddInteger(&attributes
,
257 PAPI_ATTR_APPEND
, "job-file-sizes", size
);
259 octets
+= (size
* copies
);
262 papiAttributeListAddInteger(&attributes
, PAPI_ATTR_APPEND
,
263 "job-k-octets", octets
/1024);
264 papiAttributeListAddInteger(&attributes
, PAPI_ATTR_APPEND
,
265 "job-octets", octets
);
266 papiAttributeListAddString(&attributes
, PAPI_ATTR_APPEND
,
267 "printer-name", queue_name_from_uri(svc
->uri
));
269 if ((*job
= (job_t
*)calloc(1, sizeof (**job
))) != NULL
)
270 (*job
)->attributes
= attributes
;
274 parse_lpd_query(service_t
*svc
, int fd
)
276 papi_attribute_t
**attributes
= NULL
;
277 cache_t
*cache
= NULL
;
278 int state
= 0x03; /* idle */
284 papiAttributeListAddString(&attributes
, PAPI_ATTR_APPEND
,
285 "printer-name", queue_name_from_uri(svc
->uri
));
287 if (uri_to_string(svc
->uri
, status
, sizeof (status
)) == 0)
288 papiAttributeListAddString(&attributes
, PAPI_ATTR_APPEND
,
289 "printer-uri-supported", status
);
292 * on most systems, status is a single line, but some appear to
293 * return multi-line status messages. To get the "best" possible
294 * printer-state-reason, we accumulate the text until we hit the
295 * first print job entry.
297 * Print job entries start with:
298 * user: rank [job number ...]
300 (void) regcomp(&job_re
, job_expr
, REG_EXTENDED
|REG_ICASE
);
303 * For remote windows printers
304 * Print job entries start with:
305 * Owner Status Jobname Job-Id Size Pages Priority
307 (void) regcomp(&wjob_re
, wjob_expr
, REG_EXTENDED
|REG_ICASE
);
308 (void) regcomp(&whjob_re
, whjob_expr
, REG_EXTENDED
|REG_ICASE
);
309 (void) regcomp(&wline_re
, wline_expr
, REG_EXTENDED
|REG_ICASE
);
313 while ((fdgets(line
, sizeof (line
), fd
) != NULL
) &&
314 (regexec(&job_re
, line
, (size_t)0, NULL
, 0) == REG_NOMATCH
) &&
315 (regexec(&wjob_re
, line
, (size_t)0, NULL
, 0) == REG_NOMATCH
)) {
317 * When windows job queue gets queried following header
318 * should not get printed
319 * Owner Status Jobname Job-Id Size Pages Priority
320 * -----------------------------------------------
322 if ((regexec(&whjob_re
, line
, (size_t)0, NULL
, 0)
324 (regexec(&wline_re
, line
, (size_t)0, NULL
, 0)
326 strlcat(status
, line
, sizeof (status
));
329 /* chop off trailing whitespace */
330 s
= status
+ strlen(status
) - 1;
331 while ((s
> status
) && (isspace(*s
) != 0))
334 papiAttributeListAddString(&attributes
, PAPI_ATTR_REPLACE
,
335 "printer-state-reasons", status
);
337 /* Check if this is for Windows remote printers */
338 if (strstr(status
, "Windows")) {
340 * It is a remote windows printer
341 * By default set the status as idle
342 * Set the printer-state after call to "parse_lpd_job"
345 (void) regcomp(&state_reason_re
, state_reason_expr
,
346 REG_EXTENDED
|REG_ICASE
);
348 if (regexec(&state_reason_re
, status
, (size_t)0, NULL
, 0) == 0)
349 state
= 0x05; /* stopped */
351 (void) regcomp(&proc_re
, proc_expr
, REG_EXTENDED
|REG_ICASE
);
352 (void) regcomp(&idle_re
, idle_expr
, REG_EXTENDED
|REG_ICASE
);
354 if (regexec(&proc_re
, status
, (size_t)0, NULL
, 0) == 0)
355 state
= 0x04; /* processing */
356 else if (regexec(&idle_re
, status
, (size_t)0, NULL
, 0) == 0)
357 state
= 0x03; /* idle */
359 state
= 0x05; /* stopped */
360 papiAttributeListAddInteger(&attributes
, PAPI_ATTR_REPLACE
,
361 "printer-state", state
);
364 if ((cache
= (cache_t
*)calloc(1, sizeof (*cache
))) == NULL
)
367 if ((cache
->printer
= (printer_t
*)calloc(1, sizeof (*cache
->printer
)))
371 cache
->printer
->attributes
= attributes
;
374 (void) regcomp(&doc1_re
, doc1_expr
, REG_EXTENDED
|REG_ICASE
);
375 (void) regcomp(&doc2_re
, doc2_expr
, REG_EXTENDED
|REG_ICASE
);
376 /* process job related entries */
377 while (line
[0] != '\0') {
380 parse_lpd_job(svc
, &job
, fd
, line
, sizeof (line
));
383 list_append(&cache
->jobs
, job
);
387 * For remote windows printer set the printer-state
388 * after parse_lpd_job
394 papiAttributeListAddInteger(&attributes
, PAPI_ATTR_REPLACE
,
395 "printer-state", win_state
);
397 time(&cache
->timestamp
);
401 cache_update(service_t
*svc
)
408 if (svc
->cache
!= NULL
) { /* this should be time based */
409 if (svc
->cache
->jobs
== NULL
) {
416 if ((fd
= lpd_open(svc
, 'q', NULL
, 15)) < 0)
419 parse_lpd_query(svc
, fd
);
425 lpd_find_printer_info(service_t
*svc
, printer_t
**printer
)
427 papi_status_t result
= PAPI_BAD_ARGUMENT
;
429 if ((svc
== NULL
) || (printer
== NULL
))
430 return (PAPI_BAD_ARGUMENT
);
434 if (svc
->cache
!= NULL
) {
435 *printer
= svc
->cache
->printer
;
438 result
= PAPI_NOT_FOUND
;
444 lpd_find_jobs_info(service_t
*svc
, job_t
***jobs
)
446 papi_status_t result
= PAPI_BAD_ARGUMENT
;
451 if (svc
->cache
!= NULL
) {
452 *jobs
= svc
->cache
->jobs
;
458 * cache jobs is free()-ed in
459 * libpapi-dynamic/common/printer.c -
460 * papiPrinterListJobs() cache printer is
461 * free()-ed by the caller of
462 * lpd_find_printer_info Invalidate the
463 * cache by freeing the cache.
472 lpd_find_job_info(service_t
*svc
, int job_id
, job_t
**job
)
474 papi_status_t result
= PAPI_BAD_ARGUMENT
;
477 if ((lpd_find_jobs_info(svc
, &jobs
) == PAPI_OK
) && (jobs
!= NULL
)) {
481 for (i
= 0; ((*job
== NULL
) && (jobs
[i
] != NULL
)); i
++) {
484 papiAttributeListGetInteger(jobs
[i
]->attributes
, NULL
,
498 cache_free(cache_t
*item
)
501 if (item
->printer
!= NULL
)
502 papiPrinterFree((papi_printer_t
*)item
->printer
);
503 if (item
->jobs
!= NULL
)
504 papiJobListFree((papi_job_t
*)item
->jobs
);