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: lpc.c,v 1.1.1.1 2008/10/15 03:28:27 james26_jang Exp $";
28
29
30/***************************************************************************
31 * SYNOPSIS
32 *      lpc [ -PPrinter] [-S Server] [-U username ][-V] [-D debug] [command]
33 * commands:
34 *
35 *   status [printer]     - show printer status (default is all printers)
36 *   msg   printer  ....  - printer status message
37 *   start [printer]      - start printing
38 *   stop [printer]       - stop printing
39 *   up [printer]         - start printing and spooling
40 *   down [printer]       - stop printing and spooling
41 *   enable [printer]     - enable spooling
42 *   disable [printer]    - disable spooling
43 *   abort [printer]      - stop printing, kill server
44 *   kill [printer]       - stop printing, kill server, restart printer
45 *   flush [printer]      - flush cached status
46 *
47 *   topq printer (user [@host] | host | jobnumer)*
48 *   hold printer (all | user [@host] | host |  jobnumer)*
49 *   release printer (all | user [@host] | host | jobnumer)*
50 *
51 *   lprm printer [ user [@host]  | host | jobnumber ] *
52 *   lpq printer [ user [@host]  | host | jobnumber ] *
53 *   lpd [pr | pr@host]   - PID of LPD server
54 *   active [pr |pr@host] - check to see if server accepting connections
55 *   client [all | pr ]     - show client configuration and printcap info
56 *   server [all |pr ]     - show server configuration and printcap info
57 *   defaultq              - show default queue for LPD server\n\
58 *   defaults              - show default configuration values\n\
59 *   lang                  - show current i18n language selection and support\n\
60 *
61 * DESCRIPTION
62 *   lpc sends a  request to lpd(8)
63 *   and reports the status of the command
64 ****************************************************************************
65 *
66 * Implementation Notes
67 * Patrick Powell Wed Jun 28 21:28:40 PDT 1995
68 *
69 * The LPC program is an extremely simplified front end to the
70 * LPC functionality in the server.  The commands send to the LPD
71 * server have the following format:
72 *
73 * \6printer user command options
74 *
75 * If no printer is specified, the printer is the default from the
76 * environment variable, etc.
77 *
78 */
79
80#include "lp.h"
81#include "defs.h"
82#include "initialize.h"
83#include "getprinter.h"
84#include "sendreq.h"
85#include "child.h"
86#include "control.h"
87#include "getopt.h"
88#include "patchlevel.h"
89#include "errorcodes.h"
90
91/**** ENDINCLUDE ****/
92
93
94/***************************************************************************
95 * main()
96 * - top level of LPP Lite.
97 *
98 ****************************************************************************/
99
100
101#undef EXTERN
102#undef DEFINE
103#define EXTERN
104#define DEFINE(X) X
105#include "lpc.h"
106
107 void usage(void);
108 void use_msg(void);
109 void doaction( struct line_list *args );
110 static char *Username_JOB;
111
112int main(int argc, char *argv[], char *envp[])
113{
114	char *s;
115	int i;
116	char msg[ LINEBUFFER ];
117	struct line_list args;
118
119#if 0
120	DEBUG1("%s",5);
121	LOGDEBUG("%s",5);
122	FATAL(LOGINFO)"%s",5);
123	LOGERR(LOGINFO)"%s",5);
124#endif
125	/* set signal handlers */
126	(void) plp_signal (SIGHUP, cleanup_HUP);
127	(void) plp_signal (SIGINT, cleanup_INT);
128	(void) plp_signal (SIGQUIT, cleanup_QUIT);
129	(void) plp_signal (SIGTERM, cleanup_TERM);
130	(void) signal( SIGPIPE, SIG_IGN );
131	(void) signal( SIGCHLD, SIG_DFL);
132
133
134	/*
135	 * set up the user state
136	 */
137#ifndef NODEBUG
138	Debug = 0;
139#endif
140
141	Init_line_list(&args);
142	Initialize(argc, argv, envp, 'D' );
143	Setup_configuration();
144
145	/* scan the argument list for a 'Debug' value */
146
147	Get_parms(argc, argv);      /* scan input args */
148	if( Auth && !getenv( "AUTH" ) ){
149		FPRINTF(STDERR,
150		_("authentication requested (-A option) and AUTH environment variable not set") );
151		usage();
152	}
153
154	DEBUG1("lpc: Printer '%s', Optind '%d', argc '%d'", Printer_DYN, Optind, argc );
155	if(DEBUGL1){
156		int ii;
157		for( ii = Optind; ii < argc; ++ii ){
158			LOGDEBUG( " [%d] '%s'", ii, argv[ii] );
159		}
160	}
161
162	if( Username_JOB && OriginalRUID ){
163		struct line_list user_list;
164		char *str, *t;
165		struct passwd *pw;
166		int found;
167		uid_t uid;
168
169		DEBUG2("lpc: checking '%s' for -U perms",
170			Allow_user_setting_DYN );
171		Init_line_list(&user_list);
172		Split( &user_list, Allow_user_setting_DYN,File_sep,0,0,0,0,0,0);
173
174		found = 0;
175		for( i = 0; !found && i < user_list.count; ++i ){
176			str = user_list.list[i];
177			DEBUG2("lpc: checking '%s'", str );
178			uid = strtol( str, &t, 10 );
179			if( str == t || *t ){
180				/* try getpasswd */
181				pw = getpwnam( str );
182				if( pw ){
183					uid = pw->pw_uid;
184				}
185			}
186			DEBUG2( "lpc: uid '%d'", uid );
187			found = ( uid == OriginalRUID );
188			DEBUG2( "lpc: found '%d'", found );
189		}
190		if( !found ){
191			DEBUG1( "%s", "-U (username) can only be used by ROOT" );
192			Username_JOB = 0;
193		}
194	}
195	if( Username_JOB ){
196		Set_DYN(&Logname_DYN, Username_JOB);
197	}
198
199	if( Optind < argc ){
200		for( i = Optind; argv[i]; ++i ){
201			Add_line_list(&args,argv[i],0,0,0);
202		}
203		Check_max(&args,2);
204		args.list[args.count] = 0;
205		doaction( &args );
206	} else while(1){
207		FPRINTF( STDOUT, "lpc>" );
208		if( fgets( msg, sizeof(msg), stdin ) == 0 ) break;
209		if( (s = safestrchr( msg, '\n' )) ) *s = 0;
210		DEBUG1("lpc: '%s'", msg );
211		Free_line_list(&args);
212		Split(&args,msg,Whitespace,0,0,0,0,0,0);
213		Check_max(&args,2);
214		args.list[args.count] = 0;
215		if(DEBUGL1)Dump_line_list("lpc - args", &args );
216		if( args.count == 0 ) continue;
217		s = args.list[0];
218		if(
219			safestrcasecmp(s,"exit") == 0 || safestrcasecmp(s,_("exit")) == 0
220			|| s[0] == 'q' || s[0] == 'Q' ){
221			break;
222		}
223		doaction(&args);
224	}
225	Free_line_list(&args);
226	Errorcode = 0;
227	Is_server = 0;
228	cleanup(0);
229	return(0);
230}
231
232void doaction( struct line_list *args )
233{
234	int action, fd, n, argspos, pcinfo_header;
235	struct line_list l;
236	char msg[SMALLBUFFER];
237	char *s, *t, *w, *printcap;
238
239	Init_line_list(&l);
240	s = t = w = printcap = 0;
241	pcinfo_header = 0;
242	if( args->count == 0 ) return;
243	action = Get_controlword( args->list[0] );
244	if(DEBUGL1)Dump_line_list("doaction - args", args );
245	if( action == 0 ){
246		use_msg();
247		return;
248	}
249	if( args->count > 1 ){
250		Set_DYN(&Printer_DYN,args->list[1]);
251		Fix_Rm_Rp_info(0,0);
252		DEBUG1("doaction: Printer '%s', RemotePrinter '%s', RemoteHost '%s'",
253			Printer_DYN, RemotePrinter_DYN, RemoteHost_DYN );
254		if( (s = safestrchr(args->list[1],'@')) ) *s = 0;
255	} else if( Printer_DYN == 0 ){
256		/* get the printer name */
257		Get_printer();
258		Fix_Rm_Rp_info(0,0);
259	} else {
260		Fix_Rm_Rp_info(0,0);
261	}
262	if( ISNULL(RemotePrinter_DYN) ){
263		SNPRINTF( msg, sizeof(msg))
264			_("Printer: %s - cannot get status from device '%s'\n"),
265			Printer_DYN, Lp_device_DYN );
266		if(  Write_fd_str( 1, msg ) < 0 ) cleanup(0);
267		return;
268	}
269	if( Direct_DYN && Lp_device_DYN ){
270		SNPRINTF( msg, sizeof(msg))
271			_("Printer: %s - direct connection to device '%s'\n"),
272			Printer_DYN, Lp_device_DYN );
273		if(  Write_fd_str( 1, msg ) < 0 ) cleanup(0);
274		return;
275	}
276
277	if( Auth ){
278		Set_DYN(&Auth_DYN, getenv("AUTH"));
279	}
280	if( Server ){
281		DEBUG1("doaction: overriding Remotehost with '%s'", Server );
282		Set_DYN(&RemoteHost_DYN, Server );
283	}
284
285	DEBUG1("lpc: RemotePrinter_DYN '%s', RemoteHost_DYN '%s'", RemotePrinter_DYN, RemoteHost_DYN );
286	if( action == OP_DEFAULTS ){
287		Dump_default_parms( 1, ".defaults", Pc_var_list );
288	} else if( action == OP_LANG ){
289		FPRINTF( STDOUT, _("Locale information directory '%s'\n"), LOCALEDIR );
290		if( (s = getenv("LANG")) ){
291			FPRINTF( STDOUT, _("LANG environment variable '%s'\n"), s );
292			t = _("");
293			if( t && *t ){
294				FPRINTF( STDOUT, _("gettext translation information '%s'\n"), t );
295			} else {
296				Write_fd_str(1,_("No translation available\n"));
297			}
298			FPRINTF(STDERR, "Translation of '%s' is '%s'\n","TRANSLATION TEST", _("TRANSLATION TEST"));
299		} else {
300			FPRINTF( STDOUT, "LANG environment variable not set\n" );
301		}
302	} else if( action == OP_CLIENT || action == OP_SERVER ){
303		if( action == OP_SERVER ){
304			Is_server = 1;
305			Setup_configuration();
306			Get_printer();
307		}
308		Dump_default_parms( 1, ".defaults", Pc_var_list );
309		Free_line_list(&l);
310		Merge_line_list(&l,&Config_line_list, 0, 0, 0);
311		Escape_colons( &l );
312		s = Join_line_list_with_sep(&l,"\n :");
313		Expand_percent( &s );
314		if( s ){
315			if( Write_fd_str( 1, ".config\n :" ) < 0 ) cleanup(0);
316			if( Write_fd_str( 1, s ) < 0 ) cleanup(0);
317			Write_fd_str( 1, "\n" );
318			free(s); s = 0;
319		} else {
320			if( Write_fd_str( 1, ".config\n" ) < 0 ) cleanup(0);
321		}
322		Free_line_list(&l);
323
324		if( args->count > 1 ){
325			for( argspos = 1; argspos < args->count; ++ argspos ){
326				if(
327					!safestrcasecmp(args->list[argspos], "all")
328					|| !safestrcasecmp(args->list[argspos], _("all") )
329					){
330					Show_all_printcap_entries();
331				} else {
332					Set_DYN(&Printer_DYN,args->list[argspos]);
333					if( Write_fd_str( 1,_("\n")) < 0 ) cleanup(0);
334					if( Write_fd_str( 1,_("# Printcap Information\n")) < 0 ) cleanup(0);
335					Show_formatted_info();
336				}
337			}
338		} else if( !safestrcasecmp( Printer_DYN, "all" )
339			|| !safestrcasecmp( Printer_DYN, _("all") ) ){
340			Show_all_printcap_entries();
341		} else {
342			if( Write_fd_str( 1,_("\n")) < 0 ) cleanup(0);
343			if( Write_fd_str( 1,_("# Printcap Information\n")) < 0 ) cleanup(0);
344			Show_formatted_info();
345		}
346	} else if( action == OP_LPQ || action == OP_LPRM ){
347		pid_t pid, result;
348		plp_status_t status;
349		if( args->count == 1 && Printer_DYN ){
350			SNPRINTF(msg,sizeof(msg)) "-P%s", Printer_DYN );
351			Add_line_list(args,msg,0,0,0);
352			Check_max(args,1);
353			args->list[args->count] = 0;
354		} else if( args->count > 1 ){
355			s = args->list[1];
356			if( safestrcasecmp(s,"all")
357			  || safestrcasecmp(s,_("all")) ){
358				SNPRINTF(msg,sizeof(msg)) "-P%s", s );
359			} else {
360				strcpy(msg, "-a" );
361			}
362			if( s ) free(s);
363			args->list[1] = safestrdup(msg,__FILE__,__LINE__);
364		}
365		if(DEBUGL1)Dump_line_list("ARGS",args);
366		if( (pid = dofork(0)) == 0 ){
367			/* we are going to close a security loophole */
368			Full_user_perms();
369			/* this would now be the same as executing LPQ as user */
370			close_on_exec(3);
371			execvp( args->list[0],args->list );
372			DIEMSG( _("execvp failed - '%s'"), Errormsg(errno) );
373			exit(0);
374		} else if( pid < 0 ) {
375			DIEMSG( _("fork failed - '%s'"), Errormsg(errno) );
376		}
377		while( (result = plp_waitpid(pid,&status,0)) != pid ){
378			int err = errno;
379			DEBUG1("lpc: waitpid(%d) returned %d, err '%s'",
380				pid, result, Errormsg(err) );
381			if( err == EINTR ) continue;
382			Errorcode = JABORT;
383			LOGERR_DIE(LOG_ERR) _("doaction: waitpid(%d) failed"), pid);
384		}
385		DEBUG1("lpc: system pid %d, exit status %s",
386			result, Decode_status( &status ) );
387	} else {
388		Add_line_list(&l, Logname_DYN, Value_sep, 0, 0 );
389		Add_line_list(&l, args->list[0], Value_sep, 0, 0);
390		Remove_line_list(args, 0);
391		if( args->count > 0 ) {
392			Add_line_list(&l, RemotePrinter_DYN, Value_sep, 0, 0 );
393			Remove_line_list(args, 0);
394		}
395		Merge_line_list(&l, args, 0, 0, 0 );
396		Check_max(&l, 1 );
397		l.list[l.count] = 0;
398		fd = Send_request( 'C', REQ_CONTROL, l.list, Connect_timeout_DYN,
399			Send_query_rw_timeout_DYN, 1 );
400		if( fd > 0 ){
401			shutdown( fd, 1 );
402			while( (n = read(fd, msg, sizeof(msg))) > 0 ){
403				if( (write(1,msg,n)) < 0 ) cleanup(0);
404			}
405		}
406		close(fd);
407	}
408	Free_line_list(&l);
409}
410
411/***************************************************************************
412 * void Get_parms(int argc, char *argv[])
413 * 1. Scan the argument list and get the flags
414 * 2. Check for duplicate information
415 ***************************************************************************/
416
417
418 char LPC_optstr[] 	/* LPC options */
419 = "AaD:P:S:VU:";
420
421/* scan the input arguments, setting up values */
422
423void Get_parms(int argc, char *argv[] )
424{
425	int option;
426
427	while ((option = Getopt (argc, argv, LPC_optstr )) != EOF) {
428		switch (option) {
429		case 'A': Auth = 1; break;
430		case 'a': Set_DYN(&Printer_DYN,"all"); break;
431		case 'D': /* debug has already been done */
432			Parse_debug( Optarg, 1 );
433			break;
434		case 'P': if( Optarg == 0 ) usage();
435			Set_DYN(&Printer_DYN,Optarg); break;
436		case 'V':
437			++Verbose;
438			break;
439		case 'S':
440			Server = Optarg;
441			break;
442		case 'U': Username_JOB = Optarg; break;
443		default:
444			usage();
445		}
446	}
447	if( Verbose ) {
448		FPRINTF( STDOUT, "%s\n", Version );
449		if( Verbose > 1 ){
450			char *s, *t;
451			if( (s = getenv("LANG")) ){
452				FPRINTF( STDOUT, _("LANG environment variable '%s'\n"), s );
453				t = _("");
454				if( t && *t ){
455					FPRINTF( STDOUT, _("gettext translation information '%s'\n"), t );
456				} else {
457					FPRINTF( STDOUT, "%s", _("No translation available\n"));
458				}
459			} else {
460				FPRINTF( STDOUT, "LANG environment variable not set\n" );
461			}
462			Printlist( Copyright, 2 );
463		}
464	}
465}
466
467 char *msg[] ={
468 N_("usage: %s [-a][-Ddebuglevel][-Pprinter][-Shost][-Uusername][-V] [command]\n"),
469 N_(" with no command, reads from STDIN\n"),
470 N_("  -a           - alias for -Pall\n"),
471 N_("  -Ddebuglevel - debug level\n"),
472 N_("  -Pprinter    - printer\n"),
473 N_("  -Pprinter@host - printer on lpd server on host\n"),
474 N_("  -Shost       - connect to lpd server on host\n"),
475 N_("  -Uuser       - identify command as coming from user\n"),
476 N_("  -V           - increase information verbosity\n"),
477 N_(" commands:\n"),
478 N_(" active    (printer[@host])        - check for active server\n"),
479 N_(" abort     (printer[@host] | all)  - stop server\n"),
480 N_(" class     printer[@host] (class | off)      - show/set class printing\n"),
481 N_(" disable   (printer[@host] | all)  - disable queueing\n"),
482 N_(" debug     (printer[@host] | all) debugparms - set debug level for printer\n"),
483 N_(" down      (printer[@host] | all)  - disable printing and queueing\n"),
484 N_(" enable    (printer[@host] | all)  - enable queueing\n"),
485 N_(" flush     (printer[@host] | all)  - flush cached status\n"),
486 N_(" hold      (printer[@host] | all) (name[@host] | job | all)*   - hold job\n"),
487 N_(" holdall   (printer[@host] | all)  - hold all jobs on\n"),
488 N_(" kill      (printer[@host] | all)  - stop and restart server\n"),
489 N_(" lpd       (printer[@host])        - get LPD PID \n"),
490 N_(" lpq       (printer[@host] | all) (name[@host] | job | all)*   - invoke LPQ\n"),
491 N_(" lprm      (printer[@host] | all) (name[@host]|host|job| all)* - invoke LPRM\n"),
492 N_(" msg       printer message text  - set status message\n"),
493 N_(" move      printer (user|jobid)* target - move jobs to new queue\n"),
494 N_(" noholdall (printer[@host] | all)  - hold all jobs off\n"),
495 N_(" printcap  (printer[@host] | all)  - report printcap values\n"),
496 N_(" quit                              - exit LPC\n"),
497 N_(" redirect  (printer[@host] | all) (printer@host | off )*       - redirect jobs\n"),
498 N_(" redo      (printer[@host] | all) (name[@host] | job | all)*   - reprint jobs\n"),
499 N_(" release   (printer[@host] | all) (name[@host] | job | all)*   - release jobs\n"),
500 N_(" reread                            - LPD reread database information\n"),
501 N_(" start     (printer[@host] | all)  - start printing\n"),
502 N_(" status    (printer[@host] | all)  - status of printers\n"),
503 N_(" stop      (printer[@host] | all)  - stop  printing\n"),
504 N_(" topq      (printer[@host] | all) (name[@host] | job | all)*   - reorder jobs\n"),
505 N_(" up        (printer[@host] | all) - enable printing and queueing\n"),
506 N_("   diagnostic:\n"),
507 N_("      defaultq               - show default queue for LPD server\n"),
508 N_("      defaults               - show default configuration values\n"),
509 N_("      lang                   - show current i18n (iNTERNATIONALIZATIONn) support\n"),
510 N_("      client (printer | all) - client config and printcap information\n"),
511 N_("      server (printer | all) - server config and printcap\n"),
512	0} ;
513
514void use_msg(void)
515{
516	int i;
517	char *s;
518	for( i = 0; (s = msg[i]); ++i ){
519		if( i == 0 ){
520			FPRINTF( STDERR, _(s), Name );
521		} else {
522			FPRINTF( STDERR, "%s", _(s) );
523		}
524	}
525}
526void usage(void)
527{
528	use_msg();
529	Parse_debug("=",-1);
530	FPRINTF( STDOUT, "%s\n", Version );
531	exit(1);
532}
533