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