12264Sjacobs /* 22264Sjacobs * CDDL HEADER START 32264Sjacobs * 42264Sjacobs * The contents of this file are subject to the terms of the 52264Sjacobs * Common Development and Distribution License (the "License"). 62264Sjacobs * You may not use this file except in compliance with the License. 72264Sjacobs * 82264Sjacobs * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 92264Sjacobs * or http://www.opensolaris.org/os/licensing. 102264Sjacobs * See the License for the specific language governing permissions 112264Sjacobs * and limitations under the License. 122264Sjacobs * 132264Sjacobs * When distributing Covered Code, include this CDDL HEADER in each 142264Sjacobs * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 152264Sjacobs * If applicable, add the following below this CDDL HEADER, with the 162264Sjacobs * fields enclosed by brackets "[]" replaced with your own identifying 172264Sjacobs * information: Portions Copyright [yyyy] [name of copyright owner] 182264Sjacobs * 192264Sjacobs * CDDL HEADER END 202264Sjacobs */ 212264Sjacobs 222264Sjacobs /* 23*3758Sjacobs * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 242264Sjacobs * Use is subject to license terms. 252264Sjacobs * 262264Sjacobs */ 272264Sjacobs 282264Sjacobs /* $Id: lpd-query.c 155 2006-04-26 02:34:54Z ktou $ */ 292264Sjacobs 302264Sjacobs #pragma ident "%Z%%M% %I% %E% SMI" 312264Sjacobs 322264Sjacobs #include <stdio.h> 332264Sjacobs #include <stdlib.h> 342264Sjacobs #include <unistd.h> 352264Sjacobs #include <sys/types.h> 362264Sjacobs #include <sys/stat.h> 372264Sjacobs #include <sys/fcntl.h> 382264Sjacobs #include <time.h> 392264Sjacobs #include <ctype.h> 402264Sjacobs #include <string.h> 412264Sjacobs #include <stdarg.h> 42*3758Sjacobs #include <regex.h> 432264Sjacobs 442264Sjacobs #include <papi_impl.h> 452264Sjacobs 46*3758Sjacobs /* The string is modified by this call */ 47*3758Sjacobs static char * 48*3758Sjacobs regvalue(regmatch_t match, char *string) 492264Sjacobs { 50*3758Sjacobs char *result = NULL; 51*3758Sjacobs 52*3758Sjacobs if (match.rm_so != match.rm_eo) { 53*3758Sjacobs result = string + match.rm_so; 54*3758Sjacobs *(result + (match.rm_eo - match.rm_so)) = '\0'; 55*3758Sjacobs } 56*3758Sjacobs 57*3758Sjacobs return (result); 58*3758Sjacobs } 59*3758Sjacobs 60*3758Sjacobs /* 61*3758Sjacobs * Print job entries start with: 62*3758Sjacobs * (user): (rank) [job (number) (...)] 63*3758Sjacobs * (user) is the job-owner's user name 64*3758Sjacobs * (rank) is the rank in queue. (active, 1st, 2nd, ...) 65*3758Sjacobs * (number) is the job number 66*3758Sjacobs * (...) is an optional hostname 67*3758Sjacobs * some servers will use whitespace a little differently than is displayed 68*3758Sjacobs * above. The regular expression below makes whitespace optional in some 69*3758Sjacobs * places. 70*3758Sjacobs */ 71*3758Sjacobs static char *job_expr = "^(.*[[:alnum:]]):[[:space:]]+([[:alnum:]]+)[[:space:]]+[[][[:space:]]*job[[:space:]]*([[:digit:]]+)[[:space:]]*(.*)]"; 72*3758Sjacobs static regex_t job_re; 73*3758Sjacobs 74*3758Sjacobs /* 75*3758Sjacobs * status line(s) for "processing" printers will contain one of the following: 76*3758Sjacobs * ready and printing 77*3758Sjacobs * Printing 78*3758Sjacobs */ 79*3758Sjacobs static char *proc_expr = "(ready and printing|printing)"; 80*3758Sjacobs static regex_t proc_re; 81*3758Sjacobs 82*3758Sjacobs /* 83*3758Sjacobs * status line(s) for "idle" printers will contain one of the following: 84*3758Sjacobs * no entries 85*3758Sjacobs * (printer) is ready 86*3758Sjacobs * idle 87*3758Sjacobs */ 88*3758Sjacobs static char *idle_expr = "(no entries|is ready| idle)"; 89*3758Sjacobs static regex_t idle_re; 90*3758Sjacobs 91*3758Sjacobs /* 92*3758Sjacobs * document line(s) 93*3758Sjacobs * (copies) copies of (name) (size) bytes 94*3758Sjacobs * (name) (size) bytes 95*3758Sjacobs * document lines can be in either format above. 96*3758Sjacobs * (copies) is the number of copies of the document to print 97*3758Sjacobs * (name) is the name of the document: /etc/motd, ... 98*3758Sjacobs * (size) is the number of bytes in the document data 99*3758Sjacobs */ 100*3758Sjacobs static char *doc1_expr = "[[:space:]]+(([[:digit:]]+) copies of )([^[:space:]]+)[[:space:]]*([[:digit:]]+) bytes"; 101*3758Sjacobs static char *doc2_expr = "[[:space:]]+()([^[:space:]]+)[[:space:]]*([[:digit:]]+) bytes"; 102*3758Sjacobs static regex_t doc1_re; 103*3758Sjacobs static regex_t doc2_re; 104*3758Sjacobs 105*3758Sjacobs static void 106*3758Sjacobs parse_lpd_job(service_t *svc, job_t **job, int fd, char *line, int len) 107*3758Sjacobs { 1082264Sjacobs papi_attribute_t **attributes = NULL; 109*3758Sjacobs regmatch_t matches[5]; 110*3758Sjacobs char *s; 1112264Sjacobs int octets = 0; 1122264Sjacobs 113*3758Sjacobs /* job_re was compiled in the calling function */ 114*3758Sjacobs if (regexec(&job_re, line, (size_t)5, matches, 0) == REG_NOMATCH) 1152264Sjacobs return; 116*3758Sjacobs 117*3758Sjacobs if ((s = regvalue(matches[1], line)) == NULL) 118*3758Sjacobs s = "nobody"; 1192264Sjacobs papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 120*3758Sjacobs "job-originating-user-name", s); 1212264Sjacobs 122*3758Sjacobs if ((s = regvalue(matches[2], line)) == NULL) 123*3758Sjacobs s = "0"; 1242264Sjacobs papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 125*3758Sjacobs "number-of-intervening-jobs", atoi(s) - 1); 126*3758Sjacobs 127*3758Sjacobs if ((s = regvalue(matches[3], line)) == NULL) 128*3758Sjacobs s = "0"; 1292264Sjacobs papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 130*3758Sjacobs "job-id", atoi(s)); 131*3758Sjacobs 132*3758Sjacobs if ((s = regvalue(matches[4], line)) == NULL) 133*3758Sjacobs s = svc->uri->host; 1342264Sjacobs papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 135*3758Sjacobs "job-originating-host-name", s); 1362264Sjacobs 137*3758Sjacobs while ((fdgets(line, len, fd) != NULL) && 138*3758Sjacobs (regexec(&job_re, line, (size_t)0, NULL, 0) == REG_NOMATCH)) { 139*3758Sjacobs int size = 0, copies = 1; 140*3758Sjacobs /* process copies/documents */ 1412264Sjacobs 142*3758Sjacobs /* doc1_re and doc2_re were compiled in the calling function */ 143*3758Sjacobs if ((regexec(&doc1_re, line, (size_t)4, matches, 0) != 0) && 144*3758Sjacobs (regexec(&doc2_re, line, (size_t)4, matches, 0) != 0)) 145*3758Sjacobs continue; 1462264Sjacobs 147*3758Sjacobs if ((s = regvalue(matches[1], line)) == NULL) 148*3758Sjacobs s = "1"; 149*3758Sjacobs if ((copies = atoi(s)) < 1) 150*3758Sjacobs copies = 1; 1512264Sjacobs 152*3758Sjacobs if ((s = regvalue(matches[2], line)) == NULL) 153*3758Sjacobs s = "unknown"; 154*3758Sjacobs papiAttributeListAddString(&attributes, 155*3758Sjacobs PAPI_ATTR_APPEND, "job-name", s); 156*3758Sjacobs papiAttributeListAddString(&attributes, 157*3758Sjacobs PAPI_ATTR_APPEND, "job-file-names", s); 1582264Sjacobs 159*3758Sjacobs if ((s = regvalue(matches[3], line)) == NULL) 160*3758Sjacobs s = "0"; 161*3758Sjacobs size = atoi(s); 162*3758Sjacobs papiAttributeListAddInteger(&attributes, 1632264Sjacobs PAPI_ATTR_APPEND, "job-file-sizes", size); 1642264Sjacobs 165*3758Sjacobs octets += (size * copies); 1662264Sjacobs } 1672264Sjacobs 1682264Sjacobs papiAttributeListAddInteger(&attributes, PAPI_ATTR_APPEND, 1692264Sjacobs "job-k-octets", octets/1024); 1702264Sjacobs papiAttributeListAddInteger(&attributes, PAPI_ATTR_APPEND, 1712264Sjacobs "job-octets", octets); 1722264Sjacobs papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 1732264Sjacobs "printer-name", queue_name_from_uri(svc->uri)); 1742264Sjacobs 1752264Sjacobs if ((*job = (job_t *)calloc(1, sizeof (**job))) != NULL) 1762264Sjacobs (*job)->attributes = attributes; 1772264Sjacobs } 1782264Sjacobs 1792264Sjacobs void 1802264Sjacobs parse_lpd_query(service_t *svc, int fd) 1812264Sjacobs { 1822264Sjacobs papi_attribute_t **attributes = NULL; 1832264Sjacobs cache_t *cache = NULL; 1842264Sjacobs int state = 0x03; /* idle */ 1852264Sjacobs char line[128]; 186*3758Sjacobs char status[1024]; 187*3758Sjacobs char *s; 1882264Sjacobs 1892264Sjacobs papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 1902264Sjacobs "printer-name", queue_name_from_uri(svc->uri)); 1912264Sjacobs 192*3758Sjacobs if (uri_to_string(svc->uri, status, sizeof (status)) == 0) 1932264Sjacobs papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 194*3758Sjacobs "printer-uri-supported", status); 195*3758Sjacobs 196*3758Sjacobs /* 197*3758Sjacobs * on most systems, status is a single line, but some appear to 198*3758Sjacobs * return multi-line status messages. To get the "best" possible 199*3758Sjacobs * printer-state-reason, we accumulate the text until we hit the 200*3758Sjacobs * first print job entry. 201*3758Sjacobs * 202*3758Sjacobs * Print job entries start with: 203*3758Sjacobs * user: rank [job number ...] 204*3758Sjacobs */ 205*3758Sjacobs (void) regcomp(&job_re, job_expr, REG_EXTENDED|REG_ICASE); 206*3758Sjacobs 207*3758Sjacobs status[0] = '\0'; 208*3758Sjacobs while ((fdgets(line, sizeof (line), fd) != NULL) && 209*3758Sjacobs (regexec(&job_re, line, (size_t)0, NULL, 0) == REG_NOMATCH)) { 210*3758Sjacobs strlcat(status, line, sizeof (status)); 211*3758Sjacobs } 212*3758Sjacobs /* chop off trailing whitespace */ 213*3758Sjacobs s = status + strlen(status) - 1; 214*3758Sjacobs while ((s > status) && (isspace(*s) != 0)) 215*3758Sjacobs *s-- = '\0'; 2162264Sjacobs 2172264Sjacobs papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 218*3758Sjacobs "printer-state-reasons", status); 2192264Sjacobs 220*3758Sjacobs (void) regcomp(&proc_re, proc_expr, REG_EXTENDED|REG_ICASE); 221*3758Sjacobs (void) regcomp(&idle_re, idle_expr, REG_EXTENDED|REG_ICASE); 222*3758Sjacobs if (regexec(&proc_re, status, (size_t)0, NULL, 0) == 0) 2232264Sjacobs state = 0x04; /* processing */ 224*3758Sjacobs else if (regexec(&idle_re, status, (size_t)0, NULL, 0) == 0) 2252264Sjacobs state = 0x03; /* idle */ 2262264Sjacobs else 2272264Sjacobs state = 0x05; /* stopped */ 2282264Sjacobs 2292264Sjacobs papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 2302264Sjacobs "printer-state", state); 2312264Sjacobs 2322264Sjacobs if ((cache = (cache_t *)calloc(1, sizeof (*cache))) == NULL) 2332264Sjacobs return; 2342264Sjacobs 2352264Sjacobs if ((cache->printer = (printer_t *)calloc(1, sizeof (*cache->printer))) 2362264Sjacobs == NULL) 2372264Sjacobs return; 2382264Sjacobs 2392264Sjacobs cache->printer->attributes = attributes; 2402264Sjacobs svc->cache = cache; 2412264Sjacobs 242*3758Sjacobs (void) regcomp(&doc1_re, doc1_expr, REG_EXTENDED|REG_ICASE); 243*3758Sjacobs (void) regcomp(&doc2_re, doc2_expr, REG_EXTENDED|REG_ICASE); 244*3758Sjacobs /* process job related entries */ 245*3758Sjacobs while (line[0] != '\0') { 246*3758Sjacobs job_t *job = NULL; 247*3758Sjacobs 248*3758Sjacobs parse_lpd_job(svc, &job, fd, line, sizeof (line)); 249*3758Sjacobs if (job == NULL) 250*3758Sjacobs break; 251*3758Sjacobs list_append(&cache->jobs, job); 2522264Sjacobs } 2532264Sjacobs 2542264Sjacobs time(&cache->timestamp); 2552264Sjacobs } 2562264Sjacobs 2572264Sjacobs void 2582264Sjacobs cache_update(service_t *svc) 2592264Sjacobs { 2602264Sjacobs int fd; 2612264Sjacobs 2622264Sjacobs if (svc->cache != NULL) /* this should be time based */ 2632264Sjacobs return; 2642264Sjacobs 2652264Sjacobs if (svc == NULL) 2662264Sjacobs return; 2672264Sjacobs 2682264Sjacobs if ((fd = lpd_open(svc, 'q', NULL, 3)) < 0) 2692264Sjacobs return; 2702264Sjacobs 2712264Sjacobs parse_lpd_query(svc, fd); 2722264Sjacobs 2732264Sjacobs close(fd); 2742264Sjacobs } 2752264Sjacobs 2762264Sjacobs papi_status_t 2772264Sjacobs lpd_find_printer_info(service_t *svc, printer_t **printer) 2782264Sjacobs { 2792264Sjacobs papi_status_t result = PAPI_BAD_ARGUMENT; 2802264Sjacobs 2812264Sjacobs if ((svc == NULL) || (printer == NULL)) 2822264Sjacobs return (PAPI_BAD_ARGUMENT); 2832264Sjacobs 2842264Sjacobs cache_update(svc); 2852264Sjacobs 2862264Sjacobs if (svc->cache != NULL) { 2872264Sjacobs *printer = svc->cache->printer; 2882264Sjacobs result = PAPI_OK; 2892264Sjacobs } else 2902264Sjacobs result = PAPI_NOT_FOUND; 2912264Sjacobs 2922264Sjacobs return (result); 2932264Sjacobs } 2942264Sjacobs 2952264Sjacobs papi_status_t 2962264Sjacobs lpd_find_jobs_info(service_t *svc, job_t ***jobs) 2972264Sjacobs { 2982264Sjacobs papi_status_t result = PAPI_BAD_ARGUMENT; 2992264Sjacobs 3002264Sjacobs if (svc != NULL) { 3012264Sjacobs cache_update(svc); 3022264Sjacobs 3032264Sjacobs if (svc->cache != NULL) { 3042264Sjacobs *jobs = svc->cache->jobs; 3052264Sjacobs result = PAPI_OK; 3062264Sjacobs } 3072264Sjacobs } 3082264Sjacobs 3092264Sjacobs return (result); 3102264Sjacobs } 3112264Sjacobs 3122264Sjacobs papi_status_t 3132264Sjacobs lpd_find_job_info(service_t *svc, int job_id, job_t **job) 3142264Sjacobs { 3152264Sjacobs papi_status_t result = PAPI_BAD_ARGUMENT; 3162264Sjacobs job_t **jobs; 3172264Sjacobs 3182264Sjacobs if (lpd_find_jobs_info(svc, &jobs) != PAPI_OK) { 3192264Sjacobs int i; 3202264Sjacobs 3212264Sjacobs *job = NULL; 3222264Sjacobs for (i = 0; ((*job == NULL) && (jobs[i] != NULL)); i++) { 3232264Sjacobs int id = -1; 3242264Sjacobs 3252264Sjacobs papiAttributeListGetInteger(jobs[i]->attributes, NULL, 3262264Sjacobs "job-id", &id); 3272264Sjacobs if (id == job_id) 3282264Sjacobs *job = jobs[i]; 3292264Sjacobs } 3302264Sjacobs 3312264Sjacobs if (*job != NULL) 3322264Sjacobs result = PAPI_OK; 3332264Sjacobs } 3342264Sjacobs 3352264Sjacobs return (result); 3362264Sjacobs } 3372264Sjacobs 3382264Sjacobs void 3392264Sjacobs cache_free(cache_t *item) 3402264Sjacobs { 3412264Sjacobs if (item != NULL) { 3422264Sjacobs if (item->printer != NULL) 3432264Sjacobs papiPrinterFree((papi_printer_t *)item->printer); 3442264Sjacobs if (item->jobs != NULL) 3452264Sjacobs papiJobListFree((papi_job_t *)item->jobs); 3462264Sjacobs free(item); 3472264Sjacobs } 3482264Sjacobs } 349