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 2009 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 #include <stdio.h> 31 #include <stdlib.h> 32 #include <unistd.h> 33 #include <sys/types.h> 34 #include <sys/stat.h> 35 #include <sys/fcntl.h> 36 #include <time.h> 37 #include <ctype.h> 38 #include <string.h> 39 #include <stdarg.h> 40 #include <regex.h> 41 42 #include <papi_impl.h> 43 44 /* The string is modified by this call */ 45 static char * 46 regvalue(regmatch_t match, char *string) 47 { 48 char *result = NULL; 49 50 if (match.rm_so != match.rm_eo) { 51 result = string + match.rm_so; 52 *(result + (match.rm_eo - match.rm_so)) = '\0'; 53 } 54 55 return (result); 56 } 57 58 /* 59 * Print job entries start with: 60 * (user): (rank) [job (number) (...)] 61 * (user) is the job-owner's user name 62 * (rank) is the rank in queue. (active, 1st, 2nd, ...) 63 * (number) is the job number 64 * (...) is an optional hostname 65 * some servers will use whitespace a little differently than is displayed 66 * above. The regular expression below makes whitespace optional in some 67 * places. 68 */ 69 static char *job_expr = "^(.*[[:alnum:]]):[[:space:]]+([[:alnum:]]+)[[:space:]]+[[][[:space:]]*job[[:space:]]*([[:digit:]]+)[[:space:]]*(.*)]"; 70 static regex_t job_re; 71 72 /* 73 * Print job entries for remote windows printer start with: 74 * Owner Status Jobname Job-Id Size Pages Priority 75 * e.g: 76 * Owner Status Jobname Job-Id Size Pages Priority 77 * ------------------------------------------------------------ 78 * root (10.3. Waiting /etc/release 2 240 1 4 79 * 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 87 */ 88 static char *wjob_expr = "^([[:alnum:]]+)[[:space:]]*[(](.*)[)]*[[:space:]]+([[:alnum:]]+)[[:space:]]+(.*)([[:alnum:]]+)(.*)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)"; 89 static regex_t wjob_re; 90 91 /* 92 * Windows job header is in the following format 93 * Owner Status Jobname Job-Id Size Pages Priority 94 * -------------------------------------------------------------- 95 */ 96 static char *whjob_expr = "Owner Status Jobname Job-Id Size Pages Priority"; 97 static regex_t whjob_re; 98 99 static char *wline_expr = "----------"; 100 static regex_t wline_re; 101 102 /* 103 * status line(s) for "processing" printers will contain one of the following: 104 * ready and printing 105 * Printing 106 */ 107 static char *proc_expr = "(ready and printing|printing)"; 108 static regex_t proc_re; 109 110 /* 111 * status line(s) for "idle" printers will contain one of the following: 112 * no entries 113 * (printer) is ready 114 * idle 115 */ 116 static char *idle_expr = "(no entries|is ready| idle)"; 117 static regex_t idle_re; 118 119 /* 120 * Printer state reason 121 * Paused 122 */ 123 static char *state_reason_expr = "(Paused)"; 124 static regex_t state_reason_re; 125 126 /* 127 * document line(s) 128 * (copies) copies of (name) (size) bytes 129 * (name) (size) bytes 130 * document lines can be in either format above. 131 * (copies) is the number of copies of the document to print 132 * (name) is the name of the document: /etc/motd, ... 133 * (size) is the number of bytes in the document data 134 */ 135 static char *doc1_expr = "[[:space:]]+(([[:digit:]]+) copies of )([^[:space:]]+)[[:space:]]*([[:digit:]]+) bytes"; 136 static char *doc2_expr = "[[:space:]]+()([^[:space:]]+)[[:space:]]*([[:digit:]]+) bytes"; 137 static regex_t doc1_re; 138 static regex_t doc2_re; 139 140 static void 141 parse_lpd_job(service_t *svc, job_t **job, int fd, char *line, int len) 142 { 143 papi_attribute_t **attributes = NULL; 144 regmatch_t matches[10]; 145 char *s; 146 int octets = 0; 147 int flag = 0; 148 149 /* 150 * job_re and wjob_re were compiled in the calling function 151 * first check for solaris jobs 152 * if there is no-match check for windows jobs 153 */ 154 155 if (regexec(&job_re, line, (size_t)5, matches, 0) == REG_NOMATCH) { 156 if (regexec(&wjob_re, line, (size_t)10, matches, 0) 157 == REG_NOMATCH) 158 return; 159 else 160 flag = 1; 161 } 162 163 if (flag == 1) { 164 /* Windows job */ 165 /* first match is job-id */ 166 if ((s = regvalue(matches[1], line)) == NULL) 167 s = "nobody"; 168 papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 169 "job-originating-user-name", s); 170 171 if ((s = regvalue(matches[4], line)) == NULL) 172 s = "unknown"; 173 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 174 "job-name", s); 175 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 176 "job-file-names", s); 177 178 if ((s = regvalue(matches[7], line)) == NULL) 179 s = "0"; 180 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 181 "job-id", atoi(s)); 182 183 if ((s = regvalue(matches[8], line)) == NULL) 184 s = "0"; 185 octets = atoi(s); 186 papiAttributeListAddInteger(&attributes, 187 PAPI_ATTR_APPEND, "job-file-sizes", atoi(s)); 188 189 } else { 190 /* Solaris job */ 191 if ((s = regvalue(matches[1], line)) == NULL) 192 s = "nobody"; 193 papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 194 "job-originating-user-name", s); 195 196 if ((s = regvalue(matches[2], line)) == NULL) 197 s = "0"; 198 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 199 "number-of-intervening-jobs", atoi(s) - 1); 200 201 if ((s = regvalue(matches[3], line)) == NULL) 202 s = "0"; 203 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 204 "job-id", atoi(s)); 205 206 if ((s = regvalue(matches[4], line)) == NULL) 207 s = svc->uri->host; 208 papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 209 "job-originating-host-name", s); 210 } 211 212 while ((fdgets(line, len, fd) != NULL) && 213 (regexec(&job_re, line, (size_t)0, NULL, 0) == REG_NOMATCH) && 214 (regexec(&wjob_re, line, (size_t)0, NULL, 0) == REG_NOMATCH)) { 215 int size = 0, copies = 1; 216 /* process copies/documents */ 217 218 /* doc1_re and doc2_re were compiled in the calling function */ 219 if ((regexec(&doc1_re, line, (size_t)4, matches, 0) != 0) && 220 (regexec(&doc2_re, line, (size_t)4, matches, 0) != 0)) 221 continue; 222 223 if ((s = regvalue(matches[1], line)) == NULL) 224 s = "1"; 225 if ((copies = atoi(s)) < 1) 226 copies = 1; 227 228 if ((s = regvalue(matches[2], line)) == NULL) 229 s = "unknown"; 230 papiAttributeListAddString(&attributes, 231 PAPI_ATTR_APPEND, "job-name", s); 232 papiAttributeListAddString(&attributes, 233 PAPI_ATTR_APPEND, "job-file-names", s); 234 235 if ((s = regvalue(matches[3], line)) == NULL) 236 s = "0"; 237 size = atoi(s); 238 239 papiAttributeListAddInteger(&attributes, 240 PAPI_ATTR_APPEND, "job-file-sizes", size); 241 242 octets += (size * copies); 243 } 244 245 papiAttributeListAddInteger(&attributes, PAPI_ATTR_APPEND, 246 "job-k-octets", octets/1024); 247 papiAttributeListAddInteger(&attributes, PAPI_ATTR_APPEND, 248 "job-octets", octets); 249 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 250 "printer-name", queue_name_from_uri(svc->uri)); 251 252 if ((*job = (job_t *)calloc(1, sizeof (**job))) != NULL) 253 (*job)->attributes = attributes; 254 } 255 256 void 257 parse_lpd_query(service_t *svc, int fd) 258 { 259 papi_attribute_t **attributes = NULL; 260 cache_t *cache = NULL; 261 int state = 0x03; /* idle */ 262 char line[128]; 263 char status[1024]; 264 char *s; 265 266 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 267 "printer-name", queue_name_from_uri(svc->uri)); 268 269 if (uri_to_string(svc->uri, status, sizeof (status)) == 0) 270 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 271 "printer-uri-supported", status); 272 273 /* 274 * on most systems, status is a single line, but some appear to 275 * return multi-line status messages. To get the "best" possible 276 * printer-state-reason, we accumulate the text until we hit the 277 * first print job entry. 278 * 279 * Print job entries start with: 280 * user: rank [job number ...] 281 */ 282 (void) regcomp(&job_re, job_expr, REG_EXTENDED|REG_ICASE); 283 284 /* 285 * For remote windows printers 286 * Print job entries start with: 287 * Owner Status Jobname Job-Id Size Pages Priority 288 */ 289 (void) regcomp(&wjob_re, wjob_expr, REG_EXTENDED|REG_ICASE); 290 (void) regcomp(&whjob_re, whjob_expr, REG_EXTENDED|REG_ICASE); 291 (void) regcomp(&wline_re, wline_expr, REG_EXTENDED|REG_ICASE); 292 293 status[0] = '\0'; 294 295 while ((fdgets(line, sizeof (line), fd) != NULL) && 296 (regexec(&job_re, line, (size_t)0, NULL, 0) == REG_NOMATCH) && 297 (regexec(&wjob_re, line, (size_t)0, NULL, 0) == REG_NOMATCH)) { 298 /* 299 * When windows job queue gets queried following header 300 * should not get printed 301 * Owner Status Jobname Job-Id Size Pages Priority 302 * ----------------------------------------------- 303 */ 304 if ((regexec(&whjob_re, line, (size_t)0, NULL, 0) 305 == REG_NOMATCH) && (regexec(&wline_re, line, (size_t)0, NULL, 0) 306 == REG_NOMATCH)) 307 strlcat(status, line, sizeof (status)); 308 } 309 310 /* chop off trailing whitespace */ 311 s = status + strlen(status) - 1; 312 while ((s > status) && (isspace(*s) != 0)) 313 *s-- = '\0'; 314 315 papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 316 "printer-state-reasons", status); 317 318 (void) regcomp(&proc_re, proc_expr, REG_EXTENDED|REG_ICASE); 319 (void) regcomp(&idle_re, idle_expr, REG_EXTENDED|REG_ICASE); 320 (void) regcomp(&state_reason_re, state_reason_expr, 321 REG_EXTENDED|REG_ICASE); 322 323 if ((regexec(&proc_re, status, (size_t)0, NULL, 0) == 0) || 324 (regexec(&state_reason_re, status, (size_t)0, NULL, 0) == 325 REG_NOMATCH)) 326 state = 0x04; /* processing */ 327 else if (regexec(&idle_re, status, (size_t)0, NULL, 0) == 0) 328 state = 0x03; /* idle */ 329 else 330 state = 0x05; /* stopped */ 331 332 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 333 "printer-state", state); 334 335 if ((cache = (cache_t *)calloc(1, sizeof (*cache))) == NULL) 336 return; 337 338 if ((cache->printer = (printer_t *)calloc(1, sizeof (*cache->printer))) 339 == NULL) 340 return; 341 342 cache->printer->attributes = attributes; 343 svc->cache = cache; 344 345 (void) regcomp(&doc1_re, doc1_expr, REG_EXTENDED|REG_ICASE); 346 (void) regcomp(&doc2_re, doc2_expr, REG_EXTENDED|REG_ICASE); 347 /* process job related entries */ 348 while (line[0] != '\0') { 349 job_t *job = NULL; 350 351 parse_lpd_job(svc, &job, fd, line, sizeof (line)); 352 if (job == NULL) 353 break; 354 list_append(&cache->jobs, job); 355 } 356 357 time(&cache->timestamp); 358 } 359 360 void 361 cache_update(service_t *svc) 362 { 363 int fd; 364 365 if (svc->cache != NULL) /* this should be time based */ 366 return; 367 368 if (svc == NULL) 369 return; 370 371 if ((fd = lpd_open(svc, 'q', NULL, 15)) < 0) 372 return; 373 374 parse_lpd_query(svc, fd); 375 376 close(fd); 377 } 378 379 papi_status_t 380 lpd_find_printer_info(service_t *svc, printer_t **printer) 381 { 382 papi_status_t result = PAPI_BAD_ARGUMENT; 383 384 if ((svc == NULL) || (printer == NULL)) 385 return (PAPI_BAD_ARGUMENT); 386 387 cache_update(svc); 388 389 if (svc->cache != NULL) { 390 *printer = svc->cache->printer; 391 result = PAPI_OK; 392 } else 393 result = PAPI_NOT_FOUND; 394 395 return (result); 396 } 397 398 papi_status_t 399 lpd_find_jobs_info(service_t *svc, job_t ***jobs) 400 { 401 papi_status_t result = PAPI_BAD_ARGUMENT; 402 403 if (svc != NULL) { 404 cache_update(svc); 405 406 if (svc->cache != NULL) { 407 *jobs = svc->cache->jobs; 408 result = PAPI_OK; 409 } 410 } 411 412 return (result); 413 } 414 415 papi_status_t 416 lpd_find_job_info(service_t *svc, int job_id, job_t **job) 417 { 418 papi_status_t result = PAPI_BAD_ARGUMENT; 419 job_t **jobs; 420 421 if (lpd_find_jobs_info(svc, &jobs) != PAPI_OK) { 422 int i; 423 424 *job = NULL; 425 for (i = 0; ((*job == NULL) && (jobs[i] != NULL)); i++) { 426 int id = -1; 427 428 papiAttributeListGetInteger(jobs[i]->attributes, NULL, 429 "job-id", &id); 430 if (id == job_id) 431 *job = jobs[i]; 432 } 433 434 if (*job != NULL) 435 result = PAPI_OK; 436 } 437 438 return (result); 439 } 440 441 void 442 cache_free(cache_t *item) 443 { 444 if (item != NULL) { 445 if (item->printer != NULL) 446 papiPrinterFree((papi_printer_t *)item->printer); 447 if (item->jobs != NULL) 448 papiJobListFree((papi_job_t *)item->jobs); 449 free(item); 450 } 451 } 452