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 (c) 2006, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* $Id: lpd-query.c 155 2006-04-26 02:34:54Z ktou $ */ 27 28 #include <stdio.h> 29 #include <stdlib.h> 30 #include <unistd.h> 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <sys/fcntl.h> 34 #include <time.h> 35 #include <ctype.h> 36 #include <string.h> 37 #include <stdarg.h> 38 #include <regex.h> 39 40 #include <papi_impl.h> 41 42 /* The string is modified by this call */ 43 static char * 44 regvalue(regmatch_t match, char *string) 45 { 46 char *result = NULL; 47 48 if (match.rm_so != match.rm_eo) { 49 result = string + match.rm_so; 50 *(result + (match.rm_eo - match.rm_so)) = '\0'; 51 } 52 53 return (result); 54 } 55 56 /* 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 65 * places. 66 */ 67 static char *job_expr = "^(.*[[:alnum:]]):[[:space:]]+([[:alnum:]]+)"\ 68 "[[:space:]]+[[][[:space:]]*job[[:space:]]*([[:digit:]]+)"\ 69 "[[: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:]]"\ 89 "+([[:alnum:]]+)[[:space:]]+(.*)([[:alnum:]]+)(.*)[[:space:]]+"\ 90 "([[:digit:]]+)[[:space:]]+([[:digit:]]+)[[:space:]]+([[:digit:]]+)"\ 91 "[[:space:]]+([[:digit:]]+)"; 92 static regex_t wjob_re; 93 94 /* 95 * Windows job header is in the following format 96 * Owner Status Jobname Job-Id Size Pages Priority 97 * -------------------------------------------------------------- 98 */ 99 static char *whjob_expr = "Owner Status Jobname Job-Id"\ 100 " Size Pages Priority"; 101 static regex_t whjob_re; 102 103 static char *wline_expr = "----------"; 104 static regex_t wline_re; 105 106 /* 107 * status line(s) for "processing" printers will contain one of the following: 108 * ready and printing 109 * Printing 110 * processing 111 */ 112 static char *proc_expr = "(ready and printing|printing|processing)"; 113 static regex_t proc_re; 114 115 /* 116 * status line(s) for "idle" printers will contain one of the following: 117 * no entries 118 * (printer) is ready 119 * idle 120 */ 121 static char *idle_expr = "(no entries|is ready| idle)"; 122 static regex_t idle_re; 123 124 /* 125 * Printer state reason (For Windows remote printers) 126 * Paused 127 */ 128 static char *state_reason_expr = "(Paused)"; 129 static regex_t state_reason_re; 130 131 /* 132 * document line(s) 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 139 */ 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; 146 147 /* Printer-state for Windows */ 148 static int win_state = 0x03; /* Idle */ 149 150 static void 151 parse_lpd_job(service_t *svc, job_t **job, int fd, char *line, int len) 152 { 153 papi_attribute_t **attributes = NULL; 154 regmatch_t matches[10]; 155 char *s; 156 int octets = 0; 157 int flag = 0; 158 159 /* 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 163 */ 164 165 if (regexec(&job_re, line, (size_t)5, matches, 0) == REG_NOMATCH) { 166 if (regexec(&wjob_re, line, (size_t)10, matches, 0) 167 == REG_NOMATCH) 168 return; 169 else 170 flag = 1; 171 } 172 173 if (flag == 1) { 174 /* Windows job */ 175 /* first match is job-id */ 176 177 if ((s = regvalue(matches[1], line)) == NULL) 178 s = "nobody"; 179 papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 180 "job-originating-user-name", s); 181 182 if ((s = regvalue(matches[4], line)) == NULL) 183 s = "unknown"; 184 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 185 "job-name", s); 186 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 187 "job-file-names", s); 188 189 if ((s = regvalue(matches[7], line)) == NULL) 190 s = "0"; 191 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 192 "job-id", atoi(s)); 193 194 if ((s = regvalue(matches[8], line)) == NULL) 195 s = "0"; 196 octets = atoi(s); 197 papiAttributeListAddInteger(&attributes, 198 PAPI_ATTR_APPEND, "job-file-sizes", atoi(s)); 199 200 /* 201 * Since a job has been found so the printer state is either 202 * 'stopped' or 'processing' 203 * By default it is "processing" 204 */ 205 win_state = 0x04; 206 } else { 207 /* Solaris job */ 208 if ((s = regvalue(matches[1], line)) == NULL) 209 s = "nobody"; 210 papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 211 "job-originating-user-name", s); 212 213 if ((s = regvalue(matches[2], line)) == NULL) 214 s = "0"; 215 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 216 "number-of-intervening-jobs", atoi(s) - 1); 217 218 if ((s = regvalue(matches[3], line)) == NULL) 219 s = "0"; 220 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 221 "job-id", atoi(s)); 222 223 if ((s = regvalue(matches[4], line)) == NULL) 224 s = svc->uri->host; 225 papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 226 "job-originating-host-name", s); 227 } 228 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 */ 234 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)) 238 continue; 239 240 if ((s = regvalue(matches[1], line)) == NULL) 241 s = "1"; 242 if ((copies = atoi(s)) < 1) 243 copies = 1; 244 245 if ((s = regvalue(matches[2], line)) == NULL) 246 s = "unknown"; 247 papiAttributeListAddString(&attributes, 248 PAPI_ATTR_APPEND, "job-name", s); 249 papiAttributeListAddString(&attributes, 250 PAPI_ATTR_APPEND, "job-file-names", s); 251 252 if ((s = regvalue(matches[3], line)) == NULL) 253 s = "0"; 254 size = atoi(s); 255 256 papiAttributeListAddInteger(&attributes, 257 PAPI_ATTR_APPEND, "job-file-sizes", size); 258 259 octets += (size * copies); 260 } 261 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)); 268 269 if ((*job = (job_t *)calloc(1, sizeof (**job))) != NULL) 270 (*job)->attributes = attributes; 271 } 272 273 void 274 parse_lpd_query(service_t *svc, int fd) 275 { 276 papi_attribute_t **attributes = NULL; 277 cache_t *cache = NULL; 278 int state = 0x03; /* idle */ 279 char line[128]; 280 char status[1024]; 281 char *s; 282 int win_flag = 0; 283 284 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 285 "printer-name", queue_name_from_uri(svc->uri)); 286 287 if (uri_to_string(svc->uri, status, sizeof (status)) == 0) 288 papiAttributeListAddString(&attributes, PAPI_ATTR_APPEND, 289 "printer-uri-supported", status); 290 291 /* 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. 296 * 297 * Print job entries start with: 298 * user: rank [job number ...] 299 */ 300 (void) regcomp(&job_re, job_expr, REG_EXTENDED|REG_ICASE); 301 302 /* 303 * For remote windows printers 304 * Print job entries start with: 305 * Owner Status Jobname Job-Id Size Pages Priority 306 */ 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); 310 311 status[0] = '\0'; 312 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)) { 316 /* 317 * When windows job queue gets queried following header 318 * should not get printed 319 * Owner Status Jobname Job-Id Size Pages Priority 320 * ----------------------------------------------- 321 */ 322 if ((regexec(&whjob_re, line, (size_t)0, NULL, 0) 323 == REG_NOMATCH) && 324 (regexec(&wline_re, line, (size_t)0, NULL, 0) 325 == REG_NOMATCH)) 326 strlcat(status, line, sizeof (status)); 327 } 328 329 /* chop off trailing whitespace */ 330 s = status + strlen(status) - 1; 331 while ((s > status) && (isspace(*s) != 0)) 332 *s-- = '\0'; 333 334 papiAttributeListAddString(&attributes, PAPI_ATTR_REPLACE, 335 "printer-state-reasons", status); 336 337 /* Check if this is for Windows remote printers */ 338 if (strstr(status, "Windows")) { 339 /* 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" 343 */ 344 win_flag = 1; 345 (void) regcomp(&state_reason_re, state_reason_expr, 346 REG_EXTENDED|REG_ICASE); 347 348 if (regexec(&state_reason_re, status, (size_t)0, NULL, 0) == 0) 349 state = 0x05; /* stopped */ 350 } else { 351 (void) regcomp(&proc_re, proc_expr, REG_EXTENDED|REG_ICASE); 352 (void) regcomp(&idle_re, idle_expr, REG_EXTENDED|REG_ICASE); 353 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 */ 358 else 359 state = 0x05; /* stopped */ 360 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 361 "printer-state", state); 362 } 363 364 if ((cache = (cache_t *)calloc(1, sizeof (*cache))) == NULL) 365 return; 366 367 if ((cache->printer = (printer_t *)calloc(1, sizeof (*cache->printer))) 368 == NULL) 369 return; 370 371 cache->printer->attributes = attributes; 372 svc->cache = cache; 373 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') { 378 job_t *job = NULL; 379 380 parse_lpd_job(svc, &job, fd, line, sizeof (line)); 381 if (job == NULL) 382 break; 383 list_append(&cache->jobs, job); 384 } 385 386 /* 387 * For remote windows printer set the printer-state 388 * after parse_lpd_job 389 */ 390 if (win_flag) { 391 if (state == 0x05) 392 win_state = state; 393 394 papiAttributeListAddInteger(&attributes, PAPI_ATTR_REPLACE, 395 "printer-state", win_state); 396 } 397 time(&cache->timestamp); 398 } 399 400 void 401 cache_update(service_t *svc) 402 { 403 int fd; 404 405 if (svc == NULL) 406 return; 407 408 if (svc->cache != NULL) { /* this should be time based */ 409 if (svc->cache->jobs == NULL) { 410 free(svc->cache); 411 svc->cache = NULL; 412 } else 413 return; 414 } 415 416 if ((fd = lpd_open(svc, 'q', NULL, 15)) < 0) 417 return; 418 419 parse_lpd_query(svc, fd); 420 421 close(fd); 422 } 423 424 papi_status_t 425 lpd_find_printer_info(service_t *svc, printer_t **printer) 426 { 427 papi_status_t result = PAPI_BAD_ARGUMENT; 428 429 if ((svc == NULL) || (printer == NULL)) 430 return (PAPI_BAD_ARGUMENT); 431 432 cache_update(svc); 433 434 if (svc->cache != NULL) { 435 *printer = svc->cache->printer; 436 result = PAPI_OK; 437 } else 438 result = PAPI_NOT_FOUND; 439 440 return (result); 441 } 442 443 papi_status_t 444 lpd_find_jobs_info(service_t *svc, job_t ***jobs) 445 { 446 papi_status_t result = PAPI_BAD_ARGUMENT; 447 448 if (svc != NULL) { 449 cache_update(svc); 450 451 if (svc->cache != NULL) { 452 *jobs = svc->cache->jobs; 453 result = PAPI_OK; 454 } 455 } 456 457 /* 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. 464 */ 465 free(svc->cache); 466 svc->cache = NULL; 467 468 return (result); 469 } 470 471 papi_status_t 472 lpd_find_job_info(service_t *svc, int job_id, job_t **job) 473 { 474 papi_status_t result = PAPI_BAD_ARGUMENT; 475 job_t **jobs; 476 477 if ((lpd_find_jobs_info(svc, &jobs) == PAPI_OK) && (jobs != NULL)) { 478 int i; 479 480 *job = NULL; 481 for (i = 0; ((*job == NULL) && (jobs[i] != NULL)); i++) { 482 int id = -1; 483 484 papiAttributeListGetInteger(jobs[i]->attributes, NULL, 485 "job-id", &id); 486 if (id == job_id) 487 *job = jobs[i]; 488 } 489 490 if (*job != NULL) 491 result = PAPI_OK; 492 } 493 494 return (result); 495 } 496 497 void 498 cache_free(cache_t *item) 499 { 500 if (item != NULL) { 501 if (item->printer != NULL) 502 papiPrinterFree((papi_printer_t *)item->printer); 503 if (item->jobs != NULL) 504 papiJobListFree((papi_job_t *)item->jobs); 505 free(item); 506 } 507 } 508