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