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