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