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_jobs.c,v 1.1.1.1 2008/10/15 03:28:27 james26_jang Exp $";
28
29#include "lp.h"
30#include "accounting.h"
31#include "child.h"
32#include "errorcodes.h"
33#include "fileopen.h"
34#include "gethostinfo.h"
35#include "getopt.h"
36#include "getprinter.h"
37#include "getqueue.h"
38#include "linksupport.h"
39#include "lockfile.h"
40#include "lpd_remove.h"
41#include "merge.h"
42#include "permission.h"
43#include "printjob.h"
44#include "proctitle.h"
45#include "sendjob.h"
46#include "sendmail.h"
47#include "stty.h"
48
49#include "lpd_jobs.h"
50#include "lpd_rcvjob.h"
51
52#if defined(USER_INCLUDE)
53# include USER_INCLUDE
54#else
55# if defined(CHOOSER_ROUTINE)
56#   error No 'USER_INCLUDE' file with function prototypes specified
57    You need an include file with function prototypes
58# endif
59#endif
60
61
62/**** ENDINCLUDE ****/
63
64#ifdef REMOVE
65/***************************************************************************
66 * Commentary:
67 * Patrick Powell Thu May 11 09:26:48 PDT 1995
68 *
69 * Job processing algorithm
70 *
71 * 1. Check to see if there is already a spooler process active.
72 *    The active file will contain the PID of the active spooler process.
73 * 2. Build the job queue
74 * 3. For each job in the job queue, service them.
75 *
76 * MULTIPLE Servers for a Single Queue.
77 * In the printcap, the "sv" flag sets the Server_names_DYN variable with
78 * the list of servers to be used for this queue.  The "ss"  flag sets
79 * the Server_queue_name_DYN flag with the queue that this is a server for.
80 *
81 * Under normal conditions, the following process hierarchy is used:
82 *
83 *  server process - printer 'spool queue'
84 *     subserver process - first printer in 'Server_name'
85 *     subserver process - second printer in 'Server_name'
86 *     ...
87 *
88 * The server process does the following:
89 *   for each printer in the Server_name list
90 *      sort them by the last order that you had in the control file
91 *   for each printer in the Server_name list
92 *      check the status of the queue, and gets the control file
93 *      information and the numbers of jobs waiting.
94 *   for each printer in the Server_name list
95 *      if printable jobs and printing enabled
96 *      start up a subserver
97 *
98 *   while(1){
99 * 	for all printable jobs do
100 * 		check to see if there is a queue for them to be
101 * 		printed on and the the queue is not busy;
102 * 	if( job does not need a server ) then
103 * 		do whatever is needed;
104 * 		update job status();
105 * 	else if( no jobs to print and no servers active ) then
106 * 		break;
107 * 	else if( no jobs to print or server active ) then
108 * 		wait for server to exit();
109 * 		if( pid is for server printing job)
110 * 			update job status();
111 * 			update server status();
112 * 	else
113 * 		dofork(0) a server process;
114 * 		record pid of server doing work;
115 * 	endif
116 *   }
117 *
118 * We then check to see if we are a slave (sv) to a master spool queue;
119 * if we are and we are not a child process of the 'master' server,
120 * we exit.
121 *
122 * Note: if we spool something to a slave queue,  then we need to start
123 * the master server to make the slave printer work.
124 *
125 * Note: the slave queue processes
126 * will not close the masters lock files;  this means a new master
127 * cannot start serving the queue until all the slaves are dead.
128 * Why this action do you ask?  The reason is that it is difficult
129 * for a new master to inherit slaves from a dead master.
130 *
131 * It turns out that many implementations of some network
132 * based databased systems and other network database routines are broken,
133 * and have memory leaks or never close file descriptors.  Up to the point
134 * where the loop for checking the queue starts,  there is a known number
135 * of file descriptors open,  and dynamically allocated memory.
136 * After this,  it is difficult to predict just what is going to happen.
137 * By forking a 'subserver' process, we firewall the actual memory and
138 * file descriptor screwups into the subserver process.
139 *
140 * When the subserver exits, it returns an error code that the server
141 * process then interprets.  This error code is used to either remove the job
142 * or retry it.
143 *
144 * Note that there are conditions under which a job cannot be removed.
145 * We simply abort at that point and let higher level authority (admins)
146 * deal with this.
147 *
148 ***************************************************************************/
149
150
151/*
152 * Signal handler to set flags and terminate system calls
153 *  NOTE: use 'volatile' so that the &*()()&* optimizing compilers
154 *  handle the value correctly.
155 */
156
157 static volatile int Susr1, Chld;
158
159 static void Sigusr1(void)
160{
161	++Susr1;
162	(void) plp_signal_break(SIGUSR1,  (plp_sigfunc_t)Sigusr1);
163	return;
164}
165
166 static void Sigchld(void)
167{
168	++Chld;
169	signal( SIGCHLD, SIG_DFL );
170	return;
171}
172
173
174/***************************************************************************
175 * Update_spool_info()
176 *  get updated spool control file information
177 ***************************************************************************/
178
179
180void Update_spool_info( struct line_list *sp )
181{
182	struct line_list info;
183	char *sc;
184
185	Init_line_list(&info);
186
187	Set_str_value(&info,SPOOLDIR, Find_str_value(sp,SPOOLDIR,Value_sep) );
188	Set_str_value(&info,PRINTER, Find_str_value(sp,PRINTER,Value_sep) );
189	Set_str_value(&info,QUEUE_CONTROL_FILE, Find_str_value(sp,QUEUE_CONTROL_FILE,Value_sep) );
190	Set_str_value(&info,HF_NAME, Find_str_value(sp,HF_NAME,Value_sep) );
191	Set_str_value(&info,IDENTIFIER, Find_str_value(sp,IDENTIFIER,Value_sep) );
192	Set_str_value(&info,SERVER, Find_str_value(sp,SERVER,Value_sep) );
193	Set_str_value(&info,DONE_TIME, Find_str_value(sp,DONE_TIME,Value_sep) );
194
195	sc = Find_str_value(&info,QUEUE_CONTROL_FILE,Value_sep);
196
197	DEBUG1("Update_spool_info: file '%s'", sc );
198
199	Free_line_list(sp);
200	Get_spool_control(sc,sp);
201	Merge_line_list(sp,&info,Value_sep,1,1);
202	Free_line_list(&info);
203}
204
205int cmp_server( const void *left, const void *right, const void *p )
206{
207    struct line_list *l, *r;
208	int tr, tl;
209	l = ((struct line_list **)left)[0];
210	r = ((struct line_list **)right)[0];
211	tl = Find_flag_value(l,DONE_TIME,Value_sep);
212	tr = Find_flag_value(r,DONE_TIME,Value_sep);
213#ifdef ORIGINAL_DEBUG//JY@1020
214	if(DEBUGL5)Dump_line_list("cmp_server - l",l);
215	if(DEBUGL5)Dump_line_list("cmp_server - r",r);
216	DEBUG5("cmp_server: tl %d, tr %d, cmp %d, p %d",
217		tl, tr, tl - tr, (int)(p!=0) );
218#endif
219	return( tl - tr );
220}
221
222
223void Get_subserver_pc( char *printer, struct line_list *subserver_info, int done_time )
224{
225	int printable, held, move, err, done;
226	char *path;
227	char buffer[SMALLBUFFER];
228
229	printable = held = move = err = done = 0;
230
231	DEBUG1("Get_subserver_pc: '%s'", printer );
232	buffer[0] = 0;
233	if( Setup_printer( printer, buffer, sizeof(buffer), 1 ) ){
234		Errorcode = JABORT;
235		FATAL(LOG_ERR) "Get_subserver_pc: '%s' - '%s'", printer, buffer);
236	}
237
238	Set_str_value(subserver_info,PRINTER,Printer_DYN);
239	Set_str_value(subserver_info,SPOOLDIR,Spool_dir_DYN);
240	path = Make_pathname( Spool_dir_DYN, Queue_control_file_DYN );
241	Set_str_value(subserver_info,QUEUE_CONTROL_FILE,path);
242	if( path ) free(path); path = 0;
243
244	Update_spool_info( subserver_info );
245
246	DEBUG1("Get_subserver_pc: scanning '%s'", Spool_dir_DYN );
247	Scan_queue( subserver_info, 0, &printable, &held, &move, 1, &err, &done, 0, 0);
248	Set_flag_value(subserver_info,PRINTABLE,printable);
249	Set_flag_value(subserver_info,HELD,held);
250	Set_flag_value(subserver_info,MOVE,move);
251	Set_flag_value(subserver_info,DONE_TIME,done_time);
252	if( !(Save_when_done_DYN || Save_on_error_DYN )
253		&& (Done_jobs_DYN || Done_jobs_max_age_DYN)
254		&& (err || done ) ){
255		Set_flag_value(subserver_info,DONE_REMOVE,1);
256	}
257
258	DEBUG1("Get_subserver_pc: printable %d, held %d, move %d, done_remove %d, fowarding '%s'",
259		printable, held, move,
260		Find_flag_value(subserver_info,DONE_REMOVE,Value_sep),
261		Find_str_value(subserver_info,FORWARDING,Value_sep) );
262}
263
264#ifdef ORIGINAL_DEBUG//JY@1020
265/***************************************************************************
266 * Dump_subserver_info()
267 *  dump the server information list
268 ***************************************************************************/
269
270void Dump_subserver_info( char *title, struct line_list *l)
271{
272	char buffer[LINEBUFFER];
273	int i;
274	LOGDEBUG("*** Dump_subserver_info: '%s' - %d subservers",
275		title,	l->count );
276	for( i = 0; i < l->count; ++i ){
277		SNPRINTF(buffer,sizeof(buffer))"server %d",i);
278#ifdef ORIGINAL_DEBUG//JY@1020
279		Dump_line_list_sub(buffer,(struct line_list *)l->list[i]);
280#endif
281	}
282}
283#endif
284
285/***************************************************************************
286 * Get_subserver_info()
287 *  hack up the server information list into a list of servers
288 ***************************************************************************/
289
290void Get_subserver_info( struct line_list *order,
291	char *list, char *old_order)
292{
293	struct line_list server_order, server, *pl;
294	int i;
295	char *s;
296
297	Unescape( old_order ); /* this is ugly - we make it forwards compatible */
298	Init_line_list(&server_order);
299	Init_line_list(&server);
300
301	DEBUG1("Get_subserver_info: old_order '%s', list '%s'",old_order, list);
302	Split(&server_order,old_order,File_sep,0,0,0,1,0,0);
303	Split(&server_order,     list,File_sep,0,0,0,1,0,0);
304#ifdef ORIGINAL_DEBUG//JY@1020
305	if(DEBUGL1)Dump_line_list("Get_subserver_info - starting",&server_order);
306#endif
307
308	/* get the info of printers */
309	for( i = 0; i < server_order.count; ++i ){
310		s = server_order.list[i];
311		DEBUG1("Get_subserver_info: doing '%s'",s);
312		if( Find_str_value(&server,s,Value_sep) ){
313			DEBUG1("Get_subserver_info: already done '%s'",s);
314			continue;
315		}
316		pl = malloc_or_die(sizeof(pl[0]),__FILE__,__LINE__);
317		Init_line_list(pl);
318		Get_subserver_pc( s, pl, i+1 );
319		Check_max(order,1);
320		DEBUG1("Get_subserver_info: adding to list '%s' at %d",s,order->count);
321		order->list[order->count++] = (char *)pl;
322		Set_str_value(&server,s,s);
323		pl = 0;
324	}
325	Free_line_list(&server_order);
326	Free_line_list(&server);
327#ifdef ORIGINAL_DEBUG//JY@1020
328	if(DEBUGL1)Dump_subserver_info("Get_subserver_info - starting order",order);
329#endif
330}
331
332/***************************************************************************
333 * Make_temp_copy - make a temporary copy in the directory
334 ***************************************************************************/
335
336char *Make_temp_copy( char *srcfile, char *destdir )
337{
338	char buffer[LARGEBUFFER];
339	char *path = 0;
340	struct stat statb;
341	int srcfd, destfd, fail, n, len, count;
342
343	fail = 0;
344	srcfd = destfd = -1;
345
346	DEBUG3("Make_temp_copy: '%s' to '%s'", srcfile, destdir);
347
348	destfd = Make_temp_fd_in_dir(&path, destdir);
349	unlink(path);
350	if( link( srcfile, path ) == -1 ){
351#ifdef ORIGINAL_DEBUG//JY@1020
352		DEBUG3("Make_temp_copy: link '%s' to '%s' failed, '%s'",
353			srcfile, path, Errormsg(errno) );
354#endif
355		srcfd = Checkread(srcfile, &statb );
356		if( srcfd < 0 ){
357			LOGERR(LOG_INFO)"Make_temp_copy: open '%s' failed", srcfile );
358			fail = 1;
359			goto error;
360		}
361		while( (n = read(srcfd,buffer,sizeof(buffer))) > 0 ){
362			for( count = len = 0; len < n
363				&& (count = write(destfd, buffer+len,n-len)) > 0;
364				len += count );
365			if( count < 0 ){
366				LOGERR(LOG_INFO)"Make_temp_copy: copy to '%s' failed", path );
367				fail = 1;
368				goto error;
369			}
370		}
371	}
372
373 error:
374	if( fail ){
375		unlink(path); path = 0;
376	}
377	if( srcfd >= 0 ) close(srcfd); srcfd = -1;
378	if( destfd >= 0 ) close(destfd); destfd = -1;
379	return( path );
380}
381
382/***************************************************************************
383 * Do_queue_jobs: process the job queue
384 ***************************************************************************/
385
386 static int Done_count;
387 static time_t Done_time;
388
389int Do_queue_jobs( char *name, int subserver )
390{
391	int master = 0;		/* this is the master */
392	int opened_logfile = 0;	/* we have not opened the log file */
393	int lock_fd;	/* fd for files */
394	char buffer[SMALLBUFFER], savename[SMALLBUFFER], errormsg[SMALLBUFFER];
395	char *path, *s, *id, *tempfile, *transfername, *openname,
396		*new_dest, *move_dest, *pr, *hf_name, *sd, *from, *forwarding;
397	struct stat statb;
398	int i, j, mod, fd, pid, printable, held, move, destinations,
399		destination, use_subserver, job_to_do, working, printing_enabled,
400		all_done, fail, job_index, change, in_tempfd, out_tempfd, len,
401		chooser_did_not_find_server, error, done, done_remove, check_for_done;
402	struct line_list servers, tinfo, *sp, *datafile, chooser_list, chooser_env;
403	plp_block_mask oblock;
404	struct job job;
405	int jobs_printed = 0;
406
407	Init_line_list(&tinfo);
408
409	lock_fd = -1;
410
411	Init_job(&job);
412	Init_line_list(&servers);
413	Init_line_list(&chooser_list);
414	Init_line_list(&chooser_env);
415	id = transfername = 0;
416	in_tempfd = out_tempfd = -1;
417
418	Name = "(Server)";
419	Set_DYN(&Printer_DYN,name);
420	DEBUG1("Do_queue_jobs: called with name '%s', subserver %d",
421		Printer_DYN, subserver );
422	name = Printer_DYN;
423
424	if(DEBUGL4){ int fdx; fdx = dup(0); LOGDEBUG("Do_queue_jobs: start next fd %d",fdx); close(fdx); };
425
426 begin:
427	Set_DYN(&Printer_DYN,name);
428	DEBUG1("Do_queue_jobs: begin name '%s'", Printer_DYN );
429	tempfile = 0;
430	Free_listof_line_list( &servers );
431	if( lock_fd != -1 ) close( lock_fd ); lock_fd = -1;
432
433	Errorcode = JABORT;
434	/* you need to have a spool queue */
435	if( Setup_printer( Printer_DYN, buffer, sizeof(buffer), 0 ) ){
436		cleanup(0);
437	}
438	if(DEBUGL4){ int fdx; fdx = dup(0);
439	LOGDEBUG("Do_queue_jobs: after Setup_printer next fd %d",fdx); close(fdx); };
440
441	setproctitle( "lpd %s '%s'", Name, Printer_DYN );
442
443 again:
444	/* block signals */
445	plp_block_one_signal(SIGCHLD, &oblock);
446	plp_block_one_signal(SIGUSR1, &oblock);
447	(void) plp_signal(SIGCHLD,  SIG_DFL);
448	(void) plp_signal(SIGUSR1,  (plp_sigfunc_t)Sigusr1);
449	Susr1 = 0;
450
451	path = Make_pathname( Spool_dir_DYN, Queue_lock_file_DYN );
452	DEBUG1( "Do_queue_jobs: checking lock file '%s'", path );
453	lock_fd = Checkwrite( path, &statb, O_RDWR, 1, 0 );
454	if( lock_fd < 0 ){
455		LOGERR_DIE(LOG_ERR) _("Do_queue_jobs: cannot open lockfile '%s'"),
456			path );
457	}
458	if(path) free(path); path = 0;
459
460	/*
461	This code is very tricky,  and may cause some headaches
462	First, you want to make sure that you have an active process
463	to do the unspooling.  If you CAN lock the lock file, then you
464	are the active process.  If you CANNOT lock the lock file
465	then some other process is the active process.
466
467	If some other process is the active process then you want to
468	make sure that you signal it.  When the process is exiting
469	then it will first truncate lock file and then close it.
470	We make the brutal assumption that from the time that the
471	process truncates the log file until it exits will be less than
472	the time taken for this process to read the file and send a signal.
473	We help a bit by having the exiting process sleep 2 seconds -
474	i.e. - we make sure that it will take a fairish time.
475
476	This makes sure that processes that read the PID before it is
477	truncated will send the signal to an existing processes.
478	*/
479
480	while( Do_lock( lock_fd, 0 ) < 0 ){
481		pid = Read_pid( lock_fd, (char *)0, 0 );
482		DEBUG1( "Do_queue_jobs: server process '%d' may be active", pid );
483		if( pid == 0 || kill( pid, SIGUSR1 ) ){
484			plp_usleep(1000);
485			continue;
486		}
487		Errorcode = 0;
488		cleanup(0);
489	}
490	pid = getpid();
491	DEBUG1( "Do_queue_jobs: writing lockfile '%s' with pid '%d'",
492		Queue_lock_file_DYN, pid );
493	Write_pid( lock_fd, pid, (char *)0 );
494
495	/* we now now new queue status so we force update */
496	if( Lpq_status_file_DYN ){
497		unlink(Lpq_status_file_DYN);
498	}
499	if( Log_file_DYN && !opened_logfile ){
500		fd = Trim_status_file( -1, Log_file_DYN, Max_log_file_size_DYN,
501			Min_log_file_size_DYN );
502		if( fd > 0 && fd != 2 ){
503			dup2(fd,2);
504			close(fd);
505		}
506		opened_logfile = 1;
507	}
508
509	s = Find_str_value(&Spool_control,DEBUG,Value_sep);
510	if(!s) s = New_debug_DYN;
511	Parse_debug( s, 0);
512
513	if( Server_queue_name_DYN ){
514		if( subserver == 0 ){
515			/* you really need to start up the master queue */
516			name = Server_queue_name_DYN;
517			DEBUG1("Do_queue_jobs: starting up master queue '%s'", name );
518			goto begin;
519		}
520		Name = "(Sub)";
521	}
522	/* set up the server name information */
523	Check_max(&servers,1);
524	sp = malloc_or_die(sizeof(sp[0]),__FILE__,__LINE__);
525	memset(sp,0,sizeof(sp[0]));
526	Set_str_value(sp,PRINTER,Printer_DYN);
527	Set_str_value(sp,SPOOLDIR,Spool_dir_DYN);
528	Set_str_value(sp,QUEUE_CONTROL_FILE,Queue_control_file_DYN);
529	servers.list[servers.count++] = (char *)sp;
530	Update_spool_info(sp);
531
532	change = Find_flag_value(&Spool_control,CHANGE,Value_sep);
533	if( change ){
534		Set_flag_value(sp,CHANGE,0);
535		Set_flag_value(&Spool_control,CHANGE,0);
536		Set_spool_control(0, Queue_control_file_DYN, &Spool_control);
537	}
538
539	master = 0;
540	if( !ISNULL(Server_names_DYN) ){
541		DEBUG1( "Do_queue_jobs: Server_names_DYN '%s', Server_order '%s'",
542			Server_names_DYN, Srver_order(&Spool_control) );
543		if( Server_queue_name_DYN ){
544			Errorcode = JABORT;
545			FATAL(LOG_ERR)"Do_queue_jobs: serving '%s' and subserver for '%s'",
546				Server_queue_name_DYN, Server_names_DYN );
547		}
548
549		/* save the Printer_DYN name */
550		safestrncpy(savename,Printer_DYN);
551		/* we now get the subserver information */
552		Get_subserver_info( &servers,
553			Server_names_DYN, Srver_order(&Spool_control) );
554
555		/* reset the main printer */
556		if( Setup_printer( savename, buffer, sizeof(buffer), 0 ) ){
557			cleanup(0);
558		}
559
560		master = 1;
561		/* start the queues that need it */
562		for( i = 1; i < servers.count; ++i ){
563			sp = (void *)servers.list[i];
564			pr = Find_str_value(sp,PRINTER,Value_sep);
565			DEBUG1("Do_queue_jobs: subserver '%s' checking for independent action", pr );
566			if( (s = Find_str_value(sp,SERVER,Value_sep)) ){
567				DEBUG1("Do_queue_jobs: subserver '%s' active server '%s'", pr,s );
568			}
569			printable = !Pr_disabled(sp) && !Pr_aborted(sp)
570				&& Find_flag_value(sp,PRINTABLE,Value_sep);
571			move = Find_flag_value(sp,MOVE,Value_sep);
572			forwarding = Find_str_value(sp,FORWARDING,Value_sep);
573			change = Find_flag_value(sp,CHANGE,Value_sep);
574			DEBUG1("Do_queue_jobs: subserver '%s', printable %d, move %d, forwarding '%s'",
575				pr, printable, move, forwarding );
576			/* now see if we need to clean up the old jobs */
577			done_remove = Find_flag_value(sp,DONE_REMOVE,Value_sep);
578
579			if( printable || move || change || forwarding || done_remove ){
580				pid = Fork_subserver( &servers, i, 0 );
581				jobs_printed = 1;
582			}
583			Set_flag_value(sp,CHANGE,0);
584		}
585	}
586
587
588#ifdef ORIGINAL_DEBUG//JY@1020
589	if(DEBUGL3)Dump_subserver_info("Do_queue_jobs - after setup",&servers);
590#endif
591
592	if(DEBUGL4){ int fdx; fdx = dup(0);
593		LOGDEBUG("Do_queue_jobs: after subservers next fd %d",fdx);close(fdx);};
594	/* get new job values */
595	if( Scan_queue( &Spool_control, &Sort_order,
596			&printable, &held, &move, 1, &error, &done, 0, 0 ) ){
597		LOGERR_DIE(LOG_ERR)"Do_queue_jobs: cannot read queue '%s'",
598			Spool_dir_DYN );
599	}
600
601	DEBUG1( "Do_queue_jobs: printable %d, held %d, move %d, err %d, done %d",
602		printable, held, move, error, done );
603
604	if(DEBUGL1){ i = dup(0);
605		LOGDEBUG("Do_queue_jobs: after Scan_queue next fd %d", i); close(i); }
606
607	/* remove junk fields from job information */
608	for( i = 0; i < Sort_order.count; ++i ){
609		/* fix up the sort stuff */
610		Free_job(&job);
611		Get_hold_file(&job,Sort_order.list[i] );
612#ifdef ORIGINAL_DEBUG//JY@1020
613		if(DEBUGL3)Dump_job("Do_queue_jobs - info", &job);
614#endif
615
616		/* debug output */
617		mod = 0;
618		if( Find_flag_value(&job.info,SERVER,Value_sep ) ){
619			Set_decimal_value(&job.info,SERVER,0);
620			mod = 1;
621		}
622		if((destinations = Find_flag_value(&job.info,DESTINATIONS,Value_sep))){
623			if( Find_str_value(&job.info,DESTINATION,Value_sep ) ){
624				Set_str_value(&job.info,DESTINATION,0);
625				mod = 1;
626			}
627			for( j = 0; j < destinations; ++j ){
628				Get_destination(&job,j);
629				if( Find_flag_value(&job.destination,SERVER,Value_sep) ){
630					mod = 1;
631					Set_decimal_value(&job.destination,SERVER,0);
632					Update_destination(&job);
633				}
634			}
635		}
636		if( mod ) Set_hold_file(&job, 0, 0 );
637	}
638
639
640	Free_job(&job);
641
642	check_for_done = 1;
643
644	while(1){
645		DEBUG1( "Do_queue_jobs: MAIN LOOP" );
646		if(DEBUGL4){ int fdx; fdx = dup(0);
647		LOGDEBUG("Do_queue_jobs: MAIN LOOP next fd %d",fdx); close(fdx); };
648		Unlink_tempfiles();
649
650		/* check for changes to spool control information */
651		plp_unblock_all_signals( &oblock );
652		plp_set_signal_mask( &oblock, 0 );
653
654		if( (Done_jobs_DYN > 0 && Done_count > Done_jobs_DYN)
655			 || (Done_jobs_max_age_DYN > 0
656					&& Done_time
657					&& (time(0) - Done_time) > Done_jobs_max_age_DYN) ){
658			Susr1 = 1;
659		}
660		DEBUG1( "Do_queue_jobs: Susr1 before scan %d, check_for_done %d",
661			Susr1, check_for_done );
662		while( Susr1 ){
663			Susr1 = 0;
664			Done_time = 0;
665			Done_count = 0;
666			DEBUG1( "Do_queue_jobs: rescanning" );
667
668			Get_spool_control( Queue_control_file_DYN, &Spool_control);
669			if( Scan_queue( &Spool_control, &Sort_order,
670					&printable, &held, &move, 1, &error, &done, 0, 0 ) ){
671				LOGERR_DIE(LOG_ERR)"Do_queue_jobs: cannot read queue '%s'",
672					Spool_dir_DYN );
673			}
674			DEBUG1( "Do_queue_jobs: printable %d, held %d, move %d, error %d, done %d",
675				printable, held, move, error, done );
676
677			for( i = 0; i < servers.count; ++i ){
678				sp = (void *)servers.list[i];
679				Update_spool_info( sp );
680				change = Find_flag_value(sp,CHANGE,Value_sep);
681				pid = Find_flag_value(sp,SERVER,Value_sep);
682				if( i > 0 && change && pid == 0 ){
683					pid = Fork_subserver( &servers, i, 0 );
684					jobs_printed = 1;
685				}
686				Set_flag_value(sp,CHANGE,0);
687			}
688#ifdef ORIGINAL_DEBUG//JY@1020
689			if(DEBUGL1) Dump_subserver_info( "Do_queue_jobs - rescan",
690				&servers );
691#endif
692
693			/* check for changes to spool control information */
694			plp_unblock_all_signals( &oblock);
695			plp_set_signal_mask( &oblock, 0);
696			DEBUG1( "Do_queue_jobs: Susr1 at end of scan %d", Susr1 );
697			/* now check to see if you remove jobs */
698			check_for_done = 0;
699			if( !(Save_when_done_DYN || Save_on_error_DYN )
700				&& (Done_jobs_DYN || Done_jobs_max_age_DYN)
701				&& (error || done) ){
702				check_for_done = 1;
703			}
704		}
705
706#ifdef ORIGINAL_DEBUG//JY@1020
707		if(DEBUGL4) Dump_line_list("Do_queue_jobs - sort order printable",
708			&Sort_order );
709#endif
710
711		Remove_done_jobs();
712
713		/* make sure you can print */
714		printing_enabled
715			= !(Pr_disabled(&Spool_control) || Pr_aborted(&Spool_control));
716		forwarding = Find_str_value(&Spool_control,FORWARDING,Value_sep);
717		DEBUG3("Do_queue_jobs: printing_enabled '%d', forwarding '%s'",
718			printing_enabled, forwarding );
719
720		openname = transfername = hf_name = id = move_dest = new_dest = 0;
721		destination = use_subserver = job_to_do = -1;
722		working =  destinations = chooser_did_not_find_server = 0;
723
724#ifdef ORIGINAL_DEBUG//JY@1020
725		if(DEBUGL2) Dump_subserver_info("Do_queue_jobs- checking for server",
726			&servers );
727#endif
728		for( j = 0; j < servers.count; ++j ){
729			sp = (void *)servers.list[j];
730			pid = Find_flag_value(sp,SERVER,Value_sep);
731			pr = Find_str_value(sp,PRINTER,Value_sep);
732			DEBUG2("Do_queue_jobs: printer '%s', server %d", pr, pid );
733			if( pid ){
734				++working;
735			}
736		}
737
738		for( job_index = 0; job_to_do < 0 && job_index < Sort_order.count;
739			++job_index ){
740
741			Free_job(&job);
742			id = move_dest = new_dest = 0;
743			destination = use_subserver = job_to_do = -1;
744			destinations = 0;
745
746			if( !Sort_order.list[job_index] ) continue;
747			DEBUG3("Do_queue_jobs: job_index [%d] '%s'", job_index,
748				Sort_order.list[job_index] );
749			Get_hold_file( &job, Sort_order.list[job_index] );
750
751#ifdef ORIGINAL_DEBUG//JY@1020
752			if(DEBUGL4)Dump_job("Do_queue_jobs: job ",&job);
753#endif
754			if( job.info.count == 0 ) continue;
755
756			/* check to see if active */
757			if( (pid = Find_flag_value(&job.info,SERVER,Value_sep)) ){
758				DEBUG3("Do_queue_jobs: [%d] active %d", job_index, pid );
759				continue;
760			}
761
762			/* get printable status */
763			Setup_cf_info( &job, 1 );
764			Job_printable(&job,&Spool_control,&printable,&held,&move,&error,&done);
765			if( (!(printable && (printing_enabled || forwarding)) && !move) || held ){
766				DEBUG3("Do_queue_jobs: [%d] not processable", job_index );
767				/* free( Sort_order.list[job_index] ); Sort_order.list[job_index] = 0; */
768				continue;
769			}
770			if( Check_print_perms(&job) == P_REJECT ){
771				Set_str_value(&job.info,ERROR,"no permission to print");
772				Set_nz_flag_value(&job.info,ERROR_TIME,time(0));
773				if( Set_hold_file( &job, 0, 0 ) ){
774					/* you cannot update hold file!! */
775					setstatus( &job, _("cannot update hold file for '%s'"),
776						id);
777					FATAL(LOG_ERR)
778						_("Do_queue_jobs: cannot update hold file for '%s'"),
779						id);
780				}
781				if( !(Save_on_error_DYN || Done_jobs_DYN || Done_jobs_max_age_DYN) ){
782					setstatus( &job, _("removing job '%s' - no permissions"), id);
783					Remove_job( &job );
784					free( Sort_order.list[job_index] ); Sort_order.list[job_index] = 0;
785				}
786				continue;
787			}
788
789			/* get destination information */
790			destinations = Find_flag_value(&job.info,DESTINATIONS,Value_sep);
791			if( !destinations ){
792				move_dest = new_dest = Find_str_value(&job.info,MOVE,Value_sep);
793				if( !new_dest ) new_dest = Frwarding(&Spool_control);
794			} else {
795				all_done = 0;
796				for( j = 0; !new_dest && j < destinations; ++j ){
797					Get_destination(&job,j);
798					if( Find_flag_value(&job.destination,SERVER,Value_sep) ){
799						break;
800					}
801					if( Find_flag_value(&job.destination,DONE_TIME,Value_sep ) ){
802						++all_done;
803						continue;
804					}
805					if( Find_flag_value(&job.destination,ERROR_TIME,Value_sep)
806						|| Find_flag_value(&job.destination,HOLD_TIME,Value_sep) ){
807						continue;
808					}
809					if( (move_dest = new_dest = Find_str_value( &job.destination,
810						MOVE,Value_sep)) ){
811						destination = j;
812						break;
813					}
814					if( Find_flag_value(&job.destination, PRINTABLE, Value_sep )
815						&& printing_enabled ){
816						new_dest = Find_str_value( &job.destination,
817							DEST,Value_sep );
818						destination = j;
819						break;
820					}
821				}
822				if( !new_dest ){
823					printable = 0;
824				}
825				if( all_done == destinations ){
826					DEBUG3("Do_queue_jobs: destinations %d, done %d",
827						destinations, all_done );
828					Update_status( &job, JSUCC );
829					continue;
830				}
831			}
832
833			DEBUG3("Do_queue_jobs: new_dest '%s', printable %d, master %d, destinations %d, destination %d",
834				new_dest, printable, master, destinations, destination );
835			if( new_dest ){
836				sp = (void *)servers.list[0];
837				if( Find_flag_value(sp,SERVER,Value_sep) ){
838					continue;
839				}
840				use_subserver = 0;
841				job_to_do = job_index;
842			} else if( printing_enabled && printable ){
843				/*
844				 * find the subserver with a class that will print this job
845				 * if master = 1, then we start with 1
846				 */
847				Free_line_list( &chooser_list );
848				Free_line_list( &chooser_env );
849				DEBUG1("Do_queue_jobs: chooser '%s', chooser_routine %lx",
850					Chooser_DYN, Cast_ptr_to_long(Chooser_routine_DYN) );
851				for( j = master; use_subserver < 0 && j < servers.count; ++j ){
852					sp = (void *)servers.list[j];
853					s = Find_str_value(sp,PRINTER,Value_sep);
854					DEBUG1("Do_queue_jobs: checking '%s'", s );
855					if( Pr_disabled(sp)
856						|| Pr_aborted(sp)
857						|| Sp_disabled(sp)
858						|| Find_flag_value(sp,SERVER,Value_sep)){
859						DEBUG1("Do_queue_jobs: cannot use [%d] '%s'",
860							j, s );
861						continue;
862					}
863					if( Get_hold_class(&job.info,sp) ){
864						DEBUG1("Do_queue_jobs: cannot use [%d] '%s' class conflict",
865							j, s );
866						/* we found a server that was not available */
867						continue;
868					}
869					if( Chooser_DYN == 0 && Chooser_routine_DYN == 0 ){
870						use_subserver = j;
871						job_to_do = job_index;
872					} else {
873						char *t;
874						/* add to the list of possible servers */
875						Set_flag_value(&chooser_list,s,j);
876						if( !Chooser_routine_DYN ){
877							/* get the environment values for the possible server */
878							t = Join_line_list_with_sep(sp,"\n");
879							Set_str_value(&chooser_env,s,t);
880							if( t ) free(t); t = 0;
881							t = Find_str_value( &chooser_env,"PRINTERS",Value_sep );
882							if( t ){
883								t = safestrdup3(t,",",s,__FILE__,__LINE__);
884								Set_str_value( &chooser_env,"PRINTERS",t );
885								if( t ) free(t); t = 0;
886							} else {
887								Set_str_value( &chooser_env,"PRINTERS",s );
888							}
889						}
890					}
891				}
892				/* we now have to find out if we really need to call the chooser
893				 * if we are working and a single queue, then we do not
894				 *   :sv == ""   - then we do
895				 */
896				DEBUG1("Do_queue_jobs: Chooser %s, working %d, master %d", Chooser_DYN, working, master);
897				if( (Chooser_routine_DYN || Chooser_DYN) && working && master == 0 ){
898					chooser_did_not_find_server = 1;
899				} else if( Chooser_routine_DYN ){
900#if defined(CHOOSER_ROUTINE)
901					extern int CHOOSER_ROUTINE( struct line_list *servers,
902						struct line_list *available, int *use_subserver );
903					/* return status for job */
904					DEBUG1("Do_queue_jobs: using CHOOSER_ROUTINE %s", STR(CHOOSER_ROUTINE) );
905					j =  CHOOSER_ROUTINE( &servers, &chooser_list, &use_subserver );
906					if( j ){
907						SETSTATUS(&job)"CHOOSER_ROUTINE exit status %s", Server_status(j));
908						chooser_did_not_find_server = 1;
909						if( j != JFAIL && j != JABORT ){
910							Update_status( &job, j );
911						}
912						if( j == JABORT ){
913							Errorcode = JABORT;
914							FATAL(LOG_ERR) "Do_queue_jobs: Chooser_routine aborted" );
915						}
916					} else if( use_subserver >= 0 ){
917						/* we use this subserver */
918						job_to_do = job_index;
919					} else {
920						/* we did not find a server queue */
921						chooser_did_not_find_server = 1;
922					}
923#else
924					Errorcode = JABORT;
925					fatal(LOG_ERR,"Do_queue_jobs: 'chooser_routine' select and no routine defined");
926#endif
927				} else if( Chooser_DYN ){
928					if( in_tempfd > 0 ) close( in_tempfd ); in_tempfd = -1;
929					if( out_tempfd > 0 ) close( out_tempfd ); out_tempfd = -1;
930					in_tempfd = Make_temp_fd(0);
931					out_tempfd = Make_temp_fd(0);
932					s = Find_str_value( &chooser_env,"PRINTERS",Value_sep );
933					if( s && (Write_fd_str( in_tempfd, s ) < 0
934						|| Write_fd_str( in_tempfd, "\n" ) < 0) ){
935						Errorcode = JABORT;
936						LOGERR_DIE(LOG_ERR) "Do_queue_jobs: write(%d) failed", in_tempfd);
937					}
938					/* we invoke the chooser with the list
939					 * of printers we have found in the order
940					 */
941					if( lseek(in_tempfd,0,SEEK_SET) == -1 ){
942						Errorcode = JFAIL;
943						LOGERR_DIE(LOG_INFO)"Do_queue_jobs: fseek(%d) failed",
944							out_tempfd);
945					}
946					j = Filter_file( in_tempfd, out_tempfd, "CHOOSER",
947						Chooser_DYN, Filter_options_DYN, &job, &chooser_env, 1 );
948					if( j ){
949						SETSTATUS(&job)"CHOOSER exit status %s", Server_status(j));
950						chooser_did_not_find_server = 1;
951						if( j != JFAIL && j != JABORT ){
952							Update_status( &job, j );
953						}
954						if( j == JABORT ){
955							Errorcode = JABORT;
956							FATAL(LOG_ERR) "Do_queue_jobs: Chooser aborted" );
957						}
958					} else {
959						if( lseek(out_tempfd,0,SEEK_SET) == -1 ){
960							Errorcode = JFAIL;
961							LOGERR_DIE(LOG_INFO)"Do_queue_jobs: fseek(%d) failed",
962								out_tempfd);
963						}
964						len = read( out_tempfd, buffer,sizeof(buffer)-1 );
965						if( len >= 0 ){
966							buffer[len] = 0;
967						} else {
968							Errorcode = JFAIL;
969							LOGERR_DIE(LOG_INFO)"Do_queue_jobs: read(%d) failed",
970								out_tempfd);
971						}
972						while( isspace(cval(buffer)) ){
973							memmove(buffer,buffer+1,safestrlen(buffer+1)+1);
974						}
975						if( (s = strpbrk(buffer,Whitespace)) ){
976							*s = 0;
977						}
978						if( buffer[0] ){
979							/* we found a server queue */
980							SETSTATUS(&job)"CHOOSER selected '%s'", buffer);
981							if( Find_str_value( &chooser_list,buffer,Value_sep ) ){
982								use_subserver = Find_flag_value(&chooser_list,buffer,Value_sep);
983								job_to_do = job_index;
984							} else if( strchr( buffer,'@') ){
985								/* we are routing to a remote queue not in list */
986								use_subserver = 0;
987								job_to_do = job_index;
988								Set_str_value( &job.info,NEW_DEST,buffer);
989								new_dest = Find_str_value( &job.info,NEW_DEST,Value_sep);
990							} else {
991								LOGMSG(LOG_ERR)"Do_queue_jobs: CHOOSER selection '%s' not a subserver",
992									buffer );
993							}
994						} else {
995							/* we did not find a server queue */
996							chooser_did_not_find_server = 1;
997						}
998					}
999				}
1000				if( in_tempfd >= 0 ) close( in_tempfd ); in_tempfd = -1;
1001				if( out_tempfd >= 0 ) close( out_tempfd ); out_tempfd = -1;
1002				Free_line_list( &chooser_env );
1003				Free_line_list( &chooser_list );
1004			}
1005		}
1006
1007		/* first, we see if there is no work and no server active */
1008		DEBUG1("Do_queue_jobs: job_to_do %d, use_subserver %d, working %d",
1009			job_to_do, use_subserver, working );
1010
1011		if( job_to_do < 0 && !working && chooser_did_not_find_server == 0 ){
1012			DEBUG1("Do_queue_jobs: nothing to do");
1013			break;
1014		}
1015
1016		/* now we see if we have to wait */
1017		if( use_subserver < 0 ){
1018			if( chooser_did_not_find_server ){
1019				setstatus(0, "chooser did not find subserver, waiting %d sec",
1020					Chooser_interval_DYN );
1021#ifdef ORIGINAL_DEBUG//JY@1020
1022				Wait_for_subserver( Chooser_interval_DYN, &servers );
1023#endif
1024			} else if( working ){
1025				if( servers.count > 1 ){
1026					setstatus(0, "waiting for server queue process to exit" );
1027				} else {
1028					setstatus(0, "waiting for subserver to exit" );
1029				}
1030#ifdef ORIGINAL_DEBUG//JY@1020
1031				Wait_for_subserver( 0, &servers );
1032#endif
1033			}
1034			continue;
1035		}
1036
1037		/*
1038		 * get the job information
1039		 */
1040		hf_name = Find_str_value(&job.info,HF_NAME,Value_sep);
1041		id = Find_str_value(&job.info,IDENTIFIER,Value_sep);
1042		if( !id ){
1043			Errorcode = JABORT;
1044			FATAL(LOG_ERR)
1045				_("Do_queue_jobs: LOGIC ERROR - no identifer '%s'"), hf_name);
1046		}
1047
1048		/*
1049		 * set the hold file information
1050		 */
1051
1052		if( destination >= 0 ){
1053			SNPRINTF(buffer,sizeof(buffer))"DEST%d",destination );
1054			Set_str_value(&job.info,DESTINATION,buffer);
1055		}
1056
1057		Set_decimal_value(&job.info,SERVER,getpid());
1058		Set_flag_value(&job.info,START_TIME,time((void *)0));
1059
1060		sp = (void *)servers.list[use_subserver];
1061		Set_str_value(sp,HF_NAME,0);
1062		Set_str_value(sp,IDENTIFIER,0);
1063
1064		pr = Find_str_value(sp,PRINTER,Value_sep);
1065		DEBUG1("Do_queue_jobs: starting job '%s' on '%s'", id, pr );
1066
1067		if( Set_hold_file( &job, 0, 0 ) ){
1068			/* you cannot update hold file!! */
1069			setstatus( &job, _("cannot update hold file '%s'"), hf_name);
1070			FATAL(LOG_ERR)
1071				_("Do_queue_jobs: cannot update hold file '%s'"), hf_name);
1072		}
1073
1074		if( Status_file_DYN ){
1075			DEBUG1("Do_queue_jobs: trimming status file '%s'/'%s'", Spool_dir_DYN, Status_file_DYN );
1076			fd = Trim_status_file( -1, Status_file_DYN, Max_status_size_DYN,
1077				Min_status_size_DYN );
1078			if( fd > 0 ) close(fd);
1079			fd = -1;
1080		}
1081
1082		if( use_subserver > 0 ){
1083			/* we set up a copy of the job descriptor to use to make
1084				the job in the new directory */
1085			int holdfile_fd = -1;
1086			struct job jcopy;
1087			Init_job(&jcopy);
1088			Copy_job(&jcopy,&job);
1089
1090			sd = Find_str_value(sp,SPOOLDIR,Value_sep);
1091			pr = Find_str_value(sp,PRINTER,Value_sep);
1092
1093			DEBUG1("Do_queue_jobs: subserver '%s', spool dir '%s' for job '%s'",
1094			   pr, sd, id );
1095			setstatus(&job, "transferring '%s' to subserver '%s'", id, pr );
1096
1097			for( i = 0; i < jcopy.datafiles.count; ++i ){
1098				datafile = (void *)jcopy.datafiles.list[i];
1099				Set_str_value( datafile, OPENNAME, 0 );
1100			}
1101			Set_str_value(&jcopy.info,OPENNAME,0);
1102			Set_str_value(&jcopy.info,HF_NAME,0);
1103#ifdef ORIGINAL_DEBUG//JY@1020
1104			if(DEBUGL2)Dump_job("Do_queue_jobs: subserver jcopy", &jcopy );
1105#endif
1106
1107			fail = 0;
1108			for( i = 0; i < jcopy.datafiles.count; ++i ){
1109				datafile = (void *)jcopy.datafiles.list[i];
1110#ifdef ORIGINAL_DEBUG//JY@1020
1111				if(DEBUGL3)Dump_line_list("Do_queue_jobs - copying datafiles",
1112					datafile);
1113#endif
1114				from = Find_str_value(datafile,TRANSFERNAME,Value_sep);
1115				path = Make_temp_copy( from, sd );
1116				DEBUG3("Do_queue_jobs: sd '%s', from '%s', path '%s'",
1117					sd, from, path );
1118				if( path ){
1119					Set_str_value( datafile, OPENNAME, path );
1120				} else {
1121					fail = 1;
1122					break;
1123				}
1124			}
1125			if( !fail ){
1126				from = Find_str_value(&jcopy.info,TRANSFERNAME,Value_sep);
1127				path = Make_temp_copy(from,sd);
1128				DEBUG3("Do_queue_jobs: sd '%s', from '%s', path '%s'",
1129					sd, from, path );
1130				if( path ){
1131					Set_str_value( &jcopy.info,OPENNAME,path );
1132				} else {
1133					fail = 1;
1134				}
1135			}
1136			/* if we fail to copy, make a note of it */
1137			if( fail ){
1138				SNPRINTF(buffer,sizeof(buffer))"cannot copy '%s' to subserver '%s' queue directory '%s'",
1139					id, pr, sd );
1140				Set_str_value(&job.info,ERROR,buffer);
1141				Set_nz_flag_value(&job.info,ERROR_TIME,time(0));
1142				setstatus(&job, "%s", buffer);
1143				path = Find_str_value(&jcopy.info,OPENNAME,Value_sep);
1144				if( path ) unlink(path);
1145				for( i = 0; i < jcopy.datafiles.count; ++i ){
1146					datafile = (void *)jcopy.datafiles.list[i];
1147					path = Find_str_value(datafile,OPENNAME,Value_sep);
1148					if( path ) unlink(path);
1149				}
1150				Update_status(&job, JFAIL);
1151				Free_job(&jcopy);
1152				continue;
1153			}
1154#ifdef ORIGINAL_DEBUG//JY@1020
1155			if(DEBUGL2)Dump_job("Do_queue_jobs: subserver jcopy new", &jcopy );
1156#endif
1157
1158			/* set up the new context */
1159			safestrncpy(savename,Printer_DYN);
1160			buffer[0] = 0;
1161
1162			/* we have to chdir to the destination directory */
1163			if( Setup_printer( pr, buffer, sizeof(buffer), 1 ) ){
1164				Errorcode = JABORT;
1165				FATAL(LOG_ERR) "Do_queue_jobs: subserver '%s' setup failed - %s'",
1166						pr, buffer );
1167			}
1168
1169			/* get the job set up */
1170			i = Check_for_missing_files( &jcopy, 0, errormsg, sizeof(errormsg), 0, &holdfile_fd );
1171			if( holdfile_fd >= 0 ) close( holdfile_fd ); holdfile_fd = -1;
1172			Free_job(&jcopy);
1173
1174			/* now we switch back to the old context */
1175			if( Setup_printer( savename, buffer, sizeof(buffer), 1 ) ){
1176				Errorcode = JABORT;
1177				FATAL(LOG_ERR) "Do_queue_jobs: subserver '%s' setup failed '%s'",
1178					savename, buffer );
1179			}
1180			/* now handle error conditions */
1181			if( i ){
1182				SNPRINTF(buffer,sizeof(buffer))"transfer '%s' to subserver '%s' failed - %s",
1183					id, pr, errormsg );
1184				Set_str_value(&job.info,ERROR,buffer);
1185				Set_nz_flag_value(&job.info,ERROR_TIME,time(0));
1186				setstatus(&job, "%s", buffer);
1187				Update_status(&job, JFAIL);
1188				continue;
1189			}
1190
1191			/* now we deal with the job in the original queue */
1192			Update_status(&job, JSUCC);
1193
1194			Set_str_value(sp,IDENTIFIER,id);
1195#ifdef ORIGINAL_DEBUG//JY@1020
1196			setstatus(&job, "transfer '%s' to subserver '%s' finished", id, pr );
1197			setmessage(&job,STATE,"COPYTO %s",pr);
1198			setstatus(&job, "starting subserver '%s'", pr );
1199#endif
1200			pid = Fork_subserver( &servers, use_subserver, 0 );
1201			jobs_printed = 1;
1202		} else {
1203			Free_line_list(&tinfo);
1204			Set_str_value(&tinfo,HF_NAME,hf_name);
1205			Set_str_value(&tinfo,NEW_DEST,new_dest);
1206			Set_str_value(&tinfo,MOVE_DEST,move_dest);
1207			if( (pid = Fork_subserver( &servers, 0, &tinfo )) < 0 ){
1208				setstatus( &job, _("sleeping, waiting for processes to exit"));
1209				plp_sleep(1);
1210			} else {
1211				Set_str_value(sp,HF_NAME,hf_name);
1212				Set_str_value(sp,IDENTIFIER,id);
1213			}
1214			jobs_printed = 1;
1215		}
1216	}
1217
1218	/* now we reset the server order */
1219
1220	Errorcode = JSUCC;
1221	Free_job(&job);
1222	Free_line_list(&tinfo);
1223	if( Server_names_DYN ){
1224		if( jobs_printed ) setstatus( 0, "no more jobs to process in load balance queue" );
1225		jobs_printed = 0;
1226		for( i = 1; i < servers.count; ++i ){
1227			sp = (void *)servers.list[i];
1228			s = Find_str_value(sp,PRINTER,Value_sep);
1229			Add_line_list(&tinfo,s,0,0,0);
1230		}
1231		s = Join_line_list_with_sep(&tinfo,",");
1232		Set_str_value(&Spool_control,SERVER_ORDER,s);
1233		Set_spool_control(0, Queue_control_file_DYN, &Spool_control);
1234		if(s) free(s); s = 0;
1235		Free_line_list(&tinfo);
1236	}
1237	Free_listof_line_list(&servers);
1238
1239	/* truncate and close the lock file then wait a short time for signal */
1240	ftruncate( lock_fd, 0 );
1241	close( lock_fd );
1242	lock_fd = -1;
1243	/* force status update */
1244	if( Lpq_status_file_DYN ){
1245		unlink(Lpq_status_file_DYN);
1246	}
1247	plp_unblock_all_signals( &oblock);
1248	plp_usleep(500);
1249	DEBUG1( "Do_queue_jobs: Susr1 at end %d", Susr1 );
1250	if( Susr1 ){
1251		DEBUG1("Do_queue_jobs: SIGUSR1 just before exit" );
1252		Susr1 = 0;
1253		goto again;
1254	}
1255	cleanup(0);
1256	return(0);
1257}
1258
1259#ifdef ORIGINAL_DEBUG//JY@1020
1260/***************************************************************************
1261 * Remote_job()
1262 * Send a job to a remote server.  This code is actually trickier
1263 *  than it looks, as the Send_job code takes most of the heat.
1264 *
1265 ***************************************************************************/
1266
1267int Remote_job( struct job *job, int lpd_bounce, char *move_dest, char *id )
1268{
1269	int status, tempfd, n;
1270	double job_size;
1271	char buffer[SMALLBUFFER], *s, *tempfile, *oldid, *newid, *old_lp_value;
1272	struct line_list *lp, *firstfile;
1273	struct job jcopy;
1274	struct stat statb;
1275
1276	DEBUG1("Remote_job: %s", id );
1277	/* setmessage(job,STATE,"SENDING"); */
1278	status = 0;
1279	Init_job(&jcopy);
1280
1281	Set_str_value(&job->info,PRSTATUS,0);
1282	Set_str_value(&job->info,ERROR,0);
1283	Set_flag_value(&job->info,ERROR_TIME,0);
1284
1285	Setup_user_reporting(job);
1286
1287	if( Accounting_remote_DYN && Accounting_file_DYN ){
1288		if( Accounting_start_DYN ){
1289			status = Do_accounting( 0,
1290				Accounting_start_DYN, job, Connect_interval_DYN );
1291		}
1292		DEBUG1("Remote_job: accounting status %s", Server_status(status) );
1293		if( status ){
1294			SNPRINTF(buffer,sizeof(buffer))
1295				"accounting check failed '%s'", Server_status(status));
1296				SETSTATUS(job)"%s", buffer );
1297			switch(status){
1298			case JFAIL: break;
1299			case JHOLD: /* Set_flag_value(&job->info,HOLD_TIME,time((void *)0)); */ break;
1300			case JREMOVE: /* Set_flag_value(&job->info,REMOVE_TIME,time((void *)0)); */ break;
1301			default:
1302					Set_str_value(&job->info,ERROR,buffer);
1303					Set_nz_flag_value(&job->info,ERROR_TIME,time(0));
1304					Set_hold_file(job, 0, 0 );
1305					break;
1306			}
1307			goto exit;
1308		}
1309	}
1310
1311	Errorcode = status = 0;
1312
1313	Copy_job(&jcopy,job);
1314#ifdef ORIGINAL_DEBUG//JY@1020
1315	if(DEBUGL2)Dump_job("Remote_job - jcopy", &jcopy );
1316#endif
1317	if( lpd_bounce ){
1318#ifdef ORIGINAL_DEBUG//JY@1020
1319		if(DEBUGL2) Dump_job( "Remote_job - before filtering", &jcopy );
1320#endif
1321		tempfd = Make_temp_fd(&tempfile);
1322
1323		old_lp_value = safestrdup(Find_str_value( &PC_entry_line_list, "lp", Value_sep),
1324			__FILE__,__LINE__ );
1325		Set_str_value( &PC_entry_line_list, LP, tempfile );
1326		Print_job( tempfd, -1, &jcopy, 0, 0, 0 );
1327		Set_str_value( &PC_entry_line_list, LP, old_lp_value );
1328		if( old_lp_value ) free(old_lp_value); old_lp_value = 0;
1329
1330		if( fstat( tempfd, &statb ) ){
1331			LOGERR(LOG_INFO)"Remote_job: fstatb failed" );
1332			status = JFAIL;
1333		}
1334		if( (close(tempfd) == -1 ) ){
1335			status = JFAIL;
1336			LOGERR(LOG_INFO)"Remote_job: close(%d) failed",
1337				tempfd);
1338		}
1339		if( statb.st_size == 0 ){
1340			LOGMSG( LOG_ERR) "Remote_job: zero length job after filtering");
1341			status = JABORT;
1342		}
1343		if( status ) goto exit;
1344		job_size = statb.st_size;
1345		Free_listof_line_list(&jcopy.datafiles);
1346		lp = malloc_or_die(sizeof(lp[0]),__FILE__,__LINE__);
1347		memset(lp,0,sizeof(lp[0]));
1348		Check_max(&jcopy.datafiles,1);
1349		jcopy.datafiles.list[jcopy.datafiles.count++] = (void *)lp;
1350		Set_str_value(lp,OPENNAME,tempfile);
1351		Set_str_value(lp,TRANSFERNAME,tempfile);
1352		s = 0;
1353		if( (firstfile = (void *)(job->datafiles.list[0])) ){
1354			s = Find_str_value( firstfile, "N", Value_sep );
1355		}
1356		Set_str_value(lp,"N",s?s:"(lpd_filter)");
1357		Set_flag_value(lp,COPIES,1);
1358		Set_double_value(lp,SIZE,job_size);
1359		Fix_bq_format( 'f', lp );
1360	} else if( !move_dest ) {
1361		int err = Errorcode;
1362		Errorcode = 0;
1363		if( Generate_banner_DYN ){
1364			Add_banner_to_job( &jcopy );
1365		}
1366		Filter_files_in_job( &jcopy, -1, 0 );
1367		status = Errorcode;
1368		Errorcode = err;
1369		if( status ){
1370			goto done;
1371		}
1372	}
1373
1374	/* fix up the control file */
1375	Fix_control( &jcopy, Control_filter_DYN, Xlate_format_DYN );
1376	oldid = Find_str_value(&job->info,IDENTIFIER,Value_sep );
1377	newid = Find_str_value(&jcopy.destination,IDENTIFIER,Value_sep );
1378	if( newid == 0 ){
1379		newid = Find_str_value(&jcopy.info,IDENTIFIER,Value_sep );
1380	}
1381	n = Find_flag_value( &jcopy.info, DATAFILE_COUNT, Value_sep );
1382	if( Max_datafiles_DYN > 0 && n > Max_datafiles_DYN ){
1383		Errorcode = JABORT;
1384		FATAL(LOG_ERR)
1385				_("Remote_job: %d datafiles and only allowed %d"),
1386					n, Max_datafiles_DYN );
1387	}
1388#ifdef ORIGINAL_DEBUG//JY@1020
1389	setmessage(job,STATE,"SENDING OLDID=%s NEWID=%s DEST=%s@%s",
1390		oldid, newid, RemotePrinter_DYN, RemoteHost_DYN );
1391	if(DEBUGL3)Dump_job("Send_job - after Fix_control", &jcopy );
1392#endif
1393	status = Send_job( &jcopy, job, Connect_timeout_DYN, Connect_interval_DYN,
1394		Max_connect_interval_DYN, Send_job_rw_timeout_DYN, 0 );
1395	DEBUG1("Remote_job: %s, status '%s'", id, Link_err_str(status) );
1396	buffer[0] = 0;
1397
1398#ifdef ORIGINAL_DEBUG//JY@1020
1399	if(DEBUGL2)Dump_job("Remote_job - final jcopy value", &jcopy );
1400#endif
1401 done:
1402	s = 0;
1403	if( status ){
1404		s = Find_str_value(&jcopy.info,ERROR,Value_sep);
1405		if( !s ){
1406			s = "Mystery error from Send_job";
1407		}
1408		Set_str_value(&job->info,ERROR,s);
1409		Set_nz_flag_value(&job->info,ERROR_TIME,time(0));
1410	}
1411	s = 0;
1412
1413	Free_job(&jcopy);
1414
1415	switch( status ){
1416	case JSUCC:
1417	case JABORT:
1418	case JFAIL:
1419	case JREMOVE:
1420		break;
1421	case LINK_ACK_FAIL:
1422		SNPRINTF(buffer,sizeof(buffer))
1423			_("link failure while sending job '%s'"), id );
1424		s = buffer;
1425		status = JFAIL;
1426		break;
1427	case LINK_PERM_FAIL:
1428		SNPRINTF(buffer,sizeof(buffer))
1429			 _("no permission to spool job '%s'"), id );
1430		s = buffer;
1431		status = JREMOVE;
1432		break;
1433	default:
1434		SNPRINTF(buffer,sizeof(buffer))
1435			_("failed to send job '%s'"), id );
1436		s = buffer;
1437		status = JFAIL;
1438		break;
1439	}
1440	if( s ){
1441		if( !Find_str_value(&job->info,ERROR,Value_sep) ){
1442			Set_str_value(&job->info,ERROR,s);
1443		}
1444		if( !Find_flag_value(&job->info,ERROR_TIME,Value_sep) ){
1445			Set_nz_flag_value(&job->info,ERROR_TIME,time(0));
1446		}
1447	}
1448
1449	Set_str_value(&job->info,PRSTATUS,Server_status(status));
1450
1451	Set_hold_file(job, 0, 0 );
1452
1453	if( Accounting_remote_DYN && Accounting_file_DYN  ){
1454		if( Accounting_end_DYN ){
1455			Do_accounting( 1, Accounting_end_DYN, job,
1456				Connect_interval_DYN );
1457		}
1458	}
1459 exit:
1460	return( status );
1461}
1462#endif
1463
1464/***************************************************************************
1465 * Local_job()
1466 * Send a job to a local printer.
1467 ***************************************************************************/
1468
1469int Local_job( struct job *job, char *id )
1470{
1471	int status, fd, status_fd, pid, poll_for_status;
1472	char *old_lp_value;
1473	char buffer[SMALLBUFFER];
1474
1475	status_fd = fd = -1;
1476
1477	DEBUG1("Local_job: starting %s", id );
1478#ifdef ORIGINAL_DEBUG//JY@1020
1479	setmessage(job,STATE,"PRINTING");
1480#endif
1481	Errorcode = status = 0;
1482	Set_str_value(&job->info,PRSTATUS,0);
1483	Set_str_value(&job->info,ERROR,0);
1484	Set_flag_value(&job->info,ERROR_TIME,0);
1485
1486	Setup_user_reporting(job);
1487
1488	SETSTATUS(job)"subserver pid %d starting", getpid());
1489
1490	if( Accounting_file_DYN && Local_accounting_DYN ){
1491		SETSTATUS(job)"accounting at start");
1492#ifdef ORIGINAL_DEBUG//JY@1020
1493		if( Accounting_start_DYN ){
1494			status = Do_accounting( 0,
1495				Accounting_start_DYN, job, Connect_interval_DYN );
1496		}
1497#endif
1498
1499		DEBUG1("Local_job: accounting status %s", Server_status(status) );
1500		if( status ){
1501			SNPRINTF(buffer,sizeof(buffer))
1502				"accounting check failed '%s'", Server_status(status));
1503			SETSTATUS(job)"%s", buffer );
1504			switch(status){
1505			case JFAIL: break;
1506			case JHOLD: /* Set_flag_value(&job->info,HOLD_TIME,time((void *)0)); */ break;
1507			case JREMOVE: /* Set_flag_value(&job->info,REMOVE_TIME,time((void *)0)); */ break;
1508			default:
1509				Set_str_value(&job->info,ERROR,buffer);
1510				Set_nz_flag_value(&job->info,ERROR_TIME,time(0));
1511				Set_hold_file(job, 0, 0 );
1512				break;
1513			}
1514			goto exit;
1515		}
1516	}
1517 	Errorcode = status = 0;
1518
1519	SETSTATUS(job)"opening device '%s'", Lp_device_DYN);
1520	pid = 0;
1521	fd = Printer_open(Lp_device_DYN, &status_fd, job,
1522		Send_try_DYN, Connect_interval_DYN, Max_connect_interval_DYN,
1523		Connect_grace_DYN, Connect_timeout_DYN, &pid, &poll_for_status );
1524
1525	/* note: we NEVER return fd == 0 or horrible things have happened */
1526	DEBUG1("Local_job: fd %d", fd );
1527	if( fd <= 0 ){
1528		status = JFAIL;
1529		goto exit;
1530	}
1531	SETSTATUS(job)"printing job '%s'", id );
1532	/* Print_job( output_device, status_device, job, timeout, poll_for_status, filter ) */
1533	old_lp_value = safestrdup(Find_str_value( &PC_entry_line_list, LP, Value_sep),
1534		__FILE__,__LINE__ );
1535	Set_str_value( &PC_entry_line_list, LP, Lp_device_DYN );
1536	status = Print_job( fd, status_fd, job, Send_job_rw_timeout_DYN, poll_for_status, 0 );
1537	Set_str_value( &PC_entry_line_list, LP, old_lp_value );
1538	if( old_lp_value ) free(old_lp_value); old_lp_value = 0;
1539	/* we close close device */
1540	DEBUG1("Local_job: shutting down fd %d", fd );
1541
1542	fd = Shutdown_or_close( fd );
1543	DEBUG1("Local_job: after shutdown fd %d, status_fd %d", fd, status_fd );
1544	if( status_fd > 0 ){
1545		/* we shut down this connection as well */
1546		status_fd = Shutdown_or_close( status_fd );
1547		/* we wait for eof on status_fd */
1548		buffer[0] = 0;
1549		if( status_fd > 0 ){
1550			Get_status_from_OF(job,"LP",pid,
1551				status_fd, buffer, sizeof(buffer)-1, Send_job_rw_timeout_DYN,
1552				0, 0, Status_file_DYN );
1553		}
1554	}
1555	if( fd > 0 ) close( fd ); fd = -1;
1556	if( status_fd > 0 ) close( status_fd ); status_fd = -1;
1557	if( pid > 0 ){
1558		SETSTATUS(job)"waiting for printer filter to exit");
1559		status = Wait_for_pid( pid, "LP", 0, Send_job_rw_timeout_DYN );
1560	}
1561	DEBUG1("Local_job: status %s", Server_status(status) );
1562
1563	Set_str_value(&job->info,PRSTATUS,Server_status(status));
1564	if( Accounting_file_DYN && Local_accounting_DYN ){
1565		SETSTATUS(job)"accounting at end");
1566#ifdef ORIGINAL_DEBUG//JY@1020
1567		if( Accounting_end_DYN ){
1568			Do_accounting( 1, Accounting_end_DYN, job,
1569				Connect_interval_DYN );
1570		}
1571#endif
1572	}
1573	SETSTATUS(job)"finished '%s', status '%s'", id, Server_status(status));
1574
1575 exit:
1576	if( fd != -1 ) close(fd); fd = -1;
1577	if( status_fd != -1 ) close(status_fd); status_fd = -1;
1578	return( status );
1579}
1580
1581int Fork_subserver( struct line_list *server_info, int use_subserver,
1582	struct line_list *parms )
1583{
1584	char *pr;
1585	struct line_list *sp;
1586	int pid;
1587	struct line_list pl;
1588
1589	Init_line_list(&pl);
1590	if( parms == 0 ) parms = &pl;
1591	sp = (void *)server_info->list[use_subserver];
1592	Set_str_value(sp,PRSTATUS,0);
1593	Set_decimal_value(sp,SERVER,0);
1594
1595	pr = Find_str_value(sp,PRINTER,Value_sep);
1596	Set_str_value(parms,PRINTER,pr);
1597	Set_flag_value(parms,SUBSERVER,use_subserver);
1598	DEBUG1( "Fork_subserver: starting '%s'", pr );
1599	if( use_subserver > 0 ){
1600#ifdef JYDEBUG//JYWeng
1601aaaaaa=fopen("/tmp/qqqqq", "a");
1602fprintf(aaaaaa, "+++++++++++++++++++++++++++++++++++++++++++++\n");
1603fprintf(aaaaaa, "Fork_subserver: Start_worker(queue)\n");
1604fprintf(aaaaaa, "+++++++++++++++++++++++++++++++++++++++++++++\n");
1605fclose(aaaaaa);
1606#endif
1607		pid = Start_worker( "queue",parms, 0 );
1608	} else {
1609#ifdef JYDEBUG//JYWeng
1610aaaaaa=fopen("/tmp/qqqqq", "a");
1611fprintf(aaaaaa, "+++++++++++++++++++++++++++++++++++++++++++++\n");
1612fprintf(aaaaaa, "Fork_subserver: Start_worker(printer)\n");
1613fprintf(aaaaaa, "+++++++++++++++++++++++++++++++++++++++++++++\n");
1614fclose(aaaaaa);
1615#endif
1616		pid = Start_worker( "printer",parms, 0 );
1617	}
1618
1619	if( pid > 0 ){
1620		Set_decimal_value(sp,SERVER,pid);
1621	} else {
1622		LOGERR(LOG_ERR) _("Fork_subserver: fork failed") );
1623	}
1624	Free_line_list(parms);
1625	return( pid );
1626}
1627
1628#ifdef ORIGINAL_DEBUG//JY@1020
1629/***************************************************************************
1630 * struct server_info *Wait_for_subserver( struct line_list *servers,
1631 *     int block )
1632 *  wait for a server process to exit
1633 *  if none present return 0
1634 *  look up the process in the process table
1635 *  update the process table status
1636 *  return the process table entry
1637 ***************************************************************************/
1638
1639void Wait_for_subserver( int timeout, struct line_list *servers
1640	/*, struct line_list *order */ )
1641{
1642	pid_t pid;
1643	plp_status_t procstatus;
1644	int found, sigval, status, i, done;
1645	struct line_list *sp = 0;
1646	struct job job;
1647	char buffer[SMALLBUFFER], *pr, *hf_name, *id;
1648
1649	/*
1650	 * wait for the process to finish or a signal to be delivered
1651	 */
1652
1653	Init_job(&job);
1654	sigval = errno = 0;
1655
1656	done = 0;
1657 again:
1658	while( (pid = plp_waitpid( -1, &procstatus, WNOHANG )) > 0 ){
1659		++done;
1660		DEBUG1("Wait_for_subserver: pid %d, status '%s'", pid,
1661			Decode_status(&procstatus));
1662		if( WIFSIGNALED( procstatus ) ){
1663			sigval = WTERMSIG( procstatus );
1664			DEBUG1("Wait_for_subserver: pid %d terminated by signal '%s'",
1665				pid, Sigstr( sigval ) );
1666			switch( sigval ){
1667			/* generated by the program */
1668			case 0:
1669			case SIGINT:
1670			case SIGKILL:
1671			case SIGQUIT:
1672			case SIGTERM:
1673			case SIGUSR1:
1674				status = JFAIL;
1675				break;
1676			default:
1677				status = JSIGNAL;
1678				break;
1679			}
1680		} else {
1681			status = WEXITSTATUS( procstatus );
1682			if( status > 0 && status < 32 ) status += JFAIL-1;
1683		}
1684		DEBUG1( "Wait_for_subserver: pid %d final status %s",
1685			pid, Server_status(status) );
1686
1687		if( status != JSIGNAL ){
1688			SNPRINTF(buffer,sizeof(buffer))
1689				 _("subserver pid %d exit status '%s'"),
1690				pid, Server_status(status));
1691		} else {
1692			SNPRINTF(buffer,sizeof(buffer))
1693				_("subserver pid %d died with signal '%s'"),
1694				pid, Sigstr(sigval));
1695			status = JABORT;
1696		}
1697#ifdef ORIGINAL_DEBUG//JY@1020
1698		if(DEBUGL4) Dump_subserver_info("Wait_for_subserver", servers );
1699#endif
1700
1701		for( found = i = 0; !found && i < servers->count; ++i ){
1702			sp = (void *)servers->list[i];
1703			if( pid == Find_flag_value(sp,SERVER,Value_sep) ){
1704				DEBUG3("Wait_for_subserver: found %d", pid );
1705				found = 1;
1706				++done;
1707
1708				Free_job(&job);
1709				Set_decimal_value(sp,SERVER,0);
1710				Set_flag_value(sp,DONE_TIME,time((void *)0));
1711
1712				/* we get the hold file information */
1713				hf_name = Find_str_value(sp,HF_NAME,Value_sep);
1714				Get_hold_file( &job, hf_name );
1715				Setup_cf_info( &job, 0 );
1716
1717				pr = Find_str_value(sp,PRINTER,Value_sep);
1718				id = Find_str_value(sp,IDENTIFIER,Value_sep);
1719				DEBUG1( "Wait_for_subserver: server pid %d for '%s' for '%s' '%s' finished",
1720					pid, pr, hf_name, id );
1721
1722				/* see if you can get the hold file and update the status */
1723				Update_status( &job, status );
1724				Set_str_value(sp,HF_NAME,0);
1725				Set_str_value(sp,IDENTIFIER,0);
1726				Update_spool_info(sp);
1727				if( i == 0 ){
1728					/* this is the information for the master spool queue */
1729					Get_spool_control(Queue_control_file_DYN, &Spool_control );
1730				}
1731			}
1732		}
1733		Free_job(&job);
1734		/* sort server order */
1735		if( Mergesort( servers->list+1, servers->count-1,
1736			sizeof( servers->list[0] ), cmp_server, 0 ) ){
1737			FATAL(LOG_ERR)
1738				_("Wait_for_subserver: Mergesort failed") );
1739		}
1740#ifdef ORIGINAL_DEBUG//JY@1020
1741		if(DEBUGL4) Dump_subserver_info(
1742			"Wait_for_subserver: after sorting", servers );
1743#endif
1744	}
1745	if( !done ){
1746		/* we need to unblock signals and wait for event */
1747		Chld = 0;
1748		Set_timeout_break( timeout );
1749		(void) plp_signal(SIGCHLD,  (plp_sigfunc_t)Sigchld);
1750		plp_sigpause();
1751		Clear_timeout();
1752		signal( SIGCHLD, SIG_DFL );
1753		if( Chld ) goto again;
1754	}
1755
1756	Free_job(&job);
1757}
1758#endif
1759
1760/***************************************************************************
1761 * int Decode_transfer_failure( int attempt, struct job *job, int status )
1762 * When you get a job failure more than a certain number of times,
1763 *  you check the 'Send_failure_action_DYN' variable
1764 *  This can be abort, retry, or remove
1765 * If retry, you keep retrying; if abort you shut the queue down;
1766 *  if remove, you remove the job and try again.
1767 ***************************************************************************/
1768
1769 static struct keywords keys[] = {
1770	{"succ", N_("succ"), INTEGER_K, (void *)0, JSUCC,0,0},
1771	{"jsucc", N_("jsucc"), INTEGER_K, (void *)0, JSUCC,0,0},
1772	{"success", N_("success"), INTEGER_K, (void *)0, JSUCC,0,0},
1773	{"jsuccess", N_("jsuccess"), INTEGER_K, (void *)0, JSUCC,0,0},
1774	{"abort", N_("abort"), INTEGER_K, (void *)0, JABORT,0,0},
1775	{"jabort", N_("jabort"), INTEGER_K, (void *)0, JABORT,0,0},
1776	{"hold", N_("hold"), INTEGER_K, (void *)0, JHOLD,0,0},
1777	{"jhold", N_("jhold"), INTEGER_K, (void *)0, JHOLD,0,0},
1778	{"remove", N_("remove"), INTEGER_K, (void *)0, JREMOVE,0,0},
1779	{"jremove", N_("jremove"), INTEGER_K, (void *)0, JREMOVE,0,0},
1780	{ 0,0,0,0,0,0,0 }
1781};
1782
1783int Decode_transfer_failure( int attempt, struct job *job )
1784{
1785	struct keywords *key;
1786	int result, n, len, c;
1787	char line[SMALLBUFFER], *outstr;
1788
1789	result = JREMOVE;
1790	outstr = Send_failure_action_DYN;
1791	if( outstr ) while( isspace(cval(outstr)) ) ++outstr;
1792	DEBUG1("Decode_transfer_failure: send_failure_action '%s'", outstr );
1793	if( outstr && cval(outstr) == '|' ){
1794		/* check to see if it is a filter */
1795		int out_tempfd, in_tempfd;
1796
1797		outstr = 0;
1798		SNPRINTF( line, sizeof(line)) "%d\n", attempt );
1799		out_tempfd = Make_temp_fd( 0);
1800		in_tempfd = Make_temp_fd( 0);
1801		if( Write_fd_str(in_tempfd,line) < 0 ){
1802			Errorcode = JFAIL;
1803			LOGERR_DIE(LOG_INFO)"Decode_transfer_failure: write(%d) failed",
1804				in_tempfd);
1805		}
1806		if( lseek(in_tempfd,0,SEEK_SET) == -1 ){
1807			Errorcode = JFAIL;
1808			LOGERR_DIE(LOG_INFO)"Decode_transfer_failure: fseek(%d) failed",
1809				in_tempfd);
1810		}
1811		n = Filter_file( in_tempfd, out_tempfd, "TRANSFER_FAILURE",
1812			Send_failure_action_DYN, Filter_options_DYN, job, 0, 1 );
1813		DEBUG1("Decode_transfer_failure: exit status %s", Server_status(n));
1814		if( n ){
1815			result = n;
1816			setstatus( job, "send_failure_action filter exit status '%s'",
1817				Server_status(result) );
1818		} else {
1819			if( lseek(out_tempfd,0,SEEK_SET) == -1 ){
1820				Errorcode = JFAIL;
1821				LOGERR_DIE(LOG_INFO)"Decode_transfer_failure: fseek(%d) failed",
1822					out_tempfd);
1823			}
1824			len = read( out_tempfd, line,sizeof(line)-1 );
1825			if( len >= 0 ){
1826				line[len] = 0;
1827			} else {
1828				Errorcode = JFAIL;
1829				LOGERR_DIE(LOG_INFO)"Decode_transfer_failure: read(%d) failed",
1830					out_tempfd);
1831			}
1832			while( (c = cval(line)) && strchr( Whitespace, c) ){
1833				memmove( line, line+1, safestrlen(line+1)+1 );
1834			}
1835			while( (len = safestrlen(line)) && (c = cval(line+len-1))
1836				&& strchr( Whitespace, c) ){
1837				line[len-1] = 0;
1838			}
1839			setstatus( job, "send_failure_action filter returned '%s'",
1840				outstr );
1841		}
1842		close( out_tempfd ); out_tempfd = -1;
1843		close( in_tempfd ); in_tempfd = -1;
1844	}
1845	if( outstr && *outstr ){
1846		DEBUG1("Decode_transfer_failure: outstr '%s'", outstr );
1847		for( key = keys; key->keyword; ++key ){
1848			DEBUG1("Decode_transfer_failure: comparing '%s' to '%s'",
1849				outstr, key->keyword );
1850			if( safestrcasecmp( key->keyword, outstr ) == 0 ){
1851				result = key->maxval;
1852				break;
1853			}
1854		}
1855	}
1856	DEBUG1("Decode_transfer_failure: result '%s'", Server_status(result) );
1857	setstatus( job, "send_failure_action '%s'", Server_status(result) );
1858	return( result );
1859}
1860
1861void Update_status( struct job *job, int status )
1862{
1863	char buffer[SMALLBUFFER];
1864	char *id, *did, *strv, *hf_name;
1865	struct line_list *destination;
1866	int copy, copies, attempt, destinations, n, done = 0;
1867
1868	did = 0;
1869	destinations = 0;
1870	destination = 0;
1871	Set_decimal_value(&job->info,SERVER,0);
1872
1873	id = Find_str_value(&job->info,IDENTIFIER,Value_sep);
1874	if( !id ){
1875#ifdef ORIGINAL_DEBUG//JY@1020
1876		if(DEBUGL1)Dump_job("Update_status - no ID", job );
1877#endif
1878		return;
1879	}
1880
1881	if( (destinations = Find_flag_value(&job->info,DESTINATIONS,Value_sep)) ){
1882		did = Find_str_value(&job->info,DESTINATION,Value_sep );
1883		if( !Get_destination_by_name( job, did ) ){
1884			destination = &job->destination;
1885			did = Find_str_value(destination,IDENTIFIER,Value_sep);
1886			if(!did) did = Find_str_value(destination,TRANSFERNAME,Value_sep);
1887			Set_decimal_value(destination,SERVER,0);
1888		}
1889	}
1890#ifdef ORIGINAL_DEBUG//JY@1020
1891	setmessage(job,STATE,"EXITSTATUS %s", Server_status(status));
1892#endif
1893
1894 again:
1895	DEBUG1("Update_status: again - status '%s', id '%s', dest id '%s'",
1896		Server_status(status), id, did );
1897
1898#ifdef ORIGINAL_DEBUG//JY@1020
1899	setmessage(job,STATE,"PROCESSSTATUS %s", Server_status(status));
1900#endif
1901	switch( status ){
1902		/* hold the destination stuff */
1903	case JHOLD:
1904		if( destination ){
1905			Set_flag_value(destination,HOLD_TIME,time((void *)0) );
1906			Update_destination(job);
1907		} else {
1908			Set_flag_value(&job->info,HOLD_TIME, time((void *)0) );
1909			Set_flag_value(&job->info,PRIORITY_TIME, 0 );
1910		}
1911		Set_hold_file( job, 0, 0 );
1912		break;
1913
1914	case JSUCC:	/* successful, remove job */
1915#ifdef ORIGINAL_DEBUG//JY@1020
1916		if(DEBUGL3)Dump_job("Update_status - JSUCC start", job );
1917#endif
1918		if( destination ){
1919			done = 0;
1920			copies = Find_flag_value(&job->info,SEQUENCE,Value_sep);
1921			Set_flag_value(&job->info,SEQUENCE,copies+1);
1922			copies = Find_flag_value(destination,COPIES,Value_sep);
1923			copy = Find_flag_value(destination,COPY_DONE,Value_sep);
1924			n = Find_flag_value(destination,DESTINATION,Value_sep);
1925			if( Find_str_value(destination,MOVE,Value_sep) ){
1926				Set_flag_value(destination,DONE_TIME,time((void *)0));
1927				setstatus( job, "%s@%s: route job '%s' moved",
1928					Printer_DYN, FQDNHost_FQDN, did );
1929				done = 1;
1930			} else {
1931				++copy;
1932				Set_flag_value(destination,COPY_DONE,copy);
1933				if( copies ){
1934					setstatus( job,
1935					"%s@%s: route job '%s' printed copy %d of %d",
1936					Printer_DYN, FQDNHost_FQDN, id, copy, copies );
1937				}
1938				if( copy >= copies ){
1939					Set_flag_value(destination,DONE_TIME,time((void *)0));
1940					done = 1;
1941					++n;
1942				}
1943			}
1944			Update_destination(job);
1945			id = Find_str_value(&job->info,IDENTIFIER,Value_sep);
1946			if( done && n >= destinations ){
1947				Set_flag_value(&job->info,DONE_TIME,time((void *)0));
1948				setstatus( job, "%s@%s: job '%s' printed",
1949					Printer_DYN, FQDNHost_FQDN, id );
1950				goto done_job;
1951			}
1952			Set_hold_file( job, 0, 0 );
1953			break;
1954		} else {
1955			copies = Find_flag_value(&job->info,COPIES,Value_sep);
1956			copy = Find_flag_value(&job->info,COPY_DONE,Value_sep);
1957			id = Find_str_value(&job->info,IDENTIFIER,Value_sep);
1958			if( !id ){
1959				Errorcode = JABORT;
1960				FATAL(LOG_ERR)
1961					_("Update_status: no identifier for '%s'"),
1962					Find_str_value(&job->info,HF_NAME,Value_sep) );
1963			}
1964
1965			if( Find_str_value(&job->info,MOVE,Value_sep) ){
1966				Set_flag_value(&job->info,DONE_TIME,time((void *)0));
1967				setstatus( job, "%s@%s: job '%s' moved",
1968					Printer_DYN, FQDNHost_FQDN, id );
1969			} else {
1970				++copy;
1971				Set_flag_value(&job->info,COPY_DONE,copy);
1972				if( copies ){
1973					setstatus( job, "%s@%s: job '%s' printed copy %d of %d",
1974					Printer_DYN, FQDNHost_FQDN, id, copy, copies );
1975				}
1976				if( copy >= copies ){
1977					Set_flag_value(&job->info,DONE_TIME,time((void *)0));
1978#ifdef ORIGINAL_DEBUG//JY@1020
1979					Sendmail_to_user( status, job );
1980#endif
1981					setstatus( job, "%s@%s: job '%s' printed",
1982						Printer_DYN, FQDNHost_FQDN, id );
1983				} else {
1984					Set_hold_file( job, 0, 0 );
1985					break;
1986				}
1987			}
1988		done_job:
1989			hf_name = Find_str_value(&job->info,HF_NAME,Value_sep);
1990			DEBUG3("Update_status: done_job, id '%s', hf '%s'", id, hf_name );
1991			if( hf_name ){
1992				Set_flag_value(&job->info,REMOVE_TIME,time((void *)0));
1993				Set_hold_file( job, 0, 0 );
1994#ifdef ORIGINAL_DEBUG//JY@1020
1995				if(DEBUGL3)Dump_job("Update_status - done_job", job );
1996#endif
1997				if( (Save_when_done_DYN || Done_jobs_DYN || Done_jobs_max_age_DYN) ){
1998					setstatus( job, _("job '%s' saved"), id );
1999					++Done_count;
2000					if( !Done_time ) Done_time = time(0);
2001				} else {
2002					if( Remove_job( job ) ){
2003						setstatus( job, _("could not remove job '%s'"), id);
2004					} else {
2005						setstatus( job, _("job '%s' removed"), id );
2006					}
2007				}
2008			}
2009		}
2010		break;
2011
2012	case JTIMEOUT:
2013	case JFAIL:	/* failed, retry ?*/
2014		status = JFAIL;
2015		if( destination ){
2016			attempt = Find_flag_value(destination,ATTEMPT,Value_sep);
2017			++attempt;
2018			Set_flag_value(destination,ATTEMPT,attempt);
2019			Update_destination(job);
2020		} else {
2021			attempt = Find_flag_value(&job->info,ATTEMPT,Value_sep);
2022			++attempt;
2023			Set_flag_value(&job->info,ATTEMPT,attempt);
2024		}
2025		DEBUG1( "Update_status: JFAIL - attempt %d, max %d",
2026			attempt, Send_try_DYN );
2027		Set_hold_file( job, 0, 0 );
2028
2029		if( Send_try_DYN > 0 && attempt >= Send_try_DYN ){
2030			char buf[60];
2031
2032			/* check to see what the failure action
2033			 *	should be - abort, failure; default is remove
2034			 */
2035			setstatus( job, _("job '%s', attempt %d, allowed %d"),
2036				id, attempt, Send_try_DYN );
2037			status = Decode_transfer_failure( attempt, job );
2038			switch( status ){
2039			case JSUCC:   strv = _("treating as successful"); break;
2040			case JFAIL:   strv = _("retrying job"); break;
2041			case JFAILNORETRY:   strv = _("no retry"); break;
2042			case JABORT:  strv = _("aborting server"); break;
2043			case JREMOVE: strv = _("removing job - status JREMOVE"); break;
2044			case JHOLD:   strv = _("holding job"); break;
2045			default:
2046				SNPRINTF( buf, sizeof(buf))
2047					_("unexpected status 0x%x"), status );
2048				strv = buf;
2049				status = JABORT;
2050				break;
2051			}
2052			setstatus( job, _("job '%s', %s"), id, strv );
2053		}
2054		if( status == JFAIL ){
2055			if( Send_try_DYN > 0 ){
2056				setstatus( job, _("job '%s' attempt %d, trying %d times"),
2057					id, attempt, Send_try_DYN );
2058			} else {
2059				setstatus( job, _("job '%s' attempt %d, trying indefinitely"),
2060					id, attempt);
2061			}
2062			if( destination ){
2063				Set_str_value(destination,ERROR,0);
2064				Set_flag_value(destination,ERROR_TIME,0);
2065				Set_str_value(destination,PRSTATUS,0);
2066			} else {
2067				Set_str_value(&job->info,ERROR,0);
2068				Set_flag_value(&job->info,ERROR_TIME,0);
2069				Set_str_value(&job->info,PRSTATUS,0);
2070			}
2071			Set_hold_file( job, 0, 0 );
2072		} else {
2073			goto again;
2074		}
2075		break;
2076
2077	case JFAILNORETRY:	/* do not try again */
2078		SNPRINTF( buffer, sizeof(buffer)) _("failed, no retry") );
2079		if( destination ){
2080			attempt = Find_flag_value(destination,ATTEMPT,Value_sep);
2081			++attempt;
2082			if( !Find_str_value(destination,ERROR,Value_sep) ){
2083				Set_str_value(destination,ERROR,buffer);
2084			}
2085			if( !Find_flag_value(destination,ERROR_TIME,Value_sep) ){
2086				Set_nz_flag_value(destination,ERROR_TIME,time(0));
2087			}
2088			Set_flag_value(destination,ATTEMPT,attempt);
2089			Update_destination(job);
2090			Set_hold_file( job, 0, 0 );
2091		} else {
2092			attempt = Find_flag_value(&job->info,ATTEMPT,Value_sep);
2093			++attempt;
2094			Set_flag_value(&job->info,ATTEMPT,attempt);
2095			if( !Find_str_value(&job->info,ERROR,Value_sep) ){
2096				Set_str_value(&job->info,ERROR,buffer);
2097			}
2098			if( !Find_flag_value(&job->info,ERROR_TIME,Value_sep) ){
2099				Set_nz_flag_value(&job->info,ERROR_TIME,time(0));
2100			}
2101			Set_nz_flag_value(&job->info,REMOVE_TIME, time( (void *)0) );
2102			Set_hold_file( job, 0, 0 );
2103#ifdef ORIGINAL_DEBUG//JY@1020
2104			Sendmail_to_user( status, job );
2105#endif
2106			if( (Save_on_error_DYN || Done_jobs_DYN || Done_jobs_max_age_DYN) ){
2107				setstatus( job, _("job '%s' saved"), id );
2108				++Done_count;
2109				if( !Done_time ) Done_time = time(0);
2110			} else {
2111				setstatus( job, _("removing job '%s' - JFAILNORETRY"), id);
2112				if( Remove_job( job ) ){
2113					setstatus( job, _("could not remove job '%s'"), id);
2114				} else {
2115					setstatus( job, _("job '%s' removed"), id );
2116				}
2117			}
2118		}
2119		break;
2120
2121	default:
2122	case JABORT:	/* abort, do not try again */
2123		SNPRINTF(buffer,sizeof(buffer)) _("aborting operations") );
2124		Set_flag_value(&job->info,PRIORITY_TIME,0);
2125		if( destination ){
2126			if( !Find_str_value(destination,ERROR,Value_sep) ){
2127				Set_str_value(destination,ERROR,buffer);
2128			}
2129			if( !Find_flag_value(destination,ERROR_TIME,Value_sep) ){
2130				Set_nz_flag_value(destination,ERROR_TIME,time(0));
2131			}
2132			strv = Find_str_value(destination,ERROR,Value_sep);
2133			Update_destination(job);
2134			setstatus( job, "job '%s', destination '%s', error '%s'",
2135				id,did,strv );
2136		} else {
2137			if( !Find_str_value(&job->info,ERROR,Value_sep) ){
2138				Set_str_value(&job->info,ERROR,buffer);
2139			}
2140			if( !Find_flag_value(&job->info,ERROR_TIME,Value_sep) ){
2141				Set_nz_flag_value(&job->info,ERROR_TIME,time(0));
2142			}
2143			strv = Find_str_value(&job->info,ERROR,Value_sep);
2144			setstatus( job, "job '%s' error '%s'",id, strv);
2145			Set_nz_flag_value(&job->info,REMOVE_TIME, time( (void *)0) );
2146			Set_hold_file( job, 0, 0 );
2147#ifdef ORIGINAL_DEBUG//JY@1020
2148			Sendmail_to_user( status, job );
2149#endif
2150			if( (Save_on_error_DYN || Done_jobs_DYN || Done_jobs_max_age_DYN) ){
2151				setstatus( job, _("job '%s' saved"), id );
2152				++Done_count;
2153				if( !Done_time ) Done_time = time(0);
2154			} else {
2155				setstatus( job, _("removing job '%s' - JABORT"), id);
2156				if( Remove_job( job ) ){
2157					setstatus( job, _("could not remove job '%s'"), id);
2158				} else {
2159					setstatus( job, _("job '%s' removed"), id );
2160				}
2161			}
2162		}
2163		if( Stop_on_abort_DYN ){
2164			setstatus( job, _("stopping printing on filter JABORT exit code") );
2165			Set_flag_value( &Spool_control,PRINTING_ABORTED,1 );
2166			Set_spool_control(0, Queue_control_file_DYN, &Spool_control);
2167		}
2168		break;
2169
2170	case JREMOVE:	/* failed, remove job */
2171		if( destination ){
2172			if( !Find_str_value(destination,ERROR,Value_sep) ){
2173				SNPRINTF( buffer, sizeof(buffer))
2174					_("removing destination due to errors") );
2175				Set_str_value(destination,ERROR,buffer);
2176			}
2177			if( !Find_flag_value(destination,ERROR_TIME,Value_sep) ){
2178				Set_nz_flag_value(destination,ERROR_TIME,time(0));
2179			}
2180			Update_destination(job);
2181			Set_hold_file( job, 0, 0 );
2182		} else {
2183			if( !Find_str_value(&job->info,ERROR,Value_sep) ){
2184				SNPRINTF( buffer, sizeof(buffer))
2185					_("too many errors") );
2186				Set_str_value(&job->info,ERROR,buffer);
2187			}
2188			if( !Find_flag_value(&job->info,ERROR_TIME,Value_sep) ){
2189				Set_nz_flag_value(&job->info,ERROR_TIME,time(0));
2190			}
2191			Set_nz_flag_value(&job->info,REMOVE_TIME, time( (void *)0) );
2192			Set_hold_file( job, 0, 0 );
2193#ifdef ORIGINAL_DEBUG//JY@1020
2194			Sendmail_to_user( status, job );
2195#endif
2196			if( (Save_on_error_DYN || Done_jobs_DYN || Done_jobs_max_age_DYN) ){
2197				setstatus( job, _("job '%s' saved"), id );
2198				++Done_count;
2199				if( !Done_time ) Done_time = time(0);
2200			} else {
2201				setstatus( job, _("removing job '%s' - JREMOVE"), id);
2202				if( Remove_job( job ) ){
2203					setstatus( job, _("could not remove job '%s'"), id);
2204				} else {
2205					setstatus( job, _("job '%s' removed"), id );
2206				}
2207			}
2208		}
2209		break;
2210	}
2211#ifdef ORIGINAL_DEBUG//JY@1020
2212	if(DEBUGL3)Dump_job("Update_status: exit result", job );
2213#endif
2214}
2215
2216/***************************************************************************
2217 * int Check_print_perms
2218 *  check the printing permissions
2219 ***************************************************************************/
2220
2221int Check_print_perms( struct job *job )
2222{
2223	char *s;
2224	int permission;
2225
2226	memset( &Perm_check, 0, sizeof(Perm_check) );
2227	Perm_check.service = 'P';
2228	Perm_check.printer = Printer_DYN;
2229	Perm_check.user = Find_str_value(&job->info,LOGNAME,Value_sep);
2230	Perm_check.remoteuser = Perm_check.user;
2231	Perm_check.authuser = Find_str_value(&job->info,AUTHUSER,Value_sep);
2232	Perm_check.authfrom = Find_str_value(&job->info,AUTHFROM,Value_sep);
2233	Perm_check.authtype = Find_str_value(&job->info,AUTHTYPE,Value_sep);
2234	Perm_check.authca = Find_str_value(&job->info,AUTHCA,Value_sep);
2235	s = Find_str_value(&job->info,FROMHOST,Value_sep);
2236	if( s && Find_fqdn( &PermHost_IP, s ) ){
2237		Perm_check.host = &PermHost_IP;
2238	}
2239	s = Find_str_value(&job->info,REMOTEHOST,Value_sep);
2240	if( s && Find_fqdn( &RemoteHost_IP, s ) ){
2241		Perm_check.remotehost = &RemoteHost_IP;
2242	} else {
2243		Perm_check.remotehost = Perm_check.host;
2244	}
2245	Perm_check.unix_socket = Find_flag_value(&job->info,UNIXSOCKET,Value_sep);
2246	Perm_check.port = Find_flag_value(&job->info,REMOTEPORT,Value_sep);
2247	permission = Perms_check( &Perm_line_list,&Perm_check, job, 1 );
2248	DEBUG3("Check_print_perms: permission '%s'", perm_str(permission) );
2249	return( permission );
2250}
2251
2252
2253void Setup_user_reporting( struct job *job )
2254{
2255	char *host = Find_str_value(&job->info,MAILNAME,Value_sep);
2256	char *port = 0, *protocol = "UDP", *s;
2257	int prot_num = SOCK_DGRAM;
2258
2259	DEBUG1("Setup_user_reporting: Allow_user_logging %d, host '%s'",
2260		Allow_user_logging_DYN, host );
2261	if( !Allow_user_logging_DYN || host==0
2262		|| safestrchr(host,'@') || !safestrchr(host,'%') ){
2263		return;
2264	}
2265
2266	host = safestrdup(host,__FILE__,__LINE__);
2267	/* OK, we try to open a connection to the logger */
2268	if( (s = safestrchr( host, '%')) ){
2269		/* *s++ = 0; */
2270		port = s;
2271	}
2272	if( (s = safestrchr( port, ',')) ){
2273		*s++ = 0;
2274		protocol = s;
2275		if( safestrcasecmp( protocol, "TCP" ) == 0 ){
2276			protocol = "TCP";
2277			prot_num = SOCK_STREAM;
2278		}
2279	}
2280
2281	DEBUG3("setup_logger_fd: host '%s', port '%s', protocol %d",
2282		host, port, prot_num );
2283	Mail_fd = Link_open_type(host, 10, prot_num, 0, 0 );
2284	DEBUG3("Setup_user_reporting: Mail_fd '%d'", Mail_fd );
2285
2286	if( Mail_fd > 0 && prot_num == SOCK_STREAM && Exit_linger_timeout_DYN > 0 ){
2287		Set_linger( Mail_fd, Exit_linger_timeout_DYN );
2288	}
2289	if( host ) free(host); host = 0;
2290}
2291
2292void Service_worker( struct line_list *args )
2293{
2294	int pid, unspooler_fd, destinations, attempt, n, lpd_bounce;
2295	struct line_list *destination;
2296	char *s, *path, *hf_name, *new_dest, *move_dest,
2297		*id, *did;
2298	struct stat statb;
2299	char buffer[SMALLBUFFER];
2300	struct job job;
2301
2302	Name="(Worker)";
2303	destination = 0;
2304	attempt = 0;
2305
2306	Init_job(&job);
2307
2308	Set_DYN(&Printer_DYN, Find_str_value(args,PRINTER,Value_sep));
2309	setproctitle( "lpd %s '%s'", Name, Printer_DYN );
2310
2311	DEBUG1("Service_worker: begin");
2312
2313	(void) plp_signal(SIGUSR1, (plp_sigfunc_t)cleanup_USR1);
2314	Errorcode = JABORT;
2315
2316	/* you need to have a spool queue */
2317	if( Setup_printer( Printer_DYN, buffer, sizeof(buffer), 0 ) ){
2318		cleanup(0);
2319	}
2320
2321	if(DEBUGL4){ int fd; fd = dup(0);
2322	LOGDEBUG("Service_worker: after Setup_printer next fd %d",fd); close(fd); };
2323
2324	pid = getpid();
2325	DEBUG1( "Service_worker: pid %d", pid );
2326	path = Make_pathname( Spool_dir_DYN, Queue_unspooler_file_DYN );
2327	if( (unspooler_fd = Checkwrite( path, &statb, O_RDWR, 1, 0 )) < 0 ){
2328		LOGERR_DIE(LOG_ERR) _("Service_worker: cannot open lockfile '%s'"),
2329			path );
2330	}
2331	if(path) free(path); path = 0;
2332	Write_pid( unspooler_fd, pid, (char *)0 );
2333	close(unspooler_fd); unspooler_fd = -1;
2334
2335	DEBUG3("Service_worker: checking path '%s'", path );
2336
2337	hf_name = Find_str_value(args,HF_NAME,Value_sep);
2338	Get_hold_file( &job, hf_name );
2339
2340	if( Setup_cf_info( &job, 1 ) ){
2341		DEBUG3("Service_worker: missing files");
2342		Errorcode = 0;
2343		cleanup(0);
2344	}
2345
2346	Set_str_value(&job.info,NEW_DEST, Find_str_value(args,NEW_DEST,Value_sep));
2347	Set_str_value(&job.info,MOVE_DEST, Find_str_value(args,MOVE_DEST,Value_sep));
2348	Set_decimal_value(&job.info,SERVER,getpid());
2349
2350	Free_line_list(args);
2351
2352	n = Set_hold_file( &job, 0, 0 );
2353	if( n ){
2354		/* you cannot update hold file!! */
2355		setstatus( &job, _("cannot update hold file for '%s'"),
2356			hf_name );
2357		FATAL(LOG_ERR)
2358			_("Service_worker: cannot update hold file for '%s'"),
2359			hf_name );
2360	}
2361
2362	id = Find_str_value(&job.info,IDENTIFIER,Value_sep);
2363	if( !id ){
2364		FATAL(LOG_ERR)
2365			_("Service_worker: no identifier for '%s'"),
2366			Find_str_value(&job.info,HF_NAME,Value_sep) );
2367	}
2368
2369	if( (destinations = Find_flag_value(&job.info,DESTINATIONS,Value_sep)) ){
2370		did = Find_str_value(&job.info,DESTINATION,Value_sep );
2371		if( !Get_destination_by_name( &job, did ) ){
2372			destination = &job.destination;
2373			attempt = Find_flag_value(destination,ATTEMPT,Value_sep);
2374		}
2375	} else {
2376		attempt = Find_flag_value(&job.info,ATTEMPT,Value_sep);
2377	}
2378	DEBUG3("Service_worker: attempt %d", attempt );
2379	new_dest = Find_str_value(&job.info,NEW_DEST,Value_sep);
2380	move_dest = Find_str_value(&job.info,MOVE_DEST,Value_sep);
2381	lpd_bounce = Lpd_bounce_DYN;
2382	if( move_dest ){
2383		lpd_bounce = 0;
2384		new_dest = move_dest;
2385	}
2386
2387	/*
2388	 * The following code is implementing job handling as follows.
2389	 *  if new_dest has a value then
2390	 *    new_dest has format 'pr' or 'pr@host'
2391	 *    if pr@host then
2392	 *       set RemotePrinter_DYN and RemoteHost_DYN
2393	 *   else
2394	 *       set RemotePrinter_DYN to pr
2395	 *       set RemoteHost_DYN to FQDNHost_FQDN
2396	 */
2397
2398	if( new_dest ){
2399		Set_DYN( &RemoteHost_DYN, 0);
2400		Set_DYN( &RemotePrinter_DYN, 0);
2401		Set_DYN( &Lp_device_DYN, 0);
2402
2403		Set_DYN( &RemotePrinter_DYN, new_dest );
2404		if( (s = safestrchr(RemotePrinter_DYN, '@')) ){
2405			*s++ = 0;
2406			Set_DYN( &RemoteHost_DYN, s );
2407			if( (s = safestrchr(s,'%')) ){
2408				*s++ = 0;
2409				Set_DYN( &Lpd_port_DYN,s );
2410			}
2411		}
2412		if( !RemoteHost_DYN ){
2413			Set_DYN( &RemoteHost_DYN, LOCALHOST);
2414		}
2415	}
2416
2417	/* we put a timeout before each attempt */
2418	if( attempt > 0 ){
2419		n = 8;
2420		if( attempt < n ) n = attempt;
2421		n = Connect_interval_DYN * (1 << (n-1)) + Connect_grace_DYN;
2422		if( Max_connect_interval_DYN > 0 && n > Max_connect_interval_DYN ){
2423			n = Max_connect_interval_DYN;
2424		}
2425		DEBUG1("Service_worker: attempt %d, sleeping %d", attempt, n);
2426		if( n > 0 ){
2427			setstatus( &job, "attempt %d, sleeping %d before retry", attempt+1, n );
2428			plp_sleep(n);
2429		}
2430	}
2431#ifdef ORIGINAL_DEBUG//JY@1020
2432
2433	if( RemotePrinter_DYN ){
2434		Name = "(Worker - Remote)";
2435		DEBUG1( "Service_worker: sending '%s' to '%s@%s'",
2436			id, RemotePrinter_DYN, RemoteHost_DYN );
2437		setproctitle( "lpd %s '%s'", Name, Printer_DYN );
2438		if( Remote_support_DYN ) uppercase( Remote_support_DYN );
2439		if( safestrchr( Remote_support_DYN, 'R' ) ){
2440			Errorcode = Remote_job( &job, lpd_bounce, move_dest, id );
2441		} else {
2442			Errorcode = JABORT;
2443			setstatus( &job, "no remote support to `%s@%s'",
2444			RemotePrinter_DYN, RemoteHost_DYN );
2445		}
2446	} else {
2447#endif
2448		Name = "(Worker - Print)";
2449		DEBUG1( "Service_worker: printing '%s'", id );
2450		setproctitle( "lpd %s '%s'", Name, Printer_DYN );
2451		Errorcode = Local_job( &job, id );
2452#ifdef ORIGINAL_DEBUG//JY@1020
2453	}
2454#endif
2455	cleanup(0);
2456}
2457
2458/***************************************************************************
2459 * int Printer_open: opens the Printer_DYN
2460 ***************************************************************************/
2461
2462/*
2463 * int Printer_open(
2464 *   lp_device        - open this device
2465 *   char error, int errlen - record errors
2466 *   int max_attempts - max attempts to open device or socket
2467 *   int interval,    - interval between attempts
2468 *   int max_interval, - maximum interval
2469 *   int grace,       - minimum time between attempts
2470 *   int connect_timeout - time to wait for connection success
2471 *   int *pid         - if we have a filter program, return pid
2472 *  returns:
2473 *    file descriptor
2474 */
2475
2476int Printer_open( char *lp_device, int *status_fd, struct job *job,
2477	int max_attempts, int interval, int max_interval, int grace,
2478	int connect_tmout, int *filterpid, int *poll_for_status )
2479{
2480	int attempt, err = 0, n, device_fd, c, in[2], pid, readable, mask;
2481	struct stat statb;
2482	time_t tm;
2483	char tm_str[32];
2484	char *host, *port, *filter;
2485	struct line_list args;
2486
2487	Init_line_list(&args);
2488	host = port = filter = 0;
2489	*filterpid = 0;
2490	DEBUG1( "Printer_open: device '%s', max_attempts %d, grace %d, interval %d, max_interval %d",
2491		lp_device, max_attempts, grace, interval, max_interval );
2492	time( &tm );
2493	tm_str[0] = 0;
2494	if( lp_device == 0 ){
2495		FATAL(LOG_ERR) "Printer_open: printer '%s' missing lp_device value",
2496			Printer_DYN );
2497	}
2498
2499	*status_fd = device_fd = -1;
2500	*poll_for_status = 0;
2501	/* we repeat until we get the device or exceed maximum attempts */
2502	for( attempt = 0; device_fd < 0 && (max_attempts <= 0 || attempt < max_attempts); ++attempt ){
2503		if( grace ) plp_sleep(grace);
2504		c = lp_device[0];
2505		switch( c ){
2506		case '|':
2507#if !defined(HAVE_SOCKETPAIR)
2508			FATAL(LOG_ERR)"Printer_open: requires socketpair() system call for output device to be filter");
2509#else
2510			if( socketpair( AF_UNIX, SOCK_STREAM, 0, in ) == -1 ){
2511				Errorcode = JFAIL;
2512				LOGERR_DIE(LOG_INFO)"Printer_open: socketpair() for filter input failed");
2513			}
2514#endif
2515			Max_open(in[0]); Max_open(in[1]);
2516			DEBUG3("Printer_open: fd in[%d,%d]", in[0], in[1] );
2517			/* set up file descriptors */
2518			Free_line_list(&args);
2519			Check_max(&args,10);
2520			args.list[args.count++] = Cast_int_to_voidstar(in[0]);	/* stdin */
2521			args.list[args.count++] = Cast_int_to_voidstar(in[0]);	/* stdout */
2522			args.list[args.count++] = Cast_int_to_voidstar(in[0]);	/* stderr */
2523			if( (pid = Make_passthrough( lp_device, Filter_options_DYN, &args,
2524					job, 0 )) < 0 ){
2525				Errorcode = JFAIL;
2526				LOGERR_DIE(LOG_INFO)
2527					"Printer_open: could not create LP_FILTER process");
2528			}
2529			args.count = 0;
2530			Free_line_list(&args);
2531
2532			*filterpid = pid;
2533			device_fd = in[1];
2534			*status_fd = in[1];
2535			if( (close( in[0] ) == -1 ) ){
2536				LOGERR_DIE(LOG_INFO)"Printer_open: close(%d) failed", in[0]);
2537			}
2538			break;
2539
2540		case '/':
2541			DEBUG3( "Printer_open: Is_server %d, DaemonUID %d, DaemonGID %d, UID %d, EUID %d, GID %d, EGID %d",
2542			Is_server, DaemonUID, DaemonGID,
2543			getuid(), geteuid(), getgid(), getegid() );
2544			device_fd = Checkwrite_timeout( connect_tmout, lp_device, &statb,
2545				(Read_write_DYN || Lock_it_DYN) ?(O_RDWR):(O_APPEND|O_WRONLY),
2546				0, Nonblocking_open_DYN );
2547			err = errno;
2548			if( device_fd > 0 ){
2549				if( Lock_it_DYN ){
2550					int status;
2551					/*
2552					 * lock the device so that multiple servers can
2553					 * use it
2554					 */
2555					status = 0;
2556					if( isatty( device_fd ) ){
2557						status = LockDevice( device_fd, 0 );
2558					} else if( S_ISREG(statb.st_mode ) ){
2559						status = Do_lock( device_fd, 0 );
2560					}
2561					if( status < 0 ){
2562						err = errno;
2563#ifdef ORIGINAL_DEBUG//JY@1020
2564						setstatus( job,
2565							"lock '%s' failed - %s", lp_device, Errormsg(errno) );
2566#endif
2567						close( device_fd );
2568						device_fd = -1;
2569					}
2570				}
2571#ifdef ORIGINAL_DEBUG//JY@1020
2572				if( isatty( device_fd ) ){
2573					Do_stty( device_fd );
2574				}
2575#endif
2576				*status_fd = device_fd;
2577			}
2578			break;
2579		default:
2580			if( safestrchr( lp_device, '%' ) ){
2581				/* we have a host%port form */
2582				host = lp_device;
2583			} else {
2584				Errorcode = JABORT;
2585				FATAL(LOG_ERR) "Printer_open: printer '%s', bad 'lp' entry '%s'",
2586					Printer_DYN, lp_device );
2587			}
2588			DEBUG1( "Printer_open: doing link open '%s'", lp_device );
2589			*status_fd = device_fd = Link_open( host, connect_tmout, 0, 0 );
2590            err = errno;
2591			break;
2592		}
2593
2594		if( device_fd < 0 ){
2595#ifdef ORIGINAL_DEBUG//JY@1020
2596			DEBUG1( "Printer_open: open '%s' failed, max_attempts %d, attempt %d '%s'",
2597				lp_device, max_attempts, attempt, Errormsg(err) );
2598#endif
2599			if( max_attempts && attempt <= max_attempts ){
2600				n = 8;
2601				if( attempt < n ) n = attempt;
2602				n = interval*( 1 << n );
2603				if( max_interval > 0 && n > max_interval ) n = max_interval;
2604#ifdef ORIGINAL_DEBUG//JY@1020
2605				setstatus( job, "cannot open '%s' - '%s', attempt %d, sleeping %d",
2606						lp_device, Errormsg( err), attempt+1, n );
2607#endif
2608				if( n > 0 ){
2609					plp_sleep(n);
2610				}
2611			} else {
2612#ifdef ORIGINAL_DEBUG//JY@1020
2613				setstatus( job, "cannot open '%s' - '%s', attempt %d",
2614						lp_device, Errormsg( err), attempt+1 );
2615#endif
2616			}
2617		}
2618	}
2619	if( device_fd >= 0 ){
2620		int fd = *status_fd;
2621		if( fstat( fd, &statb ) < 0 ) {
2622			LOGERR_DIE(LOG_INFO)"Printer_open: fstat() on status_fd %d failed", fd);
2623		}
2624		/* we can only read status from a device, fifo, or socket */
2625		if( (mask = fcntl( fd, F_GETFL, 0 )) == -1 ){
2626			Errorcode = JABORT;
2627			LOGERR_DIE(LOG_ERR) "Printer_open: cannot fcntl fd %d", fd );
2628		}
2629		DEBUG2( "Printer_open: status_fd %d fcntl 0%o", fd, mask );
2630		mask &= O_ACCMODE;
2631		/* first, check to see if we have RD or RW */
2632		readable = 1;
2633		switch( mask ){
2634		case O_WRONLY:
2635			readable = 0;
2636			if( fd == device_fd ){
2637				*status_fd = -1;
2638			} else {
2639				Errorcode = JABORT;
2640				FATAL(LOG_ERR) "Printer_open: LOGIC ERROR: status_fd %d WRITE ONLY", fd );
2641			}
2642			break;
2643		}
2644		/* we handle the case where we have a device like a parallel port
2645		 *  or a USB port which does NOT support 'select()'
2646		 * AND where there may be some really strange status at the end
2647		 * of the printing operation
2648		 * AND we cannot close the connection UNTIL we get the status
2649		 *   This is really silly but we need to handle it.  Note that the
2650		 *   IFHP filter has to do exactly the same thing... Sigh...
2651		 */
2652		if( readable && S_ISCHR(statb.st_mode) && !isatty(device_fd) ){
2653			*poll_for_status = 1;
2654		}
2655	}
2656
2657	DEBUG1 ("Printer_open: '%s' is fd %d", lp_device, device_fd);
2658	return( device_fd );
2659}
2660
2661void Add_banner_to_job( struct job *job )
2662{
2663	char *banner_name, *tempfile;
2664	struct line_list *lp;
2665	int tempfd;
2666
2667	Errorcode = 0;
2668    banner_name = Find_str_value(&job->info, BNRNAME, Value_sep );
2669    if( banner_name == 0 ){
2670        banner_name = Find_str_value( &job->info,LOGNAME,Value_sep);
2671	}
2672	if( banner_name == 0 ) banner_name = "ANONYMOUS";
2673	Set_str_value(&job->info,BNRNAME,banner_name);
2674    banner_name = Find_str_value(&job->info, BNRNAME, Value_sep );
2675	DEBUG1("Add_banner_to_job: banner name '%s'", banner_name );
2676	if( !Banner_last_DYN ){
2677		DEBUG1("Add_banner_to_job: banner at start");
2678		Init_buf(&Outbuf, &Outmax, &Outlen );
2679		Print_banner( banner_name, Banner_start_DYN, job );
2680        tempfd = Make_temp_fd(&tempfile);
2681		if( Write_fd_len( tempfd, Outbuf, Outlen ) < 0 ){
2682			LOGERR(LOG_INFO)"Add_banner_to_job: write to '%s' failed", tempfile );
2683			Errorcode = JABORT;
2684			return;
2685		}
2686		close(tempfd);
2687		lp = malloc_or_die(sizeof(lp[0]),__FILE__,__LINE__);
2688		memset(lp,0,sizeof(lp[0]));
2689		Check_max(&job->datafiles,1);
2690		memmove( &job->datafiles.list[1], &job->datafiles.list[0],
2691			job->datafiles.count * sizeof(job->datafiles.list[0]) );
2692		job->datafiles.list[0] = (void *)lp;
2693		++job->datafiles.count;
2694
2695		Set_str_value(lp,OPENNAME,tempfile);
2696		Set_str_value(lp,TRANSFERNAME,tempfile);
2697		Set_str_value(lp,"N","BANNER");
2698		Set_str_value(lp,FORMAT,"f");
2699	}
2700	if( Banner_last_DYN || Banner_end_DYN) {
2701		Init_buf(&Outbuf, &Outmax, &Outlen );
2702		Print_banner( banner_name, Banner_end_DYN, job );
2703        tempfd = Make_temp_fd(&tempfile);
2704		if( Write_fd_len( tempfd, Outbuf, Outlen ) < 0 ){
2705			LOGERR(LOG_INFO)"Add_banner_to_job: write to '%s' failed", tempfile );
2706			Errorcode = JABORT;
2707			return;
2708		}
2709		close(tempfd);
2710		lp = malloc_or_die(sizeof(lp[0]),__FILE__,__LINE__);
2711		memset(lp,0,sizeof(lp[0]));
2712		Check_max(&job->datafiles,1);
2713		job->datafiles.list[job->datafiles.count] = (void *)lp;
2714		++job->datafiles.count;
2715		Set_str_value(lp,OPENNAME,tempfile);
2716		Set_str_value(lp,TRANSFERNAME,tempfile);
2717		Set_str_value(lp,"N","BANNER");
2718		Set_str_value(lp,FORMAT,"f");
2719	}
2720#ifdef ORIGINAL_DEBUG//JY@1020
2721	if(DEBUGL3)Dump_job("Add_banner_to_job", job);
2722#endif
2723}
2724
2725/* change the format of the output of a filter
2726 * bq_format=IoIo...D
2727 *   I is input type or '*' for all types
2728 *   o is output type
2729 *   D is default
2730 *     If no default, preserve the original type
2731 */
2732
2733void Fix_bq_format( int format, struct line_list *datafile )
2734{
2735	char fmt[2], *s;
2736	fmt[0] = format; fmt[1] = 0;
2737	if( (s = Bounce_queue_format_DYN) ){
2738		lowercase( s );
2739		while( s[0] ){
2740			if( s[1] ){
2741				if( format == cval(s) || cval(s) == '*' ){
2742					fmt[0] = s[1];
2743					break;
2744				}
2745			} else {
2746				if( cval(s) != '*' ){
2747					fmt[0] = s[0];
2748				}
2749				break;
2750			}
2751			s += 2;
2752		}
2753	}
2754	Set_str_value(datafile,FORMAT,fmt);
2755}
2756
2757/*
2758 * Filter all the files in the print job
2759 */
2760void Filter_files_in_job( struct job *job, int outfd, char *user_filter )
2761{
2762	struct line_list *datafile;
2763	char *tempfile, *openname, *format, *s, *filter, *id, *old_lp_value;
2764	char filter_name[8], filter_title[64], msg[SMALLBUFFER],
2765		filtermsgbuffer[SMALLBUFFER];
2766	struct stat statb;
2767	int tempfd, fd, n, pid, count, if_error[2];
2768	struct line_list files;
2769
2770	Init_line_list(&files);
2771	DEBUG1("Filter_files_in_job: starting, user_filter '%s'", user_filter);
2772    if(DEBUGL3){
2773		struct stat statb; int i;
2774        LOGDEBUG("Filter_files_in_job: START open fd's");
2775        for( i = 0; i < 20; ++i ){
2776            if( fstat(i,&statb) == 0 ){
2777                LOGDEBUG("  fd %d (0%o)", i, statb.st_mode&S_IFMT);
2778            }
2779        }
2780    }
2781	Errorcode = 0;
2782	old_lp_value = safestrdup(Find_str_value( &PC_entry_line_list, "lp", Value_sep ),
2783		__FILE__,__LINE__ );
2784
2785	id = Find_str_value(&job->info,IDENTIFIER,Value_sep);
2786	tempfd = -1;
2787	for( count = 0; count < job->datafiles.count; ++count ){
2788		datafile = (void *)job->datafiles.list[count];
2789#ifdef ORIGINAL_DEBUG//JY@1020
2790		if(DEBUGL4)Dump_line_list("Filter_files_in_job - datafile", datafile );
2791#endif
2792
2793		openname = Find_str_value(datafile,OPENNAME,Value_sep);
2794		if( !openname ) openname = Find_str_value(datafile,TRANSFERNAME,Value_sep);
2795		format = Find_str_value(datafile,FORMAT,Value_sep);
2796
2797		Set_str_value(&job->info,FORMAT,format);
2798		Set_str_value(&job->info,DF_NAME,openname);
2799		Set_str_value(&job->info,"N", Find_str_value(datafile,"N",Value_sep) );
2800
2801		/*
2802		 * now we check to see if there is an input filter
2803		 */
2804		SNPRINTF(filter_name,sizeof(filter_name))"%s","if");
2805		filter_name[0] = cval(format);
2806		filter = user_filter;
2807		switch( cval(format) ){
2808			case 'p': case 'f': case 'l':
2809				filter_name[0] = 'i';
2810				if( !filter ) filter = IF_Filter_DYN;
2811				break;
2812			case 'a': case 'i': case 'o': case 's':
2813				SETSTATUS(job)"bad data file format '%c', using 'f' format", cval(format) );
2814				filter_name[0] = 'i';
2815				format = "f";
2816				if( !filter ) filter = IF_Filter_DYN;
2817				break;
2818		}
2819		if( !filter ){
2820			filter = Find_str_value(&PC_entry_line_list,
2821				filter_name,Value_sep);
2822		}
2823		if( !filter){
2824			filter = Find_str_value(&Config_line_list,filter_name,
2825				Value_sep);
2826		}
2827		if( filter == 0 ) filter = Filter_DYN;
2828		DEBUG3("Filter_files_in_job: format '%s', filter '%s'", format, filter );
2829
2830		if( filter == 0 ){
2831			continue;
2832		}
2833
2834		uppercase(filter_name);
2835		if( filter ){
2836			s = filter;
2837			if( cval(s) == '(' ){
2838				++s;
2839				while( isspace(cval(s))) ++s;
2840			} else {
2841				if( !(s = strchr(filter,'/')) ) s = filter;
2842			}
2843			SNPRINTF(msg, sizeof(msg)) "%s", s );
2844			if( (s = strpbrk(msg,Whitespace)) ) *s = 0;
2845			if( (s = strrchr(msg,'/')) ) memmove(msg,s+1,safestrlen(s+1)+1);
2846		}
2847		SNPRINTF(filter_title,sizeof(filter_title))"%s filter '%s'",
2848			filter_name, msg );
2849
2850		if( (fd = Checkread( openname, &statb )) < 0 ){
2851			Errorcode = JFAIL;
2852			LOGMSG( LOG_ERR) "Filter_files_in_job: job '%s', cannot open data file '%s'",
2853				id, openname );
2854			goto end_of_job;
2855		}
2856		SETSTATUS(job)"processing '%s', size %0.0f, format '%s', %s",
2857			openname, (double)statb.st_size, format, filter_title );
2858		if( cval(format) == 'p' ){
2859			DEBUG3("Filter_files_in_job: using 'p' formatter '%s'", Pr_program_DYN );
2860			SETSTATUS(job)"format 'p' pretty printer '%s'", Pr_program_DYN);
2861			if( Pr_program_DYN == 0 ){
2862				SETSTATUS(job)"no 'p' format filter available" );
2863				Errorcode = JABORT;
2864				goto end_of_job;
2865			}
2866			tempfd = Make_temp_fd(&tempfile);
2867			Set_str_value(datafile,OPENNAME,tempfile);
2868			n = Filter_file( fd, tempfd, "PR_PROGRAM",
2869				Pr_program_DYN, 0, job, 0, 1 );
2870			if( n ){
2871				Errorcode = JABORT;
2872				LOGERR(LOG_INFO)"Filter_files_in_job:  could not make '%s' process",
2873					Pr_program_DYN );
2874				goto end_of_job;
2875			}
2876			close(fd); fd = tempfd; tempfd = -1;
2877			if( fstat(fd, &statb ) == -1 ){
2878				Errorcode = JABORT;
2879				LOGERR(LOG_INFO)"Filter_files_in_job: fstat() failed");
2880			}
2881			SETSTATUS(job)"data file '%s', size now %0.0f",
2882				openname, (double)statb.st_size );
2883			format = "f";
2884			Set_str_value(datafile,FORMAT,format);
2885		}
2886		if( filter ){
2887			DEBUG3("Filter_files_in_job: format '%s' starting filter '%s'",
2888				format, filter );
2889			DEBUG2("Filter_files_in_job: filter_stderr_to_status_file %d, ps '%s'",
2890				Filter_stderr_to_status_file_DYN, Status_file_DYN );
2891			if_error[0] = if_error[1] = -1;
2892			if( Filter_stderr_to_status_file_DYN && Status_file_DYN && *Status_file_DYN ){
2893				if_error[1] = Checkwrite( Status_file_DYN, &statb, O_WRONLY|O_APPEND, 0, 0 );
2894			} else if( pipe( if_error ) == -1 ){
2895				Errorcode = JFAIL;
2896				LOGERR(LOG_INFO)"Filter_files_in_job: pipe() failed");
2897				goto end_of_job;
2898			}
2899			Max_open(if_error[0]); Max_open(if_error[1]);
2900			DEBUG3("Filter_files_in_job: %s fd if_error[%d,%d]", filter_title,
2901				 if_error[0], if_error[1] );
2902			s = 0;
2903			if( Backwards_compatible_filter_DYN ) s = BK_filter_options_DYN;
2904			if( s == 0 ) s = Filter_options_DYN;
2905
2906			Free_line_list(&files);
2907			Check_max(&files, 10 );
2908			files.list[files.count++] = Cast_int_to_voidstar(fd);		/* stdin */
2909
2910			if( outfd > 0 ){
2911				files.list[files.count++] = Cast_int_to_voidstar(outfd);	/* stdout */
2912			} else {
2913				tempfd = Make_temp_fd(&tempfile);
2914				Set_str_value( &PC_entry_line_list, LP, tempfile );
2915				Set_str_value(datafile,OPENNAME,tempfile);
2916				files.list[files.count++] = Cast_int_to_voidstar(tempfd);	/* stdout */
2917			}
2918
2919			files.list[files.count++] = Cast_int_to_voidstar(if_error[1]);	/* stderr */
2920			if( (pid = Make_passthrough( filter, s, &files, job, 0 )) < 0 ){
2921				Errorcode = JFAIL;
2922				LOGERR(LOG_INFO)"Filter_files_in_job:  could not make %s process",
2923					filter_title );
2924				goto end_of_job;
2925			}
2926			files.count = 0;
2927			Free_line_list(&files);
2928
2929			if( fd > 0 ) close(fd); fd = -1;
2930			if( tempfd > 0 ) close(tempfd); tempfd = -1;
2931			if( (close(if_error[1]) == -1 ) ){
2932				Errorcode = JFAIL;
2933				LOGERR_DIE(LOG_INFO)"Filter_files_in_job: X5 close(%d) failed",
2934					if_error[1]);
2935			}
2936			if_error[1] = -1;
2937			Init_buf(&Outbuf, &Outmax, &Outlen );
2938
2939			filtermsgbuffer[0] = 0;
2940			if( if_error[0] != -1 ){
2941				n = Get_status_from_OF(job,filter_title,pid,
2942					if_error[0], filtermsgbuffer, sizeof(filtermsgbuffer)-1,
2943					0, 0, 0, Status_file_DYN );
2944				if( filtermsgbuffer[0] ){
2945					SETSTATUS(job) "%s filter msg - '%s'", filter_title, filtermsgbuffer );
2946				}
2947				if( n ){
2948					Errorcode = n;
2949					SETSTATUS(job)"%s filter problems, error '%s'",
2950						filter_title, Server_status(n));
2951					goto end_of_job;
2952				}
2953				close(if_error[0]);
2954				if_error[0] = -1;
2955			}
2956			/* now we get the exit status for the filter */
2957			n = Wait_for_pid( pid, filter_title, 0, 0 );
2958			if( n ){
2959				Errorcode = n;
2960				SETSTATUS(job)"%s filter exit status '%s'",
2961					filter_title, Server_status(n));
2962				goto end_of_job;
2963			}
2964			SETSTATUS(job) "%s filter finished", filter_title );
2965			Fix_bq_format( cval(format), datafile );
2966		}
2967		DEBUG3("Filter_files_in_job: finished file");
2968	}
2969 end_of_job:
2970	if( old_lp_value ) free( old_lp_value ); old_lp_value = 0;
2971	Free_line_list(&files);
2972    if(DEBUGL3){
2973		struct stat statb; int i;
2974        LOGDEBUG("Filter_files_in_job: END open fd's");
2975        for( i = 0; i < 20; ++i ){
2976            if( fstat(i,&statb) == 0 ){
2977                LOGDEBUG("  fd %d (0%o)", i, statb.st_mode&S_IFMT);
2978            }
2979        }
2980    }
2981#ifdef ORIGINAL_DEBUG//JY@1020
2982	if(DEBUGL3)Dump_job("Filter_files_in_job", job);
2983#endif
2984}
2985
2986void Service_queue( struct line_list *args )
2987{
2988	int subserver;
2989
2990	Set_DYN(&Printer_DYN, Find_str_value(args, PRINTER,Value_sep) );
2991	subserver = Find_flag_value( args, SUBSERVER, Value_sep );
2992
2993	Free_line_list(args);
2994	Do_queue_jobs( Printer_DYN, subserver );
2995	cleanup(0);
2996}
2997
2998
2999int Remove_done_jobs( void )
3000{
3001	struct job job;
3002	char *id;
3003	int removed = 0;
3004	time_t tm;
3005	int job_index, info_index, pid, remove, error, done, incoming;
3006	struct line_list info;
3007	char tval[SMALLBUFFER];
3008
3009	DEBUG3("Remove_done_jobs: save_when_done %d, save_on_error %d, done_jobs %d, d_j_max_age %d",
3010		Save_when_done_DYN, Save_on_error_DYN,
3011		Done_jobs_DYN, Done_jobs_max_age_DYN );
3012	if( Save_when_done_DYN || Save_on_error_DYN
3013		|| !(Done_jobs_DYN > 0 || Done_jobs_max_age_DYN > 0) ){
3014		return( 0 );
3015	}
3016
3017	Init_line_list(&info);
3018	time( &tm );
3019	Init_job(&job);
3020	for( job_index = 0; job_index < Sort_order.count; ++job_index ){
3021		char *hold_file = Sort_order.list[job_index];
3022		Free_job(&job);
3023		if( ISNULL(hold_file) ) continue;
3024		DEBUG3("Remove_done_jobs: done_jobs - job_index [%d] '%s'", job_index,
3025			hold_file);
3026		Get_hold_file( &job, hold_file );
3027#ifdef ORIGINAL_DEBUG//JY@1020
3028		if(DEBUGL4)Dump_job("Remove_done_jobs: done_jobs - job ",&job);
3029#endif
3030		if( job.info.count == 0 ) continue;
3031		/* get status from hold file */
3032		id = Find_str_value(&job.info,IDENTIFIER,Value_sep);
3033		done = Find_flag_value(&job.info,DONE_TIME,Value_sep);
3034		error = Find_flag_value(&job.info,ERROR_TIME,Value_sep);
3035		incoming = Find_flag_value(&job.info,INCOMING_TIME,Value_sep);
3036		remove = Find_flag_value(&job.info,REMOVE_TIME,Value_sep);
3037		DEBUG3("Remove_done_jobs: remove 0x%x, done 0x%x, error 0x%x, incoming 0x%x",
3038			remove, done, error, incoming );
3039		if( !remove ) continue;
3040		if( (pid = Find_flag_value(&job.info,SERVER,Value_sep)) && kill( pid, 0 ) == 0 ){
3041			DEBUG3("Remove_done_jobs: '%s' active %d", hold_file, pid );
3042			continue;
3043		}
3044		if( Done_jobs_max_age_DYN > 0
3045			&& ( (error && (tm - error) > Done_jobs_max_age_DYN)
3046			   || (done && (tm - done) > Done_jobs_max_age_DYN) ) ){
3047			setstatus( &job, _("job '%s' removed- status expired"), id );
3048			/* Setup_cf_info( &job, 0 ); */
3049			Remove_job( &job );
3050		} else if( Done_jobs_DYN > 0 ){
3051			SNPRINTF(tval,sizeof(tval)) "0x%08x", remove );
3052			Set_str_value(&info, tval, hold_file );
3053		}
3054	}
3055
3056#ifdef ORIGINAL_DEBUG//JY@1020
3057	if(DEBUGL1)Dump_line_list("Remove_done_jobs - removal candidates",&info);
3058	DEBUG1( "Remove_done_jobs: checking for removal - remove_count %d", Done_jobs_DYN );
3059#endif
3060
3061	for( info_index = 0; info_index < info.count - Done_jobs_DYN; ++info_index ){
3062		char *hold_file = info.list[info_index];
3063		if( (hold_file = safestrchr( hold_file, '=' )) ){
3064			++hold_file;
3065		} else {
3066			Errorcode = JABORT;
3067			fatal(LOG_ERR,"Remove_done_jobs: bad hold file format '%s'",
3068				info.list[info_index]);
3069		}
3070		DEBUG1( "Remove_done_jobs: [%d] hold_file '%s'",
3071			info_index, hold_file );
3072		Free_job(&job);
3073		Get_hold_file( &job, hold_file );
3074		Setup_cf_info( &job, 0 );
3075		Remove_job( &job );
3076		removed = 1;
3077	}
3078	Free_job(&job);
3079	Free_line_list(&info);
3080	if( removed && Lpq_status_file_DYN ){
3081		unlink(Lpq_status_file_DYN);
3082	}
3083	return( removed );
3084}
3085#endif
3086