1 /* 2 * CDDL HEADER START 3 * 4 * The contents of this file are subject to the terms of the 5 * Common Development and Distribution License (the "License"). 6 * You may not use this file except in compliance with the License. 7 * 8 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9 * or http://www.opensolaris.org/os/licensing. 10 * See the License for the specific language governing permissions 11 * and limitations under the License. 12 * 13 * When distributing Covered Code, include this CDDL HEADER in each 14 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15 * If applicable, add the following below this CDDL HEADER, with the 16 * fields enclosed by brackets "[]" replaced with your own identifying 17 * information: Portions Copyright [yyyy] [name of copyright owner] 18 * 19 * CDDL HEADER END 20 */ 21 /* 22 * Copyright 2006 Sun Microsystems, Inc. All rights reserved. 23 * Use is subject to license terms. 24 */ 25 26 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 27 /* All Rights Reserved */ 28 29 #pragma ident "%Z%%M% %I% %E% SMI" 30 31 #include "limits.h" 32 #include "ulimit.h" 33 #include "sys/utsname.h" 34 35 #include "lpsched.h" 36 37 #include <sys/stat.h> 38 #include <sys/time.h> /* to up the max # of fds */ 39 #include <sys/resource.h> 40 #include <syslog.h> 41 #include <locale.h> 42 #include <stdio_ext.h> 43 44 45 int lock_fd = -1; 46 int isStartingForms = 0; 47 int Starting = 0; 48 int Shutdown = 0; 49 int DoneChildren = 0; 50 int Sig_Alrm = 0; 51 int OpenMax = OPEN_MAX; 52 int Reserve_Fds = 0; 53 54 char *Local_System = 0; 55 char *SHELL = 0; 56 57 char *LP_TRAY_UNMOUNT = NULL; 58 char *LP_KILL_NO_PAPER = NULL; 59 char *LP_ALL_NEW = NULL; 60 61 gid_t Lp_Gid; 62 uid_t Lp_Uid; 63 64 #if defined(DEBUG) 65 unsigned long debug = 0; 66 static int signals = 0; 67 #endif 68 69 extern int errno; 70 extern char *lpsched_buildinfo; 71 extern void shutdown_messages(); 72 73 int am_in_background = 0; 74 75 static void disable_signals(); 76 static void startup(); 77 static void process(); 78 static void ticktock(int); 79 static void background(); 80 static void usage(); 81 static void Exit(); 82 static void disable_signals(); 83 84 /** 85 ** main() 86 **/ 87 88 int 89 main(int argc, char *argv[]) 90 { 91 int c; 92 extern char *optarg; 93 extern int optopt; 94 extern int opterr; 95 char * cp; 96 struct rlimit rlim; 97 int fd_limit = 4096; 98 99 (void) setlocale(LC_ALL, ""); 100 if ((cp = strrchr(argv[0], '/')) == NULL) 101 cp = argv[0]; 102 else 103 cp++; 104 105 /* open the syslog() */ 106 openlog(cp, LOG_PID|LOG_NDELAY|LOG_NOWAIT, LOG_LPR); 107 108 SHELL = DEFAULT_SHELL; 109 LP_TRAY_UNMOUNT = getenv("LP_TRAY_UNMOUNT"); 110 LP_KILL_NO_PAPER = getenv("LP_KILL_NO_PAPER"); 111 LP_ALL_NEW = getenv("LP_ALL_NEW"); 112 113 opterr = 0; 114 while((c = getopt(argc, (char * const *)argv, "D:dsf:n:r:M:p:")) != EOF) 115 switch(c) 116 { 117 # if defined (DEBUG) 118 case 'd': 119 debug = DB_ALL; 120 syslog(LOG_DEBUG, "debug = DB_ALL\n"); 121 goto SkipD; 122 case 'D': 123 if (*optarg == '?') { 124 note ( 125 "-D flag[,flag...] (all logs \"foo\" are in /var/lp/logs,\n" 126 " although \"lpsched\" goes to stdout if SDB)\n" 127 "\n" 128 " EXEC (log all exec's in \"exec\")\n" 129 " DONE (log just exec finishes in \"exec\")\n" 130 " INIT (log initialization info in \"lpsched\" or stdout)\n" 131 " ABORT (issue abort(2) on fatal error)\n" 132 " SCHEDLOG (log additional debugging info in \"lpsched\")\n" 133 " SDB (don't start lpsched as background process)\n" 134 " MESSAGES (log all message traffic in \"messages\")\n" 135 ); 136 note ("\ 137 ALL (all of the above; equivalent to -d)\n" 138 ); 139 exit (0); 140 } 141 while ((cp = strtok(optarg, ", "))) { 142 #define IFSETDB(P,S,F) if (STREQU(P, S)) debug |= F 143 IFSETDB (cp, "EXEC", DB_EXEC); 144 else IFSETDB (cp, "DONE", DB_DONE); 145 else IFSETDB (cp, "INIT", DB_INIT); 146 else IFSETDB (cp, "ABORT", DB_ABORT); 147 else IFSETDB (cp, "SCHEDLOG", DB_SCHEDLOG); 148 else IFSETDB (cp, "SDB", DB_SDB); 149 else IFSETDB (cp, "MESSAGES", DB_MESSAGES); 150 else IFSETDB (cp, "ALL", DB_ALL); 151 else { 152 note ("-D flag not recognized; try -D?\n"); 153 exit (1); 154 } 155 optarg = 0; 156 } 157 syslog(LOG_DEBUG, "-D set\n"); 158 SkipD: 159 break; 160 161 case 's': 162 signals++; 163 break; 164 # endif /* DEBUG */ 165 166 case 'f': 167 if ((ET_SlowSize = atoi(optarg)) < 1) 168 ET_SlowSize = 1; 169 syslog(LOG_DEBUG, "-f option is %d\n", ET_SlowSize); 170 break; 171 172 case 'n': 173 if ((ET_NotifySize = atoi(optarg)) < 1) 174 ET_NotifySize = 1; 175 syslog(LOG_DEBUG, "-n option is %d\n", ET_NotifySize); 176 break; 177 178 case 'r': 179 if ((Reserve_Fds = atoi(optarg)) < 0) 180 Reserve_Fds = 0; 181 syslog(LOG_DEBUG, "-r option is %d\n", Reserve_Fds); 182 break; 183 184 case 'p': 185 if ((fd_limit = atoi(optarg)) < 16) 186 fd_limit = 4096; 187 syslog(LOG_DEBUG, "-p option is %d\n", fd_limit); 188 break; 189 190 case '?': 191 if (optopt == '?') { 192 usage (); 193 exit (0); 194 } else 195 fail ("%s: illegal option -- %c\n", argv[0], optopt); 196 } 197 198 /* reset the fd resource limit */ 199 rlim.rlim_max = rlim.rlim_cur = fd_limit; 200 setrlimit(RLIMIT_NOFILE, &rlim); 201 getrlimit(RLIMIT_NOFILE, &rlim); 202 (void) enable_extended_FILE_stdio(-1, -1); 203 syslog(LOG_DEBUG, "file descriptor resource limit is %d (~%d printers)", 204 rlim.rlim_cur, (rlim.rlim_cur - 12)/ 2); 205 206 lp_alloc_fail_handler = mallocfail; 207 208 startup(); 209 210 process(); 211 212 lpshut(1); /* one last time to clean up */ 213 /*NOTREACHED*/ 214 return (0); 215 } 216 217 static void 218 startup() 219 { 220 struct passwd *p; 221 222 223 Starting = 1; 224 getpaths(); 225 226 /* 227 * There must be a user named "lp". 228 */ 229 if ((p = getpwnam(LPUSER)) == NULL) 230 fail ("Can't find the user \"lp\" on this system!\n"); 231 232 Lp_Uid = p->pw_uid; 233 Lp_Gid = p->pw_gid; 234 235 /* 236 * Only "root" is allowed to run us. 237 */ 238 if ((getuid() != 0) && (geteuid() != 0)) 239 fail ("You must be \"root\" to run this program.\n"); 240 241 setuid (0); 242 243 Local_System = Strdup("localhost"); 244 245 /* 246 * Make sure that all critical directories are present and that 247 * symbolic links are correct. 248 */ 249 lpfsck(); 250 251 /* 252 * Try setting the lock file to see if another Spooler is running. 253 * We'll release it immediately; this allows us to fork the child 254 * that will run in the background. The child will relock the file. 255 */ 256 if ((lock_fd = open_locked(Lp_Schedlock, "a", 0664)) < 0) 257 if (errno == EAGAIN) 258 fail ("Print services already active.\n"); 259 else 260 fail ("Can't open file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR); 261 close(lock_fd); 262 263 background(); 264 /* 265 * We are the child process now. 266 */ 267 268 if ((lock_fd = open_locked(Lp_Schedlock, "w", 0664)) < 0) 269 fail ("Failed to lock the file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR); 270 271 Close (0); 272 Close (2); 273 if (am_in_background) 274 Close (1); 275 276 if ((OpenMax = ulimit(4, 0L)) == -1) 277 OpenMax = OPEN_MAX; 278 279 disable_signals(); 280 281 init_messages(); 282 283 init_memory(); 284 285 note (lpsched_buildinfo); 286 note ("Print services started.\n"); 287 Starting = 0; 288 } 289 290 void 291 lpshut(int immediate) 292 { 293 int i; 294 extern MESG * Net_md; 295 296 297 /* 298 * If this is the first time here, stop all running 299 * child processes, and shut off the alarm clock so 300 * it doesn't bug us. 301 */ 302 if (!Shutdown) { 303 mputm (Net_md, S_SHUTDOWN, 1); 304 for (i = 0; i < ET_Size; i++) 305 terminate (&Exec_Table[i]); 306 alarm (0); 307 Shutdown = (immediate? 2 : 1); 308 } 309 310 /* 311 * If this is an express shutdown, or if all the 312 * child processes have been cleaned up, clean up 313 * and get out. 314 */ 315 if (Shutdown == 2) { 316 317 /* 318 * We don't shut down the message queues until 319 * now, to give the children a chance to answer. 320 * This means an LP command may have been snuck 321 * in while we were waiting for the children to 322 * finish, but that's OK because we'll have 323 * stored the jobs on disk (that's part of the 324 * normal operation, not just during shutdown phase). 325 */ 326 shutdown_messages(); 327 328 (void) close(lock_fd); 329 (void) Unlink(Lp_Schedlock); 330 331 note ("Print services stopped.\n"); 332 exit (0); 333 /*NOTREACHED*/ 334 } 335 } 336 337 static void 338 process() 339 { 340 register FSTATUS *pfs; 341 register PWSTATUS *ppws; 342 343 344 /* 345 * Call the "check_..._alert()" routines for each form/print-wheel; 346 * we need to do this at this point because these routines 347 * short-circuit themselves while we are in startup mode. 348 * Calling them now will kick off any necessary alerts. 349 */ 350 isStartingForms = 1; 351 for (pfs = walk_ftable(1); pfs; pfs = walk_ftable(0)) 352 check_form_alert (pfs, (_FORM *)0); 353 isStartingForms = 0; 354 355 for (ppws = walk_pwtable(1); ppws; ppws = walk_pwtable(0)) 356 check_pwheel_alert (ppws, (PWHEEL *)0); 357 358 /* 359 * Clear the alarm, then schedule an EV_ALARM. This will clear 360 * all events that had been scheduled for later without waiting 361 * for the next tick. 362 */ 363 alarm (0); 364 schedule (EV_ALARM); 365 366 /* 367 * Start the ball rolling. 368 */ 369 schedule (EV_INTERF, (PSTATUS *)0); 370 schedule (EV_NOTIFY, (RSTATUS *)0); 371 schedule (EV_SLOWF, (RSTATUS *)0); 372 373 for (EVER) { 374 take_message (); 375 376 if (Sig_Alrm) 377 schedule (EV_ALARM); 378 379 if (DoneChildren) 380 dowait (); 381 382 if (Shutdown) 383 check_children(); 384 if (Shutdown == 2) 385 break; 386 } 387 } 388 389 /*ARGSUSED*/ 390 static void 391 ticktock(int sig) 392 { 393 Sig_Alrm = 1; 394 (void)signal (SIGALRM, ticktock); 395 return; 396 } 397 398 static void 399 background() 400 { 401 #if defined(DEBUG) 402 if (debug & DB_SDB) 403 return; 404 #endif 405 406 switch(fork()) 407 { 408 case -1: 409 fail ("Failed to fork child process (%s).\n", PERROR); 410 /*NOTREACHED*/ 411 412 case 0: 413 (void) setpgrp(); 414 am_in_background = 1; 415 return; 416 417 default: 418 note ("Print services started.\n"); 419 exit(0); 420 /* NOTREACHED */ 421 } 422 } 423 424 static void 425 usage() 426 { 427 note ("\ 428 usage: lpsched [ options ]\n\ 429 [ -f #filter-slots ] (increase no. concurrent slow filters)\n\ 430 [ -n #notify-slots ] (increase no. concurrent notifications)\n\ 431 [ -r #reserved-fds ] (increase margin of file descriptors)\n" 432 ); 433 434 #if defined(DEBUG) 435 note ("\ 436 [ -D flag[,flag...] ] (debug modes; use -D? for usage info.)\n\ 437 [ -d ] (same as -D ALL)\n\ 438 [ -s ] (don't trap most signals)\n" 439 ); 440 #endif 441 442 note ("\ 443 WARNING: all these options are currently unsupported\n" 444 ); 445 446 return; 447 } 448 449 static void 450 Exit(n) 451 int n; 452 { 453 fail ("Received unexpected signal %d; terminating.\n", n); 454 } 455 456 static void 457 disable_signals() 458 { 459 int i; 460 461 # if defined(DEBUG) 462 if (!signals) 463 # endif 464 for (i = 0; i < NSIG; i++) 465 if (signal(i, SIG_IGN) != SIG_IGN) 466 signal (i, Exit); 467 468 (void) signal(SIGHUP, SIG_IGN); 469 (void) signal(SIGINT, SIG_IGN); 470 (void) signal(SIGQUIT, SIG_IGN); 471 (void) signal(SIGALRM, ticktock); 472 (void) signal(SIGTERM, lpshut); /* needs arg, but sig# OK */ 473 (void) signal(SIGCLD, SIG_IGN); 474 (void) signal(SIGTSTP, SIG_IGN); 475 (void) signal(SIGCONT, SIG_DFL); 476 (void) signal(SIGTTIN, SIG_IGN); 477 (void) signal(SIGTTOU, SIG_IGN); 478 (void) signal(SIGXFSZ, SIG_IGN); /* could be a problem */ 479 (void) signal(SIGWINCH, SIG_IGN); /* if started in a window */ 480 (void) signal(SIGTHAW, SIG_IGN); /* used by CPR - energystar */ 481 482 #if defined(DEBUG) 483 if (debug & DB_ABORT) 484 (void) signal(SIGABRT, SIG_DFL); 485 #endif 486 487 } 488