xref: /illumos-gate/usr/src/cmd/fm/fmd/common/fmd_main.c (revision 34a0f871d192b33b865455a8812a3d34c1866315)
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