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: lpd_logger.c,v 1.1.1.1 2008/10/15 03:28:27 james26_jang Exp $";
28
29
30#include "lp.h"
31#include "child.h"
32#include "errorcodes.h"
33#include "fileopen.h"
34#include "getopt.h"
35#include "getprinter.h"
36#include "getqueue.h"
37#include "linksupport.h"
38#include "proctitle.h"
39
40#include "lpd_logger.h"
41
42/***************************************************************************
43 * Setup_logger()
44 *
45 * We will have a process that sits and listens for log data, and then
46 * forwards it to the destination.  This process will have some odd properities.
47 *
48 * 1.  It will never update its destination.  This means you will have to
49 *     kill the logger to get it to accept a new destination.
50 ***************************************************************************/
51
52
53
54/*
55 * Start_logger - helper function to setup logger process
56 */
57
58int Start_logger( int log_fd )
59{
60	struct line_list args, passfd;
61	int fd = Logger_fd;
62	int pid;
63
64	Init_line_list(&passfd);
65	Init_line_list(&args);
66
67	Logger_fd = -1;
68	Setup_lpd_call( &passfd, &args );
69	Logger_fd = fd;
70
71	Set_str_value(&args,CALL,"logger");
72
73	Check_max(&passfd,2);
74	Set_decimal_value(&args,INPUT,passfd.count);
75	passfd.list[passfd.count++] = Cast_int_to_voidstar(log_fd);
76
77	pid = Make_lpd_call( "logger", &passfd, &args );
78	passfd.count = 0;
79	Free_line_list( &args );
80	Free_line_list( &passfd );
81	DEBUG1("Start_logger: log_fd %d, status_pid %d", log_fd, pid );
82	return(pid);
83}
84
85int Dump_queue_status(int outfd)
86{
87	int i, count, fd;
88	char *s, *sp, *pr;
89	struct line_list info;
90	struct job job;
91	char buffer[SMALLBUFFER];
92	/* char *esc_lf_2 = Escape("\n", 2); */
93	/* char *esc_lf_2 = "%25250a"; */
94	char *esc_lf_1 = "%250a";
95	struct stat statb;
96
97	s = sp = 0;
98	Init_job(&job);
99	Init_line_list(&info);
100	if(All_line_list.count == 0 ){
101		Get_all_printcap_entries();
102	}
103	DEBUGF(DLOG2)("Dump_queue_status: writing to fd %d", outfd );
104	for( i = 0; i < All_line_list.count; ++i ){
105		Set_DYN(&Printer_DYN,0);
106		pr = All_line_list.list[i];
107		DEBUGF(DLOG2)("Dump_queue_status: checking '%s'", pr );
108		if( Setup_printer( pr, buffer, sizeof(buffer), 0 ) ) continue;
109		Free_line_list( &Sort_order );
110		if( Scan_queue( &Spool_control, &Sort_order, 0,0,0,0,0,0,0,0 ) ){
111			continue;
112		}
113		Free_line_list(&info);
114		Set_str_value(&info,PRINTER,Printer_DYN);
115		Set_str_value(&info,HOST,FQDNHost_FQDN);
116		Set_decimal_value(&info,PROCESS,getpid());
117		Set_str_value(&info,UPDATE_TIME,Time_str(0,0));
118
119		if( Write_fd_str( outfd, "DUMP=" ) < 0 ){ return(1); }
120		s = Join_line_list(&info,"\n");
121		sp = Escape(s, 1);
122		if( Write_fd_str( outfd, sp ) < 0 ){ return(1); }
123
124		if( s ) free(s); s = 0;
125		if( sp ) free(sp); sp = 0;
126
127		if( Write_fd_str( outfd, "VALUE=" ) < 0 ){ return(1); }
128
129		if( Write_fd_str( outfd, "QUEUE%3d" ) < 0 ){ return(1); }
130		if( (fd = Checkread( Queue_control_file_DYN, &statb )) > 0 ){
131			while( (count = read(fd, buffer, sizeof(buffer)-1)) > 0 ){
132				buffer[count] = 0;
133				s = Escape(buffer,3);
134				if( Write_fd_str( outfd, s ) < 0 ){ return(1); }
135				if(s) free(s); s = 0;
136			}
137			close(fd);
138		}
139		if( Write_fd_str( outfd, esc_lf_1 ) < 0 ){ return(1); }
140
141		if( Write_fd_str( outfd, "PRSTATUS%3d" ) < 0 ){ return(1); }
142		if( (fd = Checkread( Queue_status_file_DYN, &statb )) > 0 ){
143			while( (count = read(fd, buffer, sizeof(buffer)-1)) > 0 ){
144				buffer[count] = 0;
145				s = Escape(buffer,3);
146				if( Write_fd_str( outfd, s ) < 0 ){ return(1); }
147				if(s) free(s); s = 0;
148			}
149			close(fd);
150		}
151		if( Write_fd_str( outfd, esc_lf_1 ) < 0 ){ return(1); }
152
153		for( count = 0; count < Sort_order.count; ++count ){
154			Free_job(&job);
155			Get_hold_file( &job, Sort_order.list[count] );
156
157			if( job.info.count == 0 ) continue;
158			if( Write_fd_str( outfd, "UPDATE%3d" ) < 0 ){ return(1); }
159			s = Join_line_list(&job.info,"\n");
160			sp = Escape(s, 3);
161			if( Write_fd_str( outfd, sp ) < 0 ){ return(1); }
162			if( s ) free(s); s = 0;
163			if( sp ) free(sp); sp = 0;
164			if( Write_fd_str( outfd, esc_lf_1 ) < 0 ){ return(1); }
165		}
166		if( Write_fd_str( outfd, "\n" ) < 0 ){ return(1); }
167	}
168
169	if( Write_fd_str( outfd, "END\n" ) < 0 ){ return(1); }
170	Set_DYN(&Printer_DYN,0);
171
172	Free_line_list( &Sort_order );
173	Free_line_list(&info);
174	Free_job(&job);
175	if( s ) free(s); s = 0;
176	if( sp ) free(sp); sp = 0;
177	return(0);
178}
179
180void Logger( struct line_list *args )
181{
182	char *s, *path, *tempfile;
183	int writefd,m, timeout, readfd;
184	time_t start_time, current_time;
185	int elapsed, left, err;
186	struct timeval timeval, *tp;
187	fd_set readfds, writefds; /* for select() */
188	char inbuffer[LARGEBUFFER];
189	char outbuffer[LARGEBUFFER];
190	int outlen = 0, input_read = 0;
191	char host[SMALLBUFFER];
192	int status_fd = -1;
193	int input_fd = -1;
194	struct stat statb;
195
196	Errorcode = JABORT;
197
198
199	Name = "LOG2";
200	setproctitle( "lpd %s", Name );
201
202	DEBUGFC(DLOG2)Dump_line_list("Logger - args", args );
203
204	timeout = Logger_timeout_DYN;
205	path = Logger_path_DYN;
206
207	/* we copy to a local buffer */
208	host[0] = 0;
209	safestrncpy(host, Logger_destination_DYN );
210	/* OK, we try to open a connection to the logger */
211	if( !(s = safestrchr( host, '%')) ){
212		int len = strlen(host);
213		SNPRINTF(host+len, sizeof(host)-len) "%2001" );
214	}
215
216	readfd = Find_flag_value(args,INPUT,Value_sep);
217	Free_line_list(args);
218
219	writefd = -2;
220	/* now we set up the IO file */
221	Set_nonblock_io(readfd);
222
223	DEBUGF(DLOG2)("Logger: host '%s'", host );
224
225	time( &start_time );
226	status_fd = Make_temp_fd( &tempfile );
227	input_fd = Checkread( tempfile, &statb );
228	unlink(tempfile);
229
230	while( 1 ){
231		tp = 0;
232		left = 0;
233		/* try to see if more output is left */
234		if( outlen == 0 && input_read ){
235			if( (m = read( input_fd, inbuffer, sizeof(inbuffer)-1 )) > 0 ){
236				inbuffer[m] = 0;
237				memcpy( outbuffer, inbuffer, m+1 );
238				outlen = m;
239				DEBUGF(DLOG2)("Logger: queue status '%s'", outbuffer );
240			} else if( m < 0 ){
241				Errorcode = JABORT;
242				LOGERR_DIE(LOG_INFO)"Logger: read error %s", tempfile);
243			}
244			if( m < (int)sizeof(inbuffer)-1 ){
245				/* we can truncate the files */
246				if( lseek( status_fd, 0, SEEK_SET) == -1 ){
247					Errorcode = JABORT;
248					LOGERR_DIE(LOG_INFO) "Logger: lseek failed write file '%s'", tempfile);
249				}
250				if( lseek( input_fd, 0, SEEK_SET) == -1 ){
251					Errorcode = JABORT;
252					LOGERR_DIE(LOG_INFO) "Logger: lseek failed read file '%s'", tempfile);
253				}
254				if( ftruncate( status_fd, 0 ) ){
255					Errorcode = JABORT;
256					LOGERR_DIE(LOG_INFO) "Logger: ftruncate failed file '%s'", tempfile);
257				}
258				input_read = 0;
259			}
260		}
261		/* now lets see if the input has been closed
262		 * do not exit until you have sent last buffer information
263		 */
264		if( readfd < 0 && outlen == 0 ){
265			DEBUGF(DLOG2)("Logger: exiting - no work to do");
266			Errorcode = 0;
267			break;
268		}
269		/* the destination is not on line yet
270		 * try to reopen
271		 */
272		if( writefd < 0 ){
273			time( &current_time );
274			elapsed = current_time - start_time;
275			left = timeout - elapsed;
276			DEBUGF(DLOG2)("Logger: writefd fd %d, max timeout %d, left %d",
277					writefd, timeout, left );
278			if( left <= 0 || writefd == -2 ){
279				writefd = Link_open(host, Connect_timeout_DYN, 0, 0 );
280				DEBUGF(DLOG2)("Logger: open fd %d", writefd );
281				if( writefd >= 0 ){
282					Set_nonblock_io( writefd );
283					if( lseek( status_fd, 0, SEEK_SET) == -1 ){
284						Errorcode = JABORT;
285						LOGERR_DIE(LOG_INFO) "Logger: lseek failed write file '%s'", tempfile);
286					}
287					if( lseek( input_fd, 0, SEEK_SET) == -1 ){
288						Errorcode = JABORT;
289						LOGERR_DIE(LOG_INFO) "Logger: lseek failed read file '%s'", tempfile);
290					}
291					if( ftruncate( status_fd, 0 ) ){
292						Errorcode = JABORT;
293						LOGERR_DIE(LOG_INFO) "Logger: ftruncate failed file '%s'", tempfile);
294					}
295					if( Dump_queue_status(status_fd) ){
296						DEBUGF(DLOG2)("Logger: Dump_queue_status failed - %s", Errormsg(errno) );
297						Errorcode = JABORT;
298						LOGERR_DIE(LOG_INFO) "Logger: cannot write file '%s'", tempfile);
299					}
300					input_read = 1;
301					/* we try again */
302					continue;
303				} else {
304					writefd = -1;
305				}
306				time( &start_time );
307				time( &current_time );
308				DEBUGF(DLOG2)("Logger: writefd now fd %d", writefd );
309			}
310			if( writefd < 0 && timeout > 0 ){
311				memset( &timeval, 0, sizeof(timeval) );
312				elapsed = current_time - start_time;
313				left = timeout - elapsed;
314				timeval.tv_sec = left;
315				tp = &timeval;
316				DEBUGF(DLOG2)("Logger: timeout now %d", left );
317			}
318		}
319		FD_ZERO( &writefds );
320		FD_ZERO( &readfds );
321		m = 0;
322		if( writefd >= 0 ){
323			if( outlen ){
324				FD_SET( writefd, &writefds );
325				if( m <= writefd ) m = writefd+1;
326			}
327			FD_SET( writefd, &readfds );
328			if( m <= writefd ) m = writefd+1;
329		}
330		if( readfd >= 0 ){
331			FD_SET( readfd, &readfds );
332			if( m <= readfd ) m = readfd+1;
333		}
334		errno = 0;
335		DEBUGF(DLOG2)("Logger: starting select, timeout '%s', left %d",
336			tp?"yes":"no", left );
337        m = select( m,
338            FD_SET_FIX((fd_set *))&readfds,
339            FD_SET_FIX((fd_set *))&writefds,
340            FD_SET_FIX((fd_set *))0, tp );
341		err = errno;
342		DEBUGF(DLOG2)("Logger: select returned %d, errno '%s'",
343			m, Errormsg(err) );
344		if( m < 0 ){
345			if( err != EINTR ){
346				Errorcode = JABORT;
347				LOGERR_DIE(LOG_INFO)"Logger: select error");
348			}
349		} else if( m > 0 ){
350			if( writefd >=0 && FD_ISSET( writefd, &readfds ) ){
351				/* we have EOF on the file descriptor */
352				DEBUGF(DLOG2)("Logger: eof on writefd fd %d", writefd );
353				close( writefd );
354				outlen = 0;
355				writefd = -2;
356			}
357			if( readfd >=0 && FD_ISSET( readfd, &readfds ) ){
358				DEBUGF(DLOG2)("Logger: read possible on fd %d", readfd );
359				inbuffer[0] = 0;
360				m = read( readfd, inbuffer, sizeof(inbuffer)-1 );
361				if( m >= 0) inbuffer[m] = 0;
362				DEBUGF(DLOG2)("Logger: read count %d '%s'", m, inbuffer );
363				if( m > 0 && writefd >= 0 ){
364					if( Write_fd_len( status_fd, inbuffer, m ) ){
365						LOGERR_DIE(LOG_INFO)"Logger: write error on tempfile fd %d", status_fd);
366					}
367					input_read = 1;
368				} else if( m == 0 ) {
369					/* we have a 0 length read - this is EOF */
370					Errorcode = 0;
371					DEBUGF(DLOG1)("Logger: eof on input fd %d", readfd);
372					close(readfd);
373					readfd = -1;
374				} else if( m < 0 ){
375					Errorcode = JABORT;
376					LOGERR_DIE(LOG_INFO)"Logger: read error on input fd %d", readfd);
377				}
378			}
379			if( writefd >=0 && FD_ISSET( writefd, &writefds ) && outlen ){
380				DEBUGF(DLOG2)("Logger: write possible on fd %d, outlen %d",
381					writefd, outlen );
382				m = write( writefd, outbuffer, outlen);
383				DEBUGF(DLOG2)("Logger: last write %d", m );
384				if( m < 0 ){
385					/* we have EOF on the file descriptor */
386					LOGERR(LOG_INFO) "Logger: error writing on writefd fd %d", writefd );
387					close( writefd );
388					writefd = -2;
389				} else if( m > 0 ){
390					memmove(outbuffer, outbuffer+m, outlen-m+1 );
391					outlen -= m;
392				}
393			}
394		}
395	}
396	cleanup(0);
397}
398