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 struct utsname utsbuf; 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 uname(&utsbuf); 244 if (utsbuf.nodename[0] != '\0') 245 Local_System = Strdup(utsbuf.nodename); 246 else 247 Local_System = Strdup("localhost"); 248 249 /* 250 * Make sure that all critical directories are present and that 251 * symbolic links are correct. 252 */ 253 lpfsck(); 254 255 /* 256 * Try setting the lock file to see if another Spooler is running. 257 * We'll release it immediately; this allows us to fork the child 258 * that will run in the background. The child will relock the file. 259 */ 260 if ((lock_fd = open_locked(Lp_Schedlock, "a", 0664)) < 0) 261 if (errno == EAGAIN) 262 fail ("Print services already active.\n"); 263 else 264 fail ("Can't open file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR); 265 close(lock_fd); 266 267 background(); 268 /* 269 * We are the child process now. 270 */ 271 272 if ((lock_fd = open_locked(Lp_Schedlock, "w", 0664)) < 0) 273 fail ("Failed to lock the file \"%s\" (%s).\n", NB(Lp_Schedlock), PERROR); 274 275 Close (0); 276 Close (2); 277 if (am_in_background) 278 Close (1); 279 280 if ((OpenMax = ulimit(4, 0L)) == -1) 281 OpenMax = OPEN_MAX; 282 283 disable_signals(); 284 285 init_messages(); 286 287 init_memory(); 288 289 note (lpsched_buildinfo); 290 note ("Print services started.\n"); 291 Starting = 0; 292 } 293 294 void 295 lpshut(int immediate) 296 { 297 int i; 298 extern MESG * Net_md; 299 300 301 /* 302 * If this is the first time here, stop all running 303 * child processes, and shut off the alarm clock so 304 * it doesn't bug us. 305 */ 306 if (!Shutdown) { 307 mputm (Net_md, S_SHUTDOWN, 1); 308 for (i = 0; i < ET_Size; i++) 309 terminate (&Exec_Table[i]); 310 alarm (0); 311 Shutdown = (immediate? 2 : 1); 312 } 313 314 /* 315 * If this is an express shutdown, or if all the 316 * child processes have been cleaned up, clean up 317 * and get out. 318 */ 319 if (Shutdown == 2) { 320 321 /* 322 * We don't shut down the message queues until 323 * now, to give the children a chance to answer. 324 * This means an LP command may have been snuck 325 * in while we were waiting for the children to 326 * finish, but that's OK because we'll have 327 * stored the jobs on disk (that's part of the 328 * normal operation, not just during shutdown phase). 329 */ 330 shutdown_messages(); 331 332 (void) close(lock_fd); 333 (void) Unlink(Lp_Schedlock); 334 335 note ("Print services stopped.\n"); 336 exit (0); 337 /*NOTREACHED*/ 338 } 339 } 340 341 static void 342 process() 343 { 344 register FSTATUS *pfs; 345 register PWSTATUS *ppws; 346 347 348 /* 349 * Call the "check_..._alert()" routines for each form/print-wheel; 350 * we need to do this at this point because these routines 351 * short-circuit themselves while we are in startup mode. 352 * Calling them now will kick off any necessary alerts. 353 */ 354 isStartingForms = 1; 355 for (pfs = walk_ftable(1); pfs; pfs = walk_ftable(0)) 356 check_form_alert (pfs, (_FORM *)0); 357 isStartingForms = 0; 358 359 for (ppws = walk_pwtable(1); ppws; ppws = walk_pwtable(0)) 360 check_pwheel_alert (ppws, (PWHEEL *)0); 361 362 /* 363 * Clear the alarm, then schedule an EV_ALARM. This will clear 364 * all events that had been scheduled for later without waiting 365 * for the next tick. 366 */ 367 alarm (0); 368 schedule (EV_ALARM); 369 370 /* 371 * Start the ball rolling. 372 */ 373 schedule (EV_INTERF, (PSTATUS *)0); 374 schedule (EV_NOTIFY, (RSTATUS *)0); 375 schedule (EV_SLOWF, (RSTATUS *)0); 376 377 for (EVER) { 378 take_message (); 379 380 if (Sig_Alrm) 381 schedule (EV_ALARM); 382 383 if (DoneChildren) 384 dowait (); 385 386 if (Shutdown) 387 check_children(); 388 if (Shutdown == 2) 389 break; 390 } 391 } 392 393 /*ARGSUSED*/ 394 static void 395 ticktock(int sig) 396 { 397 Sig_Alrm = 1; 398 (void)signal (SIGALRM, ticktock); 399 return; 400 } 401 402 static void 403 background() 404 { 405 #if defined(DEBUG) 406 if (debug & DB_SDB) 407 return; 408 #endif 409 410 switch(fork()) 411 { 412 case -1: 413 fail ("Failed to fork child process (%s).\n", PERROR); 414 /*NOTREACHED*/ 415 416 case 0: 417 (void) setpgrp(); 418 am_in_background = 1; 419 return; 420 421 default: 422 note ("Print services started.\n"); 423 exit(0); 424 /* NOTREACHED */ 425 } 426 } 427 428 static void 429 usage() 430 { 431 note ("\ 432 usage: lpsched [ options ]\n\ 433 [ -f #filter-slots ] (increase no. concurrent slow filters)\n\ 434 [ -n #notify-slots ] (increase no. concurrent notifications)\n\ 435 [ -r #reserved-fds ] (increase margin of file descriptors)\n" 436 ); 437 438 #if defined(DEBUG) 439 note ("\ 440 [ -D flag[,flag...] ] (debug modes; use -D? for usage info.)\n\ 441 [ -d ] (same as -D ALL)\n\ 442 [ -s ] (don't trap most signals)\n" 443 ); 444 #endif 445 446 note ("\ 447 WARNING: all these options are currently unsupported\n" 448 ); 449 450 return; 451 } 452 453 static void 454 Exit(n) 455 int n; 456 { 457 fail ("Received unexpected signal %d; terminating.\n", n); 458 } 459 460 static void 461 disable_signals() 462 { 463 int i; 464 465 # if defined(DEBUG) 466 if (!signals) 467 # endif 468 for (i = 0; i < NSIG; i++) 469 if (signal(i, SIG_IGN) != SIG_IGN) 470 signal (i, Exit); 471 472 (void) signal(SIGHUP, SIG_IGN); 473 (void) signal(SIGINT, SIG_IGN); 474 (void) signal(SIGQUIT, SIG_IGN); 475 (void) signal(SIGALRM, ticktock); 476 (void) signal(SIGTERM, lpshut); /* needs arg, but sig# OK */ 477 (void) signal(SIGCLD, SIG_IGN); 478 (void) signal(SIGTSTP, SIG_IGN); 479 (void) signal(SIGCONT, SIG_DFL); 480 (void) signal(SIGTTIN, SIG_IGN); 481 (void) signal(SIGTTOU, SIG_IGN); 482 (void) signal(SIGXFSZ, SIG_IGN); /* could be a problem */ 483 (void) signal(SIGWINCH, SIG_IGN); /* if started in a window */ 484 (void) signal(SIGTHAW, SIG_IGN); /* used by CPR - energystar */ 485 486 #if defined(DEBUG) 487 if (debug & DB_ABORT) 488 (void) signal(SIGABRT, SIG_DFL); 489 #endif 490 491 } 492