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 void shutdown_messages(); 71 72 int am_in_background = 0; 73 74 static void disable_signals(); 75 static void startup(); 76 static void process(); 77 static void ticktock(int); 78 static void background(); 79 static void usage(); 80 static void Exit(); 81 static void disable_signals(); 82 83 /** 84 ** main() 85 **/ 86 87 int 88 main(int argc, char *argv[]) 89 { 90 int c; 91 extern char *optarg; 92 extern int optopt; 93 extern int opterr; 94 char * cp; 95 struct rlimit rlim; 96 int fd_limit = 4096; 97 98 (void) setlocale(LC_ALL, ""); 99 if ((cp = strrchr(argv[0], '/')) == NULL) 100 cp = argv[0]; 101 else 102 cp++; 103 104 /* open the syslog() */ 105 openlog(cp, LOG_PID|LOG_NDELAY|LOG_NOWAIT, LOG_LPR); 106 107 SHELL = DEFAULT_SHELL; 108 LP_TRAY_UNMOUNT = getenv("LP_TRAY_UNMOUNT"); 109 LP_KILL_NO_PAPER = getenv("LP_KILL_NO_PAPER"); 110 LP_ALL_NEW = getenv("LP_ALL_NEW"); 111 112 opterr = 0; 113 while((c = getopt(argc, (char * const *)argv, "D:dsf:n:r:M:p:")) != EOF) 114 switch(c) 115 { 116 # if defined (DEBUG) 117 case 'd': 118 debug = DB_ALL; 119 syslog(LOG_DEBUG, "debug = DB_ALL\n"); 120 goto SkipD; 121 case 'D': 122 if (*optarg == '?') { 123 note ( 124 "-D flag[,flag...] (all logs \"foo\" are in /var/lp/logs,\n" 125 " although \"lpsched\" goes to stdout if SDB)\n" 126 "\n" 127 " EXEC (log all exec's in \"exec\")\n" 128 " DONE (log just exec finishes in \"exec\")\n" 129 " INIT (log initialization info in \"lpsched\" or stdout)\n" 130 " ABORT (issue abort(2) on fatal error)\n" 131 " SCHEDLOG (log additional debugging info in \"lpsched\")\n" 132 " SDB (don't start lpsched as background process)\n" 133 " MESSAGES (log all message traffic in \"messages\")\n" 134 ); 135 note ("\ 136 ALL (all of the above; equivalent to -d)\n" 137 ); 138 exit (0); 139 } 140 while ((cp = strtok(optarg, ", "))) { 141 #define IFSETDB(P,S,F) if (STREQU(P, S)) debug |= F 142 IFSETDB (cp, "EXEC", DB_EXEC); 143 else IFSETDB (cp, "DONE", DB_DONE); 144 else IFSETDB (cp, "INIT", DB_INIT); 145 else IFSETDB (cp, "ABORT", DB_ABORT); 146 else IFSETDB (cp, "SCHEDLOG", DB_SCHEDLOG); 147 else IFSETDB (cp, "SDB", DB_SDB); 148 else IFSETDB (cp, "MESSAGES", DB_MESSAGES); 149 else IFSETDB (cp, "ALL", DB_ALL); 150 else { 151 note ("-D flag not recognized; try -D?\n"); 152 exit (1); 153 } 154 optarg = 0; 155 } 156 syslog(LOG_DEBUG, "-D set\n"); 157 SkipD: 158 break; 159 160 case 's': 161 signals++; 162 break; 163 # endif /* DEBUG */ 164 165 case 'f': 166 if ((ET_SlowSize = atoi(optarg)) < 1) 167 ET_SlowSize = 1; 168 syslog(LOG_DEBUG, "-f option is %d\n", ET_SlowSize); 169 break; 170 171 case 'n': 172 if ((ET_NotifySize = atoi(optarg)) < 1) 173 ET_NotifySize = 1; 174 syslog(LOG_DEBUG, "-n option is %d\n", ET_NotifySize); 175 break; 176 177 case 'r': 178 if ((Reserve_Fds = atoi(optarg)) < 0) 179 Reserve_Fds = 0; 180 syslog(LOG_DEBUG, "-r option is %d\n", Reserve_Fds); 181 break; 182 183 case 'p': 184 if ((fd_limit = atoi(optarg)) < 16) 185 fd_limit = 4096; 186 syslog(LOG_DEBUG, "-p option is %d\n", fd_limit); 187 break; 188 189 case '?': 190 if (optopt == '?') { 191 usage (); 192 exit (0); 193 } else 194 fail ("%s: illegal option -- %c\n", argv[0], optopt); 195 } 196 197 /* reset the fd resource limit */ 198 rlim.rlim_max = rlim.rlim_cur = fd_limit; 199 setrlimit(RLIMIT_NOFILE, &rlim); 200 getrlimit(RLIMIT_NOFILE, &rlim); 201 (void) enable_extended_FILE_stdio(-1, -1); 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 ("Print services started.\n"); 285 Starting = 0; 286 } 287 288 void 289 lpshut(int immediate) 290 { 291 int i; 292 extern MESG * Net_md; 293 294 295 /* 296 * If this is the first time here, stop all running 297 * child processes, and shut off the alarm clock so 298 * it doesn't bug us. 299 */ 300 if (!Shutdown) { 301 mputm (Net_md, S_SHUTDOWN, 1); 302 for (i = 0; i < ET_Size; i++) 303 terminate (&Exec_Table[i]); 304 alarm (0); 305 Shutdown = (immediate? 2 : 1); 306 } 307 308 /* 309 * If this is an express shutdown, or if all the 310 * child processes have been cleaned up, clean up 311 * and get out. 312 */ 313 if (Shutdown == 2) { 314 315 /* 316 * We don't shut down the message queues until 317 * now, to give the children a chance to answer. 318 * This means an LP command may have been snuck 319 * in while we were waiting for the children to 320 * finish, but that's OK because we'll have 321 * stored the jobs on disk (that's part of the 322 * normal operation, not just during shutdown phase). 323 */ 324 shutdown_messages(); 325 326 (void) close(lock_fd); 327 (void) Unlink(Lp_Schedlock); 328 329 note ("Print services stopped.\n"); 330 exit (0); 331 /*NOTREACHED*/ 332 } 333 } 334 335 static void 336 process() 337 { 338 register FSTATUS *pfs; 339 register PWSTATUS *ppws; 340 341 342 /* 343 * Call the "check_..._alert()" routines for each form/print-wheel; 344 * we need to do this at this point because these routines 345 * short-circuit themselves while we are in startup mode. 346 * Calling them now will kick off any necessary alerts. 347 */ 348 isStartingForms = 1; 349 for (pfs = walk_ftable(1); pfs; pfs = walk_ftable(0)) 350 check_form_alert (pfs, (_FORM *)0); 351 isStartingForms = 0; 352 353 for (ppws = walk_pwtable(1); ppws; ppws = walk_pwtable(0)) 354 check_pwheel_alert (ppws, (PWHEEL *)0); 355 356 /* 357 * Clear the alarm, then schedule an EV_ALARM. This will clear 358 * all events that had been scheduled for later without waiting 359 * for the next tick. 360 */ 361 alarm (0); 362 schedule (EV_ALARM); 363 364 /* 365 * Start the ball rolling. 366 */ 367 schedule (EV_INTERF, (PSTATUS *)0); 368 schedule (EV_NOTIFY, (RSTATUS *)0); 369 schedule (EV_SLOWF, (RSTATUS *)0); 370 371 for (EVER) { 372 take_message (); 373 374 if (Sig_Alrm) 375 schedule (EV_ALARM); 376 377 if (DoneChildren) 378 dowait (); 379 380 if (Shutdown) 381 check_children(); 382 if (Shutdown == 2) 383 break; 384 } 385 } 386 387 /*ARGSUSED*/ 388 static void 389 ticktock(int sig) 390 { 391 Sig_Alrm = 1; 392 (void)signal (SIGALRM, ticktock); 393 return; 394 } 395 396 static void 397 background() 398 { 399 #if defined(DEBUG) 400 if (debug & DB_SDB) 401 return; 402 #endif 403 404 switch(fork()) 405 { 406 case -1: 407 fail ("Failed to fork child process (%s).\n", PERROR); 408 /*NOTREACHED*/ 409 410 case 0: 411 (void) setpgrp(); 412 am_in_background = 1; 413 return; 414 415 default: 416 note ("Print services started.\n"); 417 exit(0); 418 /* NOTREACHED */ 419 } 420 } 421 422 static void 423 usage() 424 { 425 note ("\ 426 usage: lpsched [ options ]\n\ 427 [ -f #filter-slots ] (increase no. concurrent slow filters)\n\ 428 [ -n #notify-slots ] (increase no. concurrent notifications)\n\ 429 [ -r #reserved-fds ] (increase margin of file descriptors)\n" 430 ); 431 432 #if defined(DEBUG) 433 note ("\ 434 [ -D flag[,flag...] ] (debug modes; use -D? for usage info.)\n\ 435 [ -d ] (same as -D ALL)\n\ 436 [ -s ] (don't trap most signals)\n" 437 ); 438 #endif 439 440 note ("\ 441 WARNING: all these options are currently unsupported\n" 442 ); 443 444 return; 445 } 446 447 static void 448 Exit(n) 449 int n; 450 { 451 fail ("Received unexpected signal %d; terminating.\n", n); 452 } 453 454 static void 455 disable_signals() 456 { 457 int i; 458 459 # if defined(DEBUG) 460 if (!signals) 461 # endif 462 for (i = 0; i < NSIG; i++) 463 if (signal(i, SIG_IGN) != SIG_IGN) 464 signal (i, Exit); 465 466 (void) signal(SIGHUP, SIG_IGN); 467 (void) signal(SIGINT, SIG_IGN); 468 (void) signal(SIGQUIT, SIG_IGN); 469 (void) signal(SIGALRM, ticktock); 470 (void) signal(SIGTERM, lpshut); /* needs arg, but sig# OK */ 471 (void) signal(SIGCLD, SIG_IGN); 472 (void) signal(SIGTSTP, SIG_IGN); 473 (void) signal(SIGCONT, SIG_DFL); 474 (void) signal(SIGTTIN, SIG_IGN); 475 (void) signal(SIGTTOU, SIG_IGN); 476 (void) signal(SIGXFSZ, SIG_IGN); /* could be a problem */ 477 (void) signal(SIGWINCH, SIG_IGN); /* if started in a window */ 478 (void) signal(SIGTHAW, SIG_IGN); /* used by CPR - energystar */ 479 480 #if defined(DEBUG) 481 if (debug & DB_ABORT) 482 (void) signal(SIGABRT, SIG_DFL); 483 #endif 484 485 } 486