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.
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.
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]
22 * Copyright (c) 2004, 2010, Oracle and/or its affiliates. All rights reserved.
25 #include <sys/types.h>
28 #include <sys/corectl.h>
29 #include <sys/resource.h>
31 #include <priv_utils.h>
41 #include <fmd_error.h>
42 #include <fmd_string.h>
44 #include <fmd_dispq.h>
51 * For DEBUG builds, we define a set of hooks for libumem that provide useful
52 * default settings for the allocator's debugging facilities.
58 return ("default,verbose"); /* $UMEM_DEBUG setting */
62 _umem_logging_init(void)
64 return ("fail,contents"); /* $UMEM_LOGGING setting */
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.
81 const char *gzp1
, *gzp2
, *gzp3
, *gzp4
, *gzp5
;
89 * Set our per-process core file path to leave core files in our
90 * var/fm/fmd directory, named after the PID to aid in debugging,
91 * and make sure that there is no restriction on core file size.
93 (void) snprintf(path
, sizeof (path
),
94 "%s/var/fm/fmd/core.%s.%%p", fmd
.d_rootdir
, fmd
.d_pname
);
96 (void) core_set_process_path(path
, strlen(path
) + 1, fmd
.d_pid
);
98 rlim
.rlim_cur
= RLIM_INFINITY
;
99 rlim
.rlim_max
= RLIM_INFINITY
;
101 (void) setrlimit(RLIMIT_CORE
, &rlim
);
104 * Claim all the file descriptors we can.
106 if (getrlimit(RLIMIT_NOFILE
, &rlim
) == 0) {
107 rlim
.rlim_cur
= rlim
.rlim_max
;
108 (void) setrlimit(RLIMIT_NOFILE
, &rlim
);
112 * Reset all of our privilege sets to the minimum set of required
113 * privileges. We continue to run as root so that files we create
114 * such as logs and checkpoints are secured in the /var filesystem.
116 * In a non-global zone some of the privileges we retain in a
117 * global zone are only optionally assigned to the zone, while others
120 * PRIV_PROC_PRIOCNTL (optional in a non-global zone):
121 * There are no calls to priocntl(2) in fmd or plugins.
123 * PRIV_SYS_CONFIG (prohibited in a non-global zone):
124 * Required, I think, for sysevent_post_event and/or
125 * other legacy sysevent activity. Legacy sysevent is not
126 * supported in a non-global zone.
128 * PRIV_SYS_DEVICES (prohibited in a non-global zone):
129 * Needed in the global zone for ioctls on various drivers
130 * such as memory-controller drivers.
132 * PRIV_SYS_RES_CONFIG (prohibited in a non-global zone):
133 * Require for p_online(2) calls to offline cpus.
135 * PRIV_SYS_NET_CONFIG (prohibited in a non-global zone):
136 * Required for ipsec in etm (which also requires
137 * PRIV_NET_PRIVADDR).
139 * We do without those privileges in a non-global zone. It's
140 * possible that there are other privs we could drop since
141 * hardware-related plugins are not present.
143 if (getzoneid() == GLOBAL_ZONEID
) {
144 gzp1
= PRIV_PROC_PRIOCNTL
;
145 gzp2
= PRIV_SYS_CONFIG
;
146 gzp3
= PRIV_SYS_DEVICES
;
147 gzp4
= PRIV_SYS_RES_CONFIG
;
148 gzp5
= PRIV_SYS_NET_CONFIG
;
150 gzp1
= gzp2
= gzp3
= gzp4
= gzp5
= NULL
;
153 if (__init_daemon_priv(PU_RESETGROUPS
| PU_LIMITPRIVS
| PU_INHERITPRIVS
,
154 0, 0, /* run as uid 0 and gid 0 */
155 PRIV_FILE_DAC_EXECUTE
, PRIV_FILE_DAC_READ
, PRIV_FILE_DAC_SEARCH
,
156 PRIV_FILE_DAC_WRITE
, PRIV_FILE_OWNER
, PRIV_PROC_OWNER
,
157 PRIV_SYS_ADMIN
, PRIV_NET_PRIVADDR
,
158 gzp1
, gzp2
, gzp3
, gzp4
, gzp5
, NULL
) != 0)
159 fmd_error(EFMD_EXIT
, "additional privileges required to run\n");
162 * Block all signals prior to the fork and leave them blocked in the
163 * parent so we don't get in a situation where the parent gets SIGINT
164 * and returns non-zero exit status and the child is actually running.
165 * In the child, restore the signal mask once we've done our setsid().
167 (void) sigfillset(&set
);
168 (void) sigdelset(&set
, SIGABRT
);
169 (void) sigprocmask(SIG_BLOCK
, &set
, &oset
);
171 if (pipe(pfds
) == -1)
172 fmd_error(EFMD_EXIT
, "failed to create pipe for daemonize");
174 if ((pid
= fork()) == -1)
175 fmd_error(EFMD_EXIT
, "failed to fork into background");
178 * If we're the parent process, wait for either the child to send us
179 * the appropriate exit status over the pipe or for the read to fail
180 * (presumably with 0 for EOF if our child terminated abnormally).
181 * If the read fails, exit with either the child's exit status if it
182 * exited or with FMD_EXIT_ERROR if it died from a fatal signal.
185 (void) close(pfds
[1]);
187 if (read(pfds
[0], &status
, sizeof (status
)) == sizeof (status
))
190 if (waitpid(pid
, &status
, 0) == pid
&& WIFEXITED(status
))
191 _exit(WEXITSTATUS(status
));
193 _exit(FMD_EXIT_ERROR
);
196 fmd
.d_pid
= getpid();
198 (void) sigprocmask(SIG_SETMASK
, &oset
, NULL
);
201 (void) close(pfds
[0]);
207 daemonize_fini(int fd
)
211 if ((fd
= open("/dev/null", O_RDWR
)) >= 0) {
212 (void) fcntl(fd
, F_DUP2FD
, STDIN_FILENO
);
213 (void) fcntl(fd
, F_DUP2FD
, STDOUT_FILENO
);
214 (void) fcntl(fd
, F_DUP2FD
, STDERR_FILENO
);
222 if (fmd
.d_signal
== 0)
227 usage(const char *arg0
, FILE *fp
)
230 "Usage: %s [-V] [-f file] [-o opt=val] [-R dir]\n", arg0
);
232 return (FMD_EXIT_USAGE
);
236 main(int argc
, char *argv
[])
238 const char *opt_f
= NULL
, *opt_R
= NULL
;
239 const char optstr
[] = "f:o:R:V";
240 int c
, pfd
= -1, opt_V
= 0;
243 struct sigaction act
;
247 * Parse the command-line once to validate all options and retrieve
248 * any overrides for our configuration file and root directory.
250 while ((c
= getopt(argc
, argv
, optstr
)) != EOF
) {
256 break; /* handle -o below */
264 return (usage(argv
[0], stderr
));
269 return (usage(argv
[0], stderr
));
273 const char *debug
= " (DEBUG)";
275 const char *debug
= "";
277 (void) printf("%s: version %s%s\n",
278 argv
[0], _fmd_version
, debug
);
279 return (FMD_EXIT_SUCCESS
);
282 closefrom(STDERR_FILENO
+ 1);
283 fmd_create(&fmd
, argv
[0], opt_R
, opt_f
);
286 * Now that we've initialized our global state, parse the command-line
287 * again for any configuration options specified using -o and set them.
289 for (optind
= 1; (c
= getopt(argc
, argv
, optstr
)) != EOF
; ) {
291 if ((p
= strchr(optarg
, '=')) == NULL
) {
292 (void) fprintf(stderr
, "%s: failed to set "
293 "option -o %s: option requires value\n",
294 fmd
.d_pname
, optarg
);
295 return (FMD_EXIT_USAGE
);
298 *p
++ = '\0'; /* strike out the delimiter */
300 if (p
[0] == '"' && p
[strlen(p
) - 1] == '"') {
301 p
[strlen(p
) - 1] = '\0';
302 (void) fmd_stresc2chr(++p
);
305 if (fmd_conf_setprop(fmd
.d_conf
, optarg
, p
) != 0) {
306 (void) fprintf(stderr
,
307 "%s: failed to set option -o %s: %s\n",
308 fmd
.d_pname
, optarg
, fmd_strerror(errno
));
309 return (FMD_EXIT_USAGE
);
314 if (fmd
.d_fmd_debug
& FMD_DBG_HELP
) {
317 return (FMD_EXIT_SUCCESS
);
321 * Update the value of fmd.d_fg based on "fg" in case it changed. We
322 * use this property to decide whether to daemonize below.
324 (void) fmd_conf_getprop(fmd
.d_conf
, "fg", &fmd
.d_fg
);
327 * Once we're done setting our global state up, set up signal handlers
328 * for ensuring orderly termination on SIGTERM. If we are starting in
329 * the foreground, we also use the same handler for SIGINT and SIGHUP.
331 (void) sigfillset(&set
);
332 (void) sigdelset(&set
, SIGABRT
); /* always unblocked for ASSERT() */
334 (void) sigfillset(&act
.sa_mask
);
335 act
.sa_handler
= handler
;
338 (void) sigaction(SIGTERM
, &act
, NULL
);
339 (void) sigdelset(&set
, SIGTERM
);
342 (void) sigaction(SIGHUP
, &act
, NULL
);
343 (void) sigdelset(&set
, SIGHUP
);
344 (void) sigaction(SIGINT
, &act
, NULL
);
345 (void) sigdelset(&set
, SIGINT
);
347 (void) sigdelset(&set
, SIGTSTP
);
348 (void) sigdelset(&set
, SIGTTIN
);
349 (void) sigdelset(&set
, SIGTTOU
);
351 (void) printf("%s: [ loading modules ... ", fmd
.d_pname
);
352 (void) fflush(stdout
);
354 pfd
= daemonize_init();
357 * Prior to this point, we are single-threaded. Once fmd_run() is
358 * called, we will be multi-threaded from this point on. The daemon's
359 * main thread will wait at the end of this function for signals.
364 (void) printf("done ]\n");
365 (void) printf("%s: [ awaiting events ]\n", fmd
.d_pname
);
369 while (!fmd
.d_signal
)
370 (void) sigsuspend(&set
);
373 return (FMD_EXIT_SUCCESS
);