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, Version 1.0 only 6 * (the "License"). You may not use this file except in compliance 7 * with the License. 8 * 9 * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 10 * or http://www.opensolaris.org/os/licensing. 11 * See the License for the specific language governing permissions 12 * and limitations under the License. 13 * 14 * When distributing Covered Code, include this CDDL HEADER in each 15 * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 16 * If applicable, add the following below this CDDL HEADER, with the 17 * fields enclosed by brackets "[]" replaced with your own identifying 18 * information: Portions Copyright [yyyy] [name of copyright owner] 19 * 20 * CDDL HEADER END 21 */ 22 /* 23 * Copyright 2005 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* Copyright (c) 1984, 1986, 1987, 1988, 1989 AT&T */ 28 /* All Rights Reserved */ 29 30 #pragma ident "%Z%%M% %I% %E% SMI" 31 32 #include "limits.h" 33 #include "ulimit.h" 34 #include "sys/utsname.h" 35 36 #include "lpsched.h" 37 38 #include <sys/stat.h> 39 #include <sys/time.h> /* to up the max # of fds */ 40 #include <sys/resource.h> 41 #include <syslog.h> 42 #include <locale.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 syslog(LOG_DEBUG, "file descriptor resource limit is %d (~%d printers)", 203 rlim.rlim_cur, (rlim.rlim_cur - 12)/ 2); 204 205 lp_alloc_fail_handler = mallocfail; 206 207 startup(); 208 209 process(); 210 211 lpshut(1); /* one last time to clean up */ 212 /*NOTREACHED*/ 213 return (0); 214 } 215 216 static void 217 startup() 218 { 219 struct passwd *p; 220 221 222 Starting = 1; 223 getpaths(); 224 225 /* 226 * There must be a user named "lp". 227 */ 228 if ((p = getpwnam(LPUSER)) == NULL) 229 fail ("Can't find the user \"lp\" on this system!\n"); 230 231 Lp_Uid = p->pw_uid; 232 Lp_Gid = p->pw_gid; 233 234 /* 235 * Only "root" is allowed to run us. 236 */ 237 if ((getuid() != 0) && (geteuid() != 0)) 238 fail ("You must be \"root\" to run this program.\n"); 239 240 setuid (0); 241 242 Local_System = Strdup("localhost"); 243 244 /* 245 * Make sure that all critical directories are present and that 246 * symbolic links are correct. 247 */ 248 lpfsck(); 249 250 /* 251 * Try setting the lock file to see if another Spooler is running. 252 * We'll release it immediately; this allows us to fork the child 253 * that will run in the background. The child will relock the file. 254 */ 255 if ((lock_fd = open_locked(Lp_Schedlock, "a", 0664)) < 0) 256 if (errno == EAGAIN) 257 fail ("Print services already active.\n"); 258 else 259 fail ("Can't open file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR); 260 close(lock_fd); 261 262 background(); 263 /* 264 * We are the child process now. 265 */ 266 267 if ((lock_fd = open_locked(Lp_Schedlock, "w", 0664)) < 0) 268 fail ("Failed to lock the file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR); 269 270 Close (0); 271 Close (2); 272 if (am_in_background) 273 Close (1); 274 275 if ((OpenMax = ulimit(4, 0L)) == -1) 276 OpenMax = OPEN_MAX; 277 278 disable_signals(); 279 280 init_messages(); 281 282 init_memory(); 283 284 note (lpsched_buildinfo); 285 note ("Print services started.\n"); 286 Starting = 0; 287 } 288 289 void 290 lpshut(int immediate) 291 { 292 int i; 293 extern MESG * Net_md; 294 295 296 /* 297 * If this is the first time here, stop all running 298 * child processes, and shut off the alarm clock so 299 * it doesn't bug us. 300 */ 301 if (!Shutdown) { 302 mputm (Net_md, S_SHUTDOWN, 1); 303 for (i = 0; i < ET_Size; i++) 304 terminate (&Exec_Table[i]); 305 alarm (0); 306 Shutdown = (immediate? 2 : 1); 307 } 308 309 /* 310 * If this is an express shutdown, or if all the 311 * child processes have been cleaned up, clean up 312 * and get out. 313 */ 314 if (Shutdown == 2) { 315 316 /* 317 * We don't shut down the message queues until 318 * now, to give the children a chance to answer. 319 * This means an LP command may have been snuck 320 * in while we were waiting for the children to 321 * finish, but that's OK because we'll have 322 * stored the jobs on disk (that's part of the 323 * normal operation, not just during shutdown phase). 324 */ 325 shutdown_messages(); 326 327 (void) close(lock_fd); 328 (void) Unlink(Lp_Schedlock); 329 330 note ("Print services stopped.\n"); 331 exit (0); 332 /*NOTREACHED*/ 333 } 334 } 335 336 static void 337 process() 338 { 339 register FSTATUS *pfs; 340 register PWSTATUS *ppws; 341 342 343 /* 344 * Call the "check_..._alert()" routines for each form/print-wheel; 345 * we need to do this at this point because these routines 346 * short-circuit themselves while we are in startup mode. 347 * Calling them now will kick off any necessary alerts. 348 */ 349 isStartingForms = 1; 350 for (pfs = walk_ftable(1); pfs; pfs = walk_ftable(0)) 351 check_form_alert (pfs, (_FORM *)0); 352 isStartingForms = 0; 353 354 for (ppws = walk_pwtable(1); ppws; ppws = walk_pwtable(0)) 355 check_pwheel_alert (ppws, (PWHEEL *)0); 356 357 /* 358 * Clear the alarm, then schedule an EV_ALARM. This will clear 359 * all events that had been scheduled for later without waiting 360 * for the next tick. 361 */ 362 alarm (0); 363 schedule (EV_ALARM); 364 365 /* 366 * Start the ball rolling. 367 */ 368 schedule (EV_INTERF, (PSTATUS *)0); 369 schedule (EV_NOTIFY, (RSTATUS *)0); 370 schedule (EV_SLOWF, (RSTATUS *)0); 371 372 for (EVER) { 373 take_message (); 374 375 if (Sig_Alrm) 376 schedule (EV_ALARM); 377 378 if (DoneChildren) 379 dowait (); 380 381 if (Shutdown) 382 check_children(); 383 if (Shutdown == 2) 384 break; 385 } 386 } 387 388 /*ARGSUSED*/ 389 static void 390 ticktock(int sig) 391 { 392 Sig_Alrm = 1; 393 (void)signal (SIGALRM, ticktock); 394 return; 395 } 396 397 static void 398 background() 399 { 400 #if defined(DEBUG) 401 if (debug & DB_SDB) 402 return; 403 #endif 404 405 switch(fork()) 406 { 407 case -1: 408 fail ("Failed to fork child process (%s).\n", PERROR); 409 /*NOTREACHED*/ 410 411 case 0: 412 (void) setpgrp(); 413 am_in_background = 1; 414 return; 415 416 default: 417 note ("Print services started.\n"); 418 exit(0); 419 /* NOTREACHED */ 420 } 421 } 422 423 static void 424 usage() 425 { 426 note ("\ 427 usage: lpsched [ options ]\n\ 428 [ -f #filter-slots ] (increase no. concurrent slow filters)\n\ 429 [ -n #notify-slots ] (increase no. concurrent notifications)\n\ 430 [ -r #reserved-fds ] (increase margin of file descriptors)\n" 431 ); 432 433 #if defined(DEBUG) 434 note ("\ 435 [ -D flag[,flag...] ] (debug modes; use -D? for usage info.)\n\ 436 [ -d ] (same as -D ALL)\n\ 437 [ -s ] (don't trap most signals)\n" 438 ); 439 #endif 440 441 note ("\ 442 WARNING: all these options are currently unsupported\n" 443 ); 444 445 return; 446 } 447 448 static void 449 Exit(n) 450 int n; 451 { 452 fail ("Received unexpected signal %d; terminating.\n", n); 453 } 454 455 static void 456 disable_signals() 457 { 458 int i; 459 460 # if defined(DEBUG) 461 if (!signals) 462 # endif 463 for (i = 0; i < NSIG; i++) 464 if (signal(i, SIG_IGN) != SIG_IGN) 465 signal (i, Exit); 466 467 (void) signal(SIGHUP, SIG_IGN); 468 (void) signal(SIGINT, SIG_IGN); 469 (void) signal(SIGQUIT, SIG_IGN); 470 (void) signal(SIGALRM, ticktock); 471 (void) signal(SIGTERM, lpshut); /* needs arg, but sig# OK */ 472 (void) signal(SIGCLD, SIG_IGN); 473 (void) signal(SIGTSTP, SIG_IGN); 474 (void) signal(SIGCONT, SIG_DFL); 475 (void) signal(SIGTTIN, SIG_IGN); 476 (void) signal(SIGTTOU, SIG_IGN); 477 (void) signal(SIGXFSZ, SIG_IGN); /* could be a problem */ 478 (void) signal(SIGWINCH, SIG_IGN); /* if started in a window */ 479 (void) signal(SIGTHAW, SIG_IGN); /* used by CPR - energystar */ 480 481 #if defined(DEBUG) 482 if (debug & DB_ABORT) 483 (void) signal(SIGABRT, SIG_DFL); 484 #endif 485 486 } 487