1 /* 2 * CDDL HEADER START 3 * 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. 7 * 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. 12 * 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] 18 * 19 * CDDL HEADER END 20 */ 21 22 /* 23 * Copyright 2007 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 * 26 */ 27 28 /* $Id: lpd-query.c 155 2006-04-26 02:34:54Z ktou $ */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include <stdio.h> 33 #include <stdlib.h> 34 #include <unistd.h> 35 #include <sys/types.h> 36 #include <sys/stat.h> 37 #include <sys/fcntl.h> 38 #include <time.h> 39 #include <ctype.h> 40 #include <string.h> 41 #include <stdarg.h> 42 #include <regex.h> 43 44 #include <papi_impl.h> 45 46 /* The string is modified by this call */ 47 static char * 48 regvalue(regmatch_t match, char *string) 49 { 50 char *result = NULL; 51 52 if (match.rm_so != match.rm_eo) { 53 result = string + match.rm_so; 54 *(result + (match.rm_eo - match.rm_so)) = '\0'; 55 } 56 57 return (result); 58 } 59 60 /* 61 * Print job entries start with: 62 * (user): (rank) [job (number) (...)] 63 * (user) is the job-owner's user name 64 * (rank) is the rank in queue. (active, 1st, 2nd, ...) 65 * (number) is the job number 66 * (...) is an optional hostname 67 * some servers will use whitespace a little differently than is displayed 68 * above. The regular expression below makes whitespace optional in some 69 * places. 70 */ 71 static char *job_expr = "^(.*[[:alnum:]]):[[:space:]]+([[:alnum:]]+)[[:space:]]+[[][[:space:]]*job[[:space:]]*([[:digit:]]+)[[:space:]]*(.*)]"; 72 static regex_t job_re; 73 74 /* 75 * status line(s) for "processing" printers will contain one of the following: 76 * ready and printing 77 * Printing 78 */ 79 static char *proc_expr = "(ready and printing|printing)"; 80 static regex_t proc_re; 81 82 /* 83 * status line(s) for "idle" printers will contain one of the following: 84 * no entries 85 * (printer) is ready 86 * idle 87 */ 88 static char *idle_expr = "(no entries|is ready| idle)"; 89 static regex_t idle_re; 90 91 /* 92 * document line(s) 93 * (copies) copies of (name) (size) bytes 94 * (name) (size) bytes 95 * document lines can be in either format above. 96 * (copies) is the number of copies of the document to print 97 * (name) is the name of the document: /etc/motd, ... 98 * (size) is the number of bytes in the document data 99 */ 100 static char *doc1_expr = "[[:space:]]+(([[:digit:]]+) copies of )([^[:space:]]+)[[:space:]]*([[:digit:]]+) bytes"; 101 static char *doc2_expr = "[[:space:]]+()([^[:space:]]+)[[:space:]]*([[:digit:]]+) bytes"; 102 static regex_t doc1_re; 103 static regex_t doc2_re; 104 105 static void 106 parse_lpd_job(service_t *svc, job_t **job, int fd, char *line, int len) 107 { 108 papi_attribute_t **attributes = NULL; 109 regmatch_t matches[5]; 110 char *s; 111 int octets = 0; 112 113 /* job_re was compiled in the calling function */ 114 if (regexec(&job_re, line, (size_t)5, matches, 0) == REG_NOMATCH) 115 return; 116 117 if ((s = regvalue(matches[1], line)) == NULL) 118 s = "nobody"; 119 papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 120 "job-originating-user-name", s); 121 122 if ((s = regvalue(matches[2], line)) == NULL) 123 s = "0"; 124 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 125 "number-of-intervening-jobs", atoi(s) - 1); 126 127 if ((s = regvalue(matches[3], line)) == NULL) 128 s = "0"; 129 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 130 "job-id", atoi(s)); 131 132 if ((s = regvalue(matches[4], line)) == NULL) 133 s = svc->uri->host; 134 papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 135 "job-originating-host-name", s); 136 137 while ((fdgets(line, len, fd) != NULL) && 138 (regexec(&job_re, line, (size_t)0, NULL, 0) == REG_NOMATCH)) { 139 int size = 0, copies = 1; 140 /* process copies/documents */ 141 142 /* doc1_re and doc2_re were compiled in the calling function */ 143 if ((regexec(&doc1_re, line, (size_t)4, matches, 0) != 0) && 144 (regexec(&doc2_re, line, (size_t)4, matches, 0) != 0)) 145 continue; 146 147 if ((s = regvalue(matches[1], line)) == NULL) 148 s = "1"; 149 if ((copies = atoi(s)) < 1) 150 copies = 1; 151 152 if ((s = regvalue(matches[2], line)) == NULL) 153 s = "unknown"; 154 papiAttributeListAddString(&attributes, 155 PAPI_ATTR_APPEND, "job-name", s); 156 papiAttributeListAddString(&attributes, 157 PAPI_ATTR_APPEND, "job-file-names", s); 158 159 if ((s = regvalue(matches[3], line)) == NULL) 160 s = "0"; 161 size = atoi(s); 162 papiAttributeListAddInteger(&attributes, 163 PAPI_ATTR_APPEND, "job-file-sizes", size); 164 165 octets += (size * copies); 166 } 167 168 papiAttributeListAddInteger(&attributes, PAPI_ATTR_APPEND, 169 "job-k-octets", octets/1024); 170 papiAttributeListAddInteger(&attributes, PAPI_ATTR_APPEND, 171 "job-octets", octets); 172 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 173 "printer-name", queue_name_from_uri(svc->uri)); 174 175 if ((*job = (job_t *)calloc(1, sizeof (**job))) != NULL) 176 (*job)->attributes = attributes; 177 } 178 179 void 180 parse_lpd_query(service_t *svc, int fd) 181 { 182 papi_attribute_t **attributes = NULL; 183 cache_t *cache = NULL; 184 int state = 0x03; /* idle */ 185 char line[128]; 186 char status[1024]; 187 char *s; 188 189 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 190 "printer-name", queue_name_from_uri(svc->uri)); 191 192 if (uri_to_string(svc->uri, status, sizeof (status)) == 0) 193 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 194 "printer-uri-supported", status); 195 196 /* 197 * on most systems, status is a single line, but some appear to 198 * return multi-line status messages. To get the "best" possible 199 * printer-state-reason, we accumulate the text until we hit the 200 * first print job entry. 201 * 202 * Print job entries start with: 203 * user: rank [job number ...] 204 */ 205 (void) regcomp(&job_re, job_expr, REG_EXTENDED|REG_ICASE); 206 207 status[0] = '\0'; 208 while ((fdgets(line, sizeof (line), fd) != NULL) && 209 (regexec(&job_re, line, (size_t)0, NULL, 0) == REG_NOMATCH)) { 210 strlcat(status, line, sizeof (status)); 211 } 212 /* chop off trailing whitespace */ 213 s = status + strlen(status) - 1; 214 while ((s > status) && (isspace(*s) != 0)) 215 *s-- = '\0'; 216 217 papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 218 "printer-state-reasons", status); 219 220 (void) regcomp(&proc_re, proc_expr, REG_EXTENDED|REG_ICASE); 221 (void) regcomp(&idle_re, idle_expr, REG_EXTENDED|REG_ICASE); 222 if (regexec(&proc_re, status, (size_t)0, NULL, 0) == 0) 223 state = 0x04; /* processing */ 224 else if (regexec(&idle_re, status, (size_t)0, NULL, 0) == 0) 225 state = 0x03; /* idle */ 226 else 227 state = 0x05; /* stopped */ 228 229 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 230 "printer-state", state); 231 232 if ((cache = (cache_t *)calloc(1, sizeof (*cache))) == NULL) 233 return; 234 235 if ((cache->printer = (printer_t *)calloc(1, sizeof (*cache->printer))) 236 == NULL) 237 return; 238 239 cache->printer->attributes = attributes; 240 svc->cache = cache; 241 242 (void) regcomp(&doc1_re, doc1_expr, REG_EXTENDED|REG_ICASE); 243 (void) regcomp(&doc2_re, doc2_expr, REG_EXTENDED|REG_ICASE); 244 /* process job related entries */ 245 while (line[0] != '\0') { 246 job_t *job = NULL; 247 248 parse_lpd_job(svc, &job, fd, line, sizeof (line)); 249 if (job == NULL) 250 break; 251 list_append(&cache->jobs, job); 252 } 253 254 time(&cache->timestamp); 255 } 256 257 void 258 cache_update(service_t *svc) 259 { 260 int fd; 261 262 if (svc->cache != NULL) /* this should be time based */ 263 return; 264 265 if (svc == NULL) 266 return; 267 268 if ((fd = lpd_open(svc, 'q', NULL, 15)) < 0) 269 return; 270 271 parse_lpd_query(svc, fd); 272 273 close(fd); 274 } 275 276 papi_status_t 277 lpd_find_printer_info(service_t *svc, printer_t **printer) 278 { 279 papi_status_t result = PAPI_BAD_ARGUMENT; 280 281 if ((svc == NULL) || (printer == NULL)) 282 return (PAPI_BAD_ARGUMENT); 283 284 cache_update(svc); 285 286 if (svc->cache != NULL) { 287 *printer = svc->cache->printer; 288 result = PAPI_OK; 289 } else 290 result = PAPI_NOT_FOUND; 291 292 return (result); 293 } 294 295 papi_status_t 296 lpd_find_jobs_info(service_t *svc, job_t ***jobs) 297 { 298 papi_status_t result = PAPI_BAD_ARGUMENT; 299 300 if (svc != NULL) { 301 cache_update(svc); 302 303 if (svc->cache != NULL) { 304 *jobs = svc->cache->jobs; 305 result = PAPI_OK; 306 } 307 } 308 309 return (result); 310 } 311 312 papi_status_t 313 lpd_find_job_info(service_t *svc, int job_id, job_t **job) 314 { 315 papi_status_t result = PAPI_BAD_ARGUMENT; 316 job_t **jobs; 317 318 if (lpd_find_jobs_info(svc, &jobs) != PAPI_OK) { 319 int i; 320 321 *job = NULL; 322 for (i = 0; ((*job == NULL) && (jobs[i] != NULL)); i++) { 323 int id = -1; 324 325 papiAttributeListGetInteger(jobs[i]->attributes, NULL, 326 "job-id", &id); 327 if (id == job_id) 328 *job = jobs[i]; 329 } 330 331 if (*job != NULL) 332 result = PAPI_OK; 333 } 334 335 return (result); 336 } 337 338 void 339 cache_free(cache_t *item) 340 { 341 if (item != NULL) { 342 if (item->printer != NULL) 343 papiPrinterFree((papi_printer_t *)item->printer); 344 if (item->jobs != NULL) 345 papiJobListFree((papi_job_t *)item->jobs); 346 free(item); 347 } 348 } 349