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