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 (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved. 23 */ 24 25 #include <sys/types.h> 26 #include <sys/stat.h> 27 #include <sys/wait.h> 28 #include <sys/corectl.h> 29 #include <sys/resource.h> 30 31 #include <priv_utils.h> 32 #include <signal.h> 33 #include <unistd.h> 34 #include <limits.h> 35 #include <fcntl.h> 36 #include <strings.h> 37 #include <stdlib.h> 38 #include <stdio.h> 39 #include <zone.h> 40 41 #include <fmd_error.h> 42 #include <fmd_string.h> 43 #include <fmd_conf.h> 44 #include <fmd_dispq.h> 45 #include <fmd_subr.h> 46 #include <fmd.h> 47 48 fmd_t fmd; 49 mutex_t _svcstate_lock = ERRORCHECKMUTEX; 50 51 /* 52 * For DEBUG builds, we define a set of hooks for libumem that provide useful 53 * default settings for the allocator's debugging facilities. 54 */ 55 #ifdef DEBUG 56 const char * 57 _umem_debug_init() 58 { 59 return ("default,verbose"); /* $UMEM_DEBUG setting */ 60 } 61 62 const char * 63 _umem_logging_init(void) 64 { 65 return ("fail,contents"); /* $UMEM_LOGGING setting */ 66 } 67 #endif /* DEBUG */ 68 69 /* 70 * We use a two-phase algorithm for becoming a daemon because we want the 71 * daemon process (the child) to do the work of becoming MT-hot and opening our 72 * event transport. Since these operations can fail and need to result in the 73 * daemon failing to start, the parent must wait until fmd_run() completes to 74 * know whether it can return zero or non-zero status to the invoking command. 75 * The parent waits on a pipe inside this function to read the exit status. 76 * The child gets the write-end of the pipe returned by daemonize_init() and 77 * then fmd_run() uses the pipe to set the exit status and detach the parent. 78 */ 79 static int 80 daemonize_init(void) 81 { 82 const char *gzp1, *gzp2, *gzp3, *gzp4, *gzp5; 83 int status, pfds[2]; 84 sigset_t set, oset; 85 struct rlimit rlim; 86 char path[PATH_MAX]; 87 pid_t pid; 88 89 /* 90 * Set our per-process core file path to leave core files in our 91 * var/fm/fmd directory, named after the PID to aid in debugging, 92 * and make sure that there is no restriction on core file size. 93 */ 94 (void) snprintf(path, sizeof (path), 95 "%s/var/fm/fmd/core.%s.%%p", fmd.d_rootdir, fmd.d_pname); 96 97 (void) core_set_process_path(path, strlen(path) + 1, fmd.d_pid); 98 99 rlim.rlim_cur = RLIM_INFINITY; 100 rlim.rlim_max = RLIM_INFINITY; 101 102 (void) setrlimit(RLIMIT_CORE, &rlim); 103 104 /* 105 * Claim all the file descriptors we can. 106 */ 107 if (getrlimit(RLIMIT_NOFILE, &rlim) == 0) { 108 rlim.rlim_cur = rlim.rlim_max; 109 (void) setrlimit(RLIMIT_NOFILE, &rlim); 110 } 111 112 /* 113 * Reset all of our privilege sets to the minimum set of required 114 * privileges. We continue to run as root so that files we create 115 * such as logs and checkpoints are secured in the /var filesystem. 116 * 117 * In a non-global zone some of the privileges we retain in a 118 * global zone are only optionally assigned to the zone, while others 119 * are prohibited: 120 * 121 * PRIV_PROC_PRIOCNTL (optional in a non-global zone): 122 * There are no calls to priocntl(2) in fmd or plugins. 123 * 124 * PRIV_SYS_CONFIG (prohibited in a non-global zone): 125 * Required, I think, for sysevent_post_event and/or 126 * other legacy sysevent activity. Legacy sysevent is not 127 * supported in a non-global zone. 128 * 129 * PRIV_SYS_DEVICES (prohibited in a non-global zone): 130 * Needed in the global zone for ioctls on various drivers 131 * such as memory-controller drivers. 132 * 133 * PRIV_SYS_RES_CONFIG (prohibited in a non-global zone): 134 * Require for p_online(2) calls to offline cpus. 135 * 136 * PRIV_SYS_NET_CONFIG (prohibited in a non-global zone): 137 * Required for ipsec in etm (which also requires 138 * PRIV_NET_PRIVADDR). 139 * 140 * We do without those privileges in a non-global zone. It's 141 * possible that there are other privs we could drop since 142 * hardware-related plugins are not present. 143 */ 144 if (getzoneid() == GLOBAL_ZONEID) { 145 gzp1 = PRIV_PROC_PRIOCNTL; 146 gzp2 = PRIV_SYS_CONFIG; 147 gzp3 = PRIV_SYS_DEVICES; 148 gzp4 = PRIV_SYS_RES_CONFIG; 149 gzp5 = PRIV_SYS_NET_CONFIG; 150 } else { 151 gzp1 = gzp2 = gzp3 = gzp4 = gzp5 = NULL; 152 } 153 154 if (__init_daemon_priv(PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS, 155 0, 0, /* run as uid 0 and gid 0 */ 156 PRIV_FILE_DAC_EXECUTE, PRIV_FILE_DAC_READ, PRIV_FILE_DAC_SEARCH, 157 PRIV_FILE_DAC_WRITE, PRIV_FILE_OWNER, PRIV_PROC_OWNER, 158 PRIV_SYS_ADMIN, PRIV_NET_PRIVADDR, 159 gzp1, gzp2, gzp3, gzp4, gzp5, NULL) != 0) 160 fmd_error(EFMD_EXIT, "additional privileges required to run\n"); 161 162 /* 163 * Block all signals prior to the fork and leave them blocked in the 164 * parent so we don't get in a situation where the parent gets SIGINT 165 * and returns non-zero exit status and the child is actually running. 166 * In the child, restore the signal mask once we've done our setsid(). 167 */ 168 (void) sigfillset(&set); 169 (void) sigdelset(&set, SIGABRT); 170 (void) sigprocmask(SIG_BLOCK, &set, &oset); 171 172 if (pipe(pfds) == -1) 173 fmd_error(EFMD_EXIT, "failed to create pipe for daemonize"); 174 175 if ((pid = fork()) == -1) 176 fmd_error(EFMD_EXIT, "failed to fork into background"); 177 178 /* 179 * If we're the parent process, wait for either the child to send us 180 * the appropriate exit status over the pipe or for the read to fail 181 * (presumably with 0 for EOF if our child terminated abnormally). 182 * If the read fails, exit with either the child's exit status if it 183 * exited or with FMD_EXIT_ERROR if it died from a fatal signal. 184 */ 185 if (pid != 0) { 186 (void) close(pfds[1]); 187 188 if (read(pfds[0], &status, sizeof (status)) == sizeof (status)) 189 _exit(status); 190 191 if (waitpid(pid, &status, 0) == pid && WIFEXITED(status)) 192 _exit(WEXITSTATUS(status)); 193 194 _exit(FMD_EXIT_ERROR); 195 } 196 197 fmd.d_pid = getpid(); 198 (void) setsid(); 199 (void) sigprocmask(SIG_SETMASK, &oset, NULL); 200 (void) chdir("/"); 201 (void) umask(022); 202 (void) close(pfds[0]); 203 204 return (pfds[1]); 205 } 206 207 static void 208 daemonize_fini(int fd) 209 { 210 (void) close(fd); 211 212 if ((fd = open("/dev/null", O_RDWR)) >= 0) { 213 (void) fcntl(fd, F_DUP2FD, STDIN_FILENO); 214 (void) fcntl(fd, F_DUP2FD, STDOUT_FILENO); 215 (void) fcntl(fd, F_DUP2FD, STDERR_FILENO); 216 (void) close(fd); 217 } 218 } 219 220 static void 221 handler(int sig) 222 { 223 if (fmd.d_signal == 0) 224 fmd.d_signal = sig; 225 } 226 227 static int 228 usage(const char *arg0, FILE *fp) 229 { 230 (void) fprintf(fp, 231 "Usage: %s [-V] [-f file] [-o opt=val] [-R dir]\n", arg0); 232 233 return (FMD_EXIT_USAGE); 234 } 235 236 int 237 main(int argc, char *argv[]) 238 { 239 const char *opt_f = NULL, *opt_R = NULL; 240 const char optstr[] = "f:o:R:V"; 241 int c, pfd = -1, opt_V = 0; 242 char *p; 243 244 struct sigaction act; 245 sigset_t set; 246 247 /* 248 * Parse the command-line once to validate all options and retrieve 249 * any overrides for our configuration file and root directory. 250 */ 251 while ((c = getopt(argc, argv, optstr)) != EOF) { 252 switch (c) { 253 case 'f': 254 opt_f = optarg; 255 break; 256 case 'o': 257 break; /* handle -o below */ 258 case 'R': 259 opt_R = optarg; 260 break; 261 case 'V': 262 opt_V++; 263 break; 264 default: 265 return (usage(argv[0], stderr)); 266 } 267 } 268 269 if (optind < argc) 270 return (usage(argv[0], stderr)); 271 272 if (opt_V) { 273 #ifdef DEBUG 274 const char *debug = " (DEBUG)"; 275 #else 276 const char *debug = ""; 277 #endif 278 (void) printf("%s: version %s%s\n", 279 argv[0], _fmd_version, debug); 280 return (FMD_EXIT_SUCCESS); 281 } 282 283 closefrom(STDERR_FILENO + 1); 284 fmd_create(&fmd, argv[0], opt_R, opt_f); 285 286 /* 287 * Now that we've initialized our global state, parse the command-line 288 * again for any configuration options specified using -o and set them. 289 */ 290 for (optind = 1; (c = getopt(argc, argv, optstr)) != EOF; ) { 291 if (c == 'o') { 292 if ((p = strchr(optarg, '=')) == NULL) { 293 (void) fprintf(stderr, "%s: failed to set " 294 "option -o %s: option requires value\n", 295 fmd.d_pname, optarg); 296 return (FMD_EXIT_USAGE); 297 } 298 299 *p++ = '\0'; /* strike out the delimiter */ 300 301 if (p[0] == '"' && p[strlen(p) - 1] == '"') { 302 p[strlen(p) - 1] = '\0'; 303 (void) fmd_stresc2chr(++p); 304 } 305 306 if (fmd_conf_setprop(fmd.d_conf, optarg, p) != 0) { 307 (void) fprintf(stderr, 308 "%s: failed to set option -o %s: %s\n", 309 fmd.d_pname, optarg, fmd_strerror(errno)); 310 return (FMD_EXIT_USAGE); 311 } 312 } 313 } 314 315 if (fmd.d_fmd_debug & FMD_DBG_HELP) { 316 fmd_help(&fmd); 317 fmd_destroy(&fmd); 318 return (FMD_EXIT_SUCCESS); 319 } 320 321 /* 322 * Update the value of fmd.d_fg based on "fg" in case it changed. We 323 * use this property to decide whether to daemonize below. 324 */ 325 (void) fmd_conf_getprop(fmd.d_conf, "fg", &fmd.d_fg); 326 327 /* 328 * Once we're done setting our global state up, set up signal handlers 329 * for ensuring orderly termination on SIGTERM. If we are starting in 330 * the foreground, we also use the same handler for SIGINT and SIGHUP. 331 */ 332 (void) sigfillset(&set); 333 (void) sigdelset(&set, SIGABRT); /* always unblocked for ASSERT() */ 334 335 (void) sigfillset(&act.sa_mask); 336 act.sa_handler = handler; 337 act.sa_flags = 0; 338 339 (void) sigaction(SIGTERM, &act, NULL); 340 (void) sigdelset(&set, SIGTERM); 341 342 if (fmd.d_fg) { 343 (void) sigaction(SIGHUP, &act, NULL); 344 (void) sigdelset(&set, SIGHUP); 345 (void) sigaction(SIGINT, &act, NULL); 346 (void) sigdelset(&set, SIGINT); 347 348 (void) sigdelset(&set, SIGTSTP); 349 (void) sigdelset(&set, SIGTTIN); 350 (void) sigdelset(&set, SIGTTOU); 351 352 (void) printf("%s: [ loading modules ... ", fmd.d_pname); 353 (void) fflush(stdout); 354 } else 355 pfd = daemonize_init(); 356 357 /* 358 * Prior to this point, we are single-threaded. Once fmd_run() is 359 * called, we will be multi-threaded from this point on. The daemon's 360 * main thread will wait at the end of this function for signals. 361 */ 362 fmd_run(&fmd, pfd); 363 364 if (fmd.d_fg) { 365 (void) printf("done ]\n"); 366 (void) printf("%s: [ awaiting events ]\n", fmd.d_pname); 367 } else 368 daemonize_fini(pfd); 369 370 while (!fmd.d_signal) 371 (void) sigsuspend(&set); 372 373 fmd_destroy(&fmd); 374 return (FMD_EXIT_SUCCESS); 375 } 376