1/* 2 * This program is free software; you can redistribute it and/or 3 * modify it under the terms of the GNU General Public License as 4 * published by the Free Software Foundation; either version 2 of 5 * the License, or (at your option) any later version. 6 * 7 * This program is distributed in the hope that it will be useful, 8 * but WITHOUT ANY WARRANTY; without even the implied warranty of 9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 10 * GNU General Public License for more details. 11 * 12 * You should have received a copy of the GNU General Public License 13 * along with this program; if not, write to the Free Software 14 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, 15 * MA 02111-1307 USA 16 */ 17/*************************************************************************** 18 * LPRng - An Extended Print Spooler System 19 * 20 * Copyright 1988-2003, Patrick Powell, San Diego, CA 21 * papowell@lprng.com 22 * See LICENSE for conditions of use. 23 * 24 ***************************************************************************/ 25 26 static char *const _id = 27"$Id: lpf.c,v 1.1.1.1 2008/10/15 03:28:27 james26_jang Exp $"; 28 29 30/*************************************************************************** 31 * Filter template and frontend. 32 * 33 * A filter is invoked with the following parameters, 34 * which can be in any order, and perhaps some missing. 35 * 36 * filtername arguments \ <- from PRINTCAP entry 37 * -PPrinter -wwidth -llength -xwidth -ylength [-c] \ 38 * -Kcontrolfilename -Lbnrname \ 39 * [-iindent] \ 40 * [-Zoptions] [-Cclass] [-Jjob] [-Raccntname] -nlogin -hHost \ 41 * -Fformat [-Tcrlf] [-Tdebug] [affile] 42 * 43 * 1. Parameters can be in different order than the above. 44 * 2. Optional parameters can be missing 45 * 3. Values specified for the width, length, etc., are from PRINTCAP 46 * or from the overridding user specified options. 47 * 48 * This program provides a common front end for most of the necessary 49 * grunt work. This falls into the following classes: 50 * 1. Parameter extraction. 51 * 2. Suspension when used as the "of" filter. 52 * 3. Termination and accounting 53 * The front end will extract parameters, then call the filter_pgm() 54 * routine, which is responsible for carrying out the required filter 55 * actions. filter_pgm() is invoked with the printer device on fd 1, 56 * and error log on fd 2. The npages variable is used to record the 57 * number of pages that were used. 58 * The "halt string", which is a sequence of characters that 59 * should cause the filter to suspend itself, is passed to filter. 60 * When these characters are detected, the "suspend_ofilter()" routine should be 61 * called. 62 * 63 * On successful termination, the accounting file will be updated. 64 * 65 * The filter_pgm() routine should return 0 (success), 1 (retry) or 2 (abort). 66 * 67 * Parameter Extraction 68 * The main() routine will extract parameters 69 * whose values are placed in the appropriate variables. This is done 70 * by using the ParmTable[], which has entries for each valid letter 71 * parmeter, such as the letter flag, the type of variable, 72 * and the address of the variable. 73 * The following variables are provided as a default set. 74 * -PPrinter -wwidth -llength -xwidth -ylength [-c] [-iindent] \ 75 * [-Zoptions] [-Cclass] [-Jjob] [-Raccntname] -nlogin -hHost \ 76 * -Fformat [affile] 77 * VARIABLE FLAG TYPE PURPOSE / PRINTCAP ENTRTY 78 * name name of filter char* argv[0], program identification 79 * width -wwidth int PW, width in chars 80 * length -llength int PL, length in lines 81 * xwidth -xwidth int PX, width in pixels 82 * xlength -xlength int PY, length in pixels 83 * literal -c int if set, ignore control chars 84 * controlfile -kcontrolfile char* control file name 85 * bnrname -Lbnrname char* banner name 86 * indent -iindent int indent amount (depends on device) 87 * zopts -Zoptions char* extra options for printer 88 * comment -Scomment char* printer name in comment field 89 * class -Cclass char* classname 90 * job -Jjob char* jobname 91 * accntname -Raccntname char* account for billing purposes 92 * login -nlogin char* login name 93 * host -hhost char* host name 94 * format -Fformat char* format 95 * statusfile -sstatusfile char* statusfile 96 * accntfile file char* AF, accounting file 97 * 98 * npages - number of pages for accounting 99 * 100 * -Tdebug - increment debug level 101 * -Tcrlf - turn LF to CRLF translation off 102 * 103 * The functions logerr(), and logerr_die() can be used to report 104 * status. The variable errorcode can be set by the user before calling 105 * these functions, and will be the exit value of the program. Its default 106 * value will be 2 (abort status). 107 * logerr() reports a message, appends information indicated by errno 108 * (see perror(2) for details), and then returns. 109 * logerr_die() will call logerr(), and then will exit with errorcode 110 * status. 111 * 112 * DEBUGGING: a simple minded debugging version can be enabled by 113 * compiling with the -DDEBUG option. 114 */ 115 116#include "portable.h" 117 118/* VARARGS3 */ 119#ifdef HAVE_STDARGS 120int plp_snprintf (char *str, size_t count, const char *fmt, ...); 121int plp_vsnprintf (char *str, size_t count, const char *fmt, va_list arg); 122#else 123int plp_snprintf (); 124int plp_vsnprintf (); 125#endif 126 127/* VARARGS2 */ 128#ifdef HAVE_STDARGS 129 void safefprintf (int fd, char *format,...); 130#else 131 void safefprintf (); 132#endif 133 134#include <sys/types.h> 135#include <stdio.h> 136#include <signal.h> 137#include <string.h> 138#include <ctype.h> 139#include <errno.h> 140#include <time.h> 141 142#ifdef HAVE_STDLIB_H 143#include <stdlib.h> 144#endif 145 146#ifdef HAVE_UNISTD_H 147#include <unistd.h> 148#endif 149 150#ifdef HAVE_SYS_FILE_H 151# include <sys/file.h> 152#endif 153 154#ifndef HAVE_ERRNO_DECL 155extern int errno; 156#endif 157 158#ifdef HAVE_SYS_FCNTL_H 159# include <sys/fcntl.h> 160#endif 161#ifdef HAVE_FCNTL_H 162# include <fcntl.h> 163#endif 164 165/* varargs declarations: */ 166 167#if defined(HAVE_STDARG_H) 168# include <stdarg.h> 169# define HAVE_STDARGS /* let's hope that works everywhere (mj) */ 170# define VA_LOCAL_DECL va_list ap; 171# define VA_START(f) va_start(ap, f) 172# define VA_SHIFT(v,t) ; /* no-op for ANSI */ 173# define VA_END va_end(ap) 174#else 175# if defined(HAVE_VARARGS_H) 176# include <varargs.h> 177# undef HAVE_STDARGS 178# define VA_LOCAL_DECL va_list ap; 179# define VA_START(f) va_start(ap) /* f is ignored! */ 180# define VA_SHIFT(v,t) v = va_arg(ap,t) 181# define VA_END va_end(ap) 182# else 183XX ** NO VARARGS ** XX 184# endif 185#endif 186 187char *Time_str(int shortform, time_t t); 188 189/* 190 * default exit status, causes abort 191 */ 192int errorcode; 193char *name; /* name of filter */ 194/* set from flags */ 195int debug, width, length, xwidth, ylength, literal, indent; 196char *zopts, *class, *job, *login, *accntname, *host, *accntfile, *format; 197char *printer, *controlfile, *bnrname, *comment; 198char *queuename, *errorfile; 199int npages; /* number of pages */ 200char *statusfile; 201char filter_stop[] = "\031\001"; /* sent to cause filter to suspend */ 202int accounting_fd; 203int crlf; /* change lf to CRLF */ 204 205void getargs( int argc, char *argv[], char *envp[] ); 206void logerr( char *msg, ... ); 207void logerr_die( char *msg, ... ); 208extern void banner( void ); 209extern void doaccnt( void ); 210extern void filter_pgm( char * ); 211int of_filter; 212 213int main( int argc, char *argv[], char *envp[] ) 214{ 215 216 /* check to see if you have the accounting fd */ 217 accounting_fd = dup(3); 218 /* if this works, then you have one */ 219 if( accounting_fd >= 0 ){ 220 (void)close( accounting_fd ); 221 accounting_fd = 3; 222 } else { 223 accounting_fd = -1; 224 } 225 if( fcntl(0,F_GETFL,0) == -1 ){ 226 FPRINTF(STDERR,"BAD FD 0\n"); 227 exit(2); 228 } 229 if( fcntl(1,F_GETFL,0) == -1 ){ 230 FPRINTF(STDERR,"BAD FD 1\n"); 231 exit(2); 232 } 233 if( fcntl(2,F_GETFL,0) == -1 ){ 234 FPRINTF(STDERR,"BAD FD 2\n"); 235 exit(2); 236 } 237 getargs( argc, argv, envp ); 238 /* 239 * Turn off SIGPIPE 240 */ 241 (void)signal( SIGPIPE, SIG_IGN ); 242 (void)signal( SIGINT, SIG_DFL ); 243 (void)signal( SIGHUP, SIG_DFL ); 244 (void)signal( SIGQUIT, SIG_DFL ); 245 (void)signal( SIGCHLD, SIG_DFL ); 246 if( of_filter || (format && format[0] == 'o') ){ 247 filter_pgm( filter_stop ); 248 } else { 249 filter_pgm( (char *)0 ); 250 } 251 return(0); 252} 253 254int Write_fd_str( int fd, const char *msg ) 255{ 256 int n; 257 n = strlen(msg); 258 return write(fd,msg,n); 259} 260 261/* VARARGS2 */ 262#ifdef HAVE_STDARGS 263 void safefprintf (int fd, char *format,...) 264#else 265 void safefprintf (va_alist) va_dcl 266#endif 267{ 268#ifndef HAVE_STDARGS 269 int fd; 270 char *format; 271#endif 272 char buf[1024]; 273 VA_LOCAL_DECL 274 275 VA_START (format); 276 VA_SHIFT (fd, int); 277 VA_SHIFT (format, char *); 278 279 buf[0] = 0; 280 (void) VSNPRINTF (buf, sizeof(buf)) format, ap); 281 Write_fd_str(fd,buf); 282} 283 284/**************************************************************************** 285 * Extract the necessary definitions for error message reporting 286 ****************************************************************************/ 287 288#if !defined(HAVE_STRERROR) 289# undef num_errors 290# if defined(HAVE_SYS_ERRLIST) 291# if !defined(HAVE_DECL_SYS_ERRLIST) 292 extern const char *const sys_errlist[]; 293# endif 294# if defined(HAVE_SYS_NERR) 295# if !defined(HAVE_DECL_SYS_NERR) 296 extern int sys_nerr; 297# endif 298# define num_errors (sys_nerr) 299# endif 300# endif 301# if !defined(num_errors) 302# define num_errors (-1) /* always use "errno=%d" */ 303# endif 304#endif 305 306const char * Errormsg ( int err ) 307{ 308 const char *cp; 309 310#if defined(HAVE_STRERROR) 311 cp = strerror(err); 312#else 313# if defined(HAVE_SYS_ERRLIST) 314 if (err >= 0 && err <= num_errors) { 315 cp = sys_errlist[err]; 316 } else 317# endif 318 { 319 static char msgbuf[32]; /* holds "errno=%d". */ 320 (void) SNPRINTF(msgbuf, sizeof(msgbuf)) "errno=%d", err); 321 cp = msgbuf; 322 } 323#endif 324 return (cp); 325} 326 327#ifdef HAVE_STDARGS 328void logerr(char *msg, ...) 329#else 330void logerr( va_alist ) va_dcl 331#endif 332{ 333#ifndef HAVE_STDARGS 334 char *msg; 335#endif 336 int err = errno; 337 int n; 338 char buf[1024]; 339 VA_LOCAL_DECL 340 VA_START(msg); 341 VA_SHIFT(msg, char *); 342 343 (void)SNPRINTF(buf,sizeof(buf)) "%s: ", name); 344 n = strlen(buf); 345 (void)VSNPRINTF(buf+n,sizeof(buf)-n) msg, ap); 346 n = strlen(buf); 347 (void)SNPRINTF(buf+n,sizeof(buf)-n) "- %s\n", Errormsg(err) ); 348 Write_fd_str(2,buf); 349 VA_END; 350} 351 352#ifdef HAVE_STDARGS 353void logerr_die(char *msg, ...) 354#else 355void logerr_die( va_alist ) va_dcl 356#endif 357{ 358#ifndef HAVE_STDARGS 359 char *msg; 360#endif 361 int err = errno; 362 int n; 363 char buf[1024]; 364 VA_LOCAL_DECL 365 VA_START(msg); 366 VA_SHIFT(msg, char *); 367 368 (void)SNPRINTF(buf,sizeof(buf)) "%s: ", name); 369 n = strlen(buf); 370 (void)VSNPRINTF(buf+n,sizeof(buf)-n) msg, ap); 371 n = strlen(buf); 372 (void)SNPRINTF(buf+n,sizeof(buf)-n) "- %s\n", Errormsg(err) ); 373 Write_fd_str(2,buf); 374 VA_END; 375 exit(errorcode); 376} 377 378/* 379 * doaccnt() 380 * writes the accounting information to the accounting file 381 * This has the format: user host printer pages format date 382 */ 383void doaccnt(void) 384{ 385 time_t t; 386 char buffer[256]; 387 FILE *f; 388 int l, len, c; 389 390 t = time((time_t *)0); 391 392 SNPRINTF(buffer, sizeof(buffer)) "%s\t%s\t%s\t%7d\t%s\t%s\n", 393 login? login: "NULL", 394 host? host: "NULL", 395 printer? printer: "NULL", 396 npages, 397 format? format: "NULL", 398 Time_str(0,0)); 399 len = strlen( buffer ); 400 if( accounting_fd < 0 ){ 401 if(accntfile && (f = fopen(accntfile, "a" )) != NULL ) { 402 accounting_fd = fileno( f ); 403 } 404 } 405 if( accounting_fd >= 0 ){ 406 for( c = l = 0; c >= 0 && l < len; l += c ){ 407 c = write( accounting_fd, &buffer[l], len-l ); 408 } 409 if( c < 0 ){ 410 logerr( "bad write to accounting file" ); 411 } 412 } 413} 414 415void getargs( int argc, char *argv[], char *envp[] ) 416{ 417 int i, c; /* argument index */ 418 char *arg, *optargv; /* argument */ 419 char *s, *end; 420 421 if( (name = argv[0]) == 0 ) name = "FILTER"; 422 if( (s = strrchr( name, '/' )) ){ 423 ++s; 424 } else { 425 s = name; 426 } 427 of_filter = (strstr( s, "of" ) != 0); 428 for( i = 1; i < argc && (arg = argv[i])[0] == '-'; ++i ){ 429 if( (c = arg[1]) == 0 ){ 430 FPRINTF( STDERR, "missing option flag"); 431 i = argc; 432 break; 433 } 434 if( c == 'c' ){ 435 literal = 1; 436 continue; 437 } 438 optargv = &arg[2]; 439 if( arg[2] == 0 ){ 440 optargv = argv[i++]; 441 if( optargv == 0 ){ 442 FPRINTF( STDERR, "missing option '%c' value", c ); 443 i = argc; 444 break; 445 } 446 } 447 switch(c){ 448 case 'C': class = optargv; break; 449 case 'E': errorfile = optargv; break; 450 case 'T': 451 for( s = optargv; s && *s; s = end ){ 452 end = strchr( s, ',' ); 453 if( end ){ 454 *end++ = 0; 455 } 456 if( !strcasecmp( s, "crlf" ) ){ 457 crlf = 1; 458 } 459 if( !strcasecmp( s, "debug" ) ){ 460 ++debug; 461 } 462 } 463 break; 464 case 'F': format = optargv; break; 465 case 'J': job = optargv; break; 466 case 'K': controlfile = optargv; break; 467 case 'L': bnrname = optargv; break; 468 case 'P': printer = optargv; break; 469 case 'Q': queuename = optargv; break; 470 case 'R': accntname = optargv; break; 471 case 'S': comment = optargv; break; 472 case 'Z': zopts = optargv; break; 473 case 'h': host = optargv; break; 474 case 'i': indent = atoi( optargv ); break; 475 case 'l': length = atoi( optargv ); break; 476 case 'n': login = optargv; break; 477 case 's': statusfile = optargv; break; 478 case 'w': width = atoi( optargv ); break; 479 case 'x': xwidth = atoi( optargv ); break; 480 case 'y': ylength = atoi( optargv ); break; 481 default: break; 482 } 483 } 484 if( i < argc ){ 485 accntfile = argv[i]; 486 } 487 if( errorfile ){ 488 int fd; 489 fd = open( errorfile, O_APPEND | O_WRONLY, 0600 ); 490 if( fd < 0 ){ 491 FPRINTF( STDERR, "cannot open error log file '%s'", errorfile ); 492 } else { 493 FPRINTF( STDERR, "using error log file '%s'", errorfile ); 494 if( fd != 2 ){ 495 dup2(fd, 2 ); 496 close(fd); 497 } 498 } 499 } 500 if( debug ){ 501 FPRINTF(STDERR, "%s command: ", name ); 502 for( i = 0; i < argc; ++i ){ 503 FPRINTF(STDERR, "%s ", argv[i] ); 504 } 505 FPRINTF( STDERR, "\n" ); 506 } 507 if( debug ){ 508 FPRINTF(STDERR, "FILTER decoded options: " ); 509 FPRINTF(STDERR,"accntfile '%s'\n", accntfile? accntfile : "null" ); 510 FPRINTF(STDERR,"accntname '%s'\n", accntname? accntname : "null" ); 511 FPRINTF(STDERR,"class '%s'\n", class? class : "null" ); 512 FPRINTF(STDERR,"errorfile '%s'\n", errorfile? errorfile : "null" ); 513 FPRINTF(STDERR,"format '%s'\n", format? format : "null" ); 514 FPRINTF(STDERR,"host '%s'\n", host? host : "null" ); 515 FPRINTF(STDERR,"indent, %d\n", indent); 516 FPRINTF(STDERR,"job '%s'\n", job? job : "null" ); 517 FPRINTF(STDERR,"length, %d\n", length); 518 FPRINTF(STDERR,"literal, %d\n", literal); 519 FPRINTF(STDERR,"login '%s'\n", login? login : "null" ); 520 FPRINTF(STDERR,"printer '%s'\n", printer? printer : "null" ); 521 FPRINTF(STDERR,"queuename '%s'\n", queuename? queuename : "null" ); 522 FPRINTF(STDERR,"statusfile '%s'\n", statusfile? statusfile : "null" ); 523 FPRINTF(STDERR,"width, %d\n", width); 524 FPRINTF(STDERR,"xwidth, %d\n", xwidth); 525 FPRINTF(STDERR,"ylength, %d\n", ylength); 526 FPRINTF(STDERR,"zopts '%s'\n", zopts? zopts : "null" ); 527 528 FPRINTF(STDERR, "FILTER environment: " ); 529 for( i = 0; (arg = envp[i]); ++i ){ 530 FPRINTF(STDERR,"%s\n", arg ); 531 } 532 FPRINTF(STDERR, "RUID: %d, EUID: %d\n", (int)getuid(), (int)geteuid() ); 533 } 534} 535 536/* 537 * suspend_ofilter(): suspends the output filter, waits for a signal 538 */ 539void suspend_ofilter(void) 540{ 541 fflush(stdout); 542 fflush(stderr); 543 if(debug)FPRINTF(STDERR,"FILTER suspending\n"); 544 kill(getpid(), SIGSTOP); 545 if(debug)FPRINTF(STDERR,"FILTER awake\n"); 546} 547/****************************************** 548 * prototype filter() 549 * filter will scan the input looking for the suspend string 550 * if any. 551 ******************************************/ 552 553void filter_pgm(char *stop) 554{ 555 int c; 556 int state, i, xout, lastc; 557 int lines = 0; 558 char inputline[1024]; 559 int inputcount = 0; 560 561 /* 562 * do whatever initializations are needed 563 */ 564 /* FPRINTF(STDERR, "filter ('%s')\n", stop ? stop : "NULL" ); */ 565 /* 566 * now scan the input string, looking for the stop string 567 */ 568 lastc = xout = state = 0; 569 npages = 1; 570 571 inputcount = 0; 572 while( (c = getchar()) != EOF ){ 573 if( inputcount < (int)sizeof(inputline) - 3 ) inputline[inputcount++] = c; 574 if( c == '\n' ){ 575 inputline[inputcount-1] = 0; 576 if(debug)FPRINTF(STDERR,"INPUTLINE count %d '%s'\n", inputcount, inputline ); 577 inputcount = 0; 578 ++lines; 579 if( lines > length ){ 580 lines -= length; 581 ++npages; 582 } 583 if( !literal && crlf == 0 && lastc != '\r' ){ 584 putchar( '\r' ); 585 } 586 } 587 if( c == '\014' ){ 588 ++npages; 589 lines = 0; 590 if( !literal && crlf == 0 ){ 591 putchar( '\r' ); 592 } 593 } 594 if( stop ){ 595 if( c == stop[state] ){ 596 ++state; 597 if( stop[state] == 0 ){ 598 state = 0; 599 suspend_ofilter(); 600 } 601 } else if( state ){ 602 for( i = 0; i < state; ++i ){ 603 putchar( stop[i] ); 604 } 605 state = 0; 606 putchar( c ); 607 } else { 608 putchar( c ); 609 } 610 } else { 611 putchar( c ); 612 } 613 lastc = c; 614 } 615 if( ferror( stdin ) ){ 616 logerr( "error on STDIN"); 617 } 618 for( i = 0; i < state; ++i ){ 619 putchar( stop[i] ); 620 } 621 if( lines > 0 ){ 622 ++npages; 623 } 624 doaccnt(); 625} 626 627/* 628 * Time_str: return "cleaned up" ctime() string... 629 * 630 * in YY/MO/DY/hr:mn:sc 631 * Thu Aug 4 12:34:17 BST 1994 -> 12:34:17 632 */ 633 634char *Time_str(int shortform, time_t t) 635{ 636 static char buffer[99]; 637 struct tm *tmptr; 638 struct timeval tv; 639 640 tv.tv_usec = 0; 641 if( t == 0 ){ 642 if( gettimeofday( &tv, 0 ) == -1 ){ 643 logerr_die( "Time_str: gettimeofday failed"); 644 } 645 t = tv.tv_sec; 646 } 647 tmptr = localtime( &t ); 648 if( shortform ){ 649 SNPRINTF( buffer, sizeof(buffer)) 650 "%02d:%02d:%02d.%03d", 651 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, 652 (int)(tv.tv_usec/1000) ); 653 } else { 654 SNPRINTF( buffer, sizeof(buffer)) 655 "%d-%02d-%02d-%02d:%02d:%02d.%03d", 656 tmptr->tm_year+1900, tmptr->tm_mon+1, tmptr->tm_mday, 657 tmptr->tm_hour, tmptr->tm_min, tmptr->tm_sec, 658 (int)(tv.tv_usec/1000) ); 659 } 660 /* now format the time */ 661 return( buffer ); 662} 663