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/*
23 * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26/*
27 * Copyright (c) 2010, Intel Corporation.
28 * All rights reserved.
29 */
30
31#include <sys/param.h>
32#include <sys/stat.h>
33#include <sys/types.h>
34#include <sys/sysevent/eventdefs.h>
35#include <sys/sysevent/dr.h>
36
37#include <stdio.h>
38#include <stdlib.h>
39#include <unistd.h>
40#include <signal.h>
41#include <syslog.h>
42#include <string.h>
43#include <strings.h>
44#include <fcntl.h>
45#include <errno.h>
46#include <time.h>
47#include <config_admin.h>
48#include <libscf.h>
49#include <libsysevent.h>
50#include <stdarg.h>
51
52/* Signal handler type */
53typedef void (sig_handler_t)(int);
54
55#define	ACPIHPD_PID_FILE "/var/run/acpihpd.pid" /* lock file path */
56
57/* Program Name */
58char *g_prog_name;
59int g_debuglevel = 0;
60
61static int s_pid_fd;
62static sysevent_handle_t *s_acpihpd_hdl;
63
64static int daemon_init(void);
65static void daemon_quit(int);
66static int set_sig_handler(int, sig_handler_t *);
67static int acpihpd_init(void);
68static void acpihpd_fini(void);
69static void acpihpd_event(sysevent_t *);
70extern void notify_hotplug(sysevent_t *ev);
71void debug_print(int, const char *, ...);
72
73int
74main(int argc, char *argv[])
75{
76	int c;
77
78	/* Get Program Name */
79	if ((g_prog_name = strrchr(argv[0], '/')) == NULL) {
80		g_prog_name = argv[0];
81	} else {
82		g_prog_name++;
83	}
84
85	while ((c = getopt(argc, argv, ":d:")) != -1) {
86		switch (c) {
87		case 'd':
88			g_debuglevel = atoi(optarg);
89			if ((g_debuglevel < 0) || (g_debuglevel > 2)) {
90				g_debuglevel = 0;
91			}
92			break;
93
94		case ':':
95			syslog(LOG_ERR,
96			    "missed argument for option %c.", optopt);
97			break;
98
99		case '?':
100			syslog(LOG_ERR, "unrecognized option %c.", optopt);
101			break;
102		}
103	}
104
105	s_acpihpd_hdl = NULL;
106
107	/* Check the daemon running lock and initialize the signal */
108	if (daemon_init() != 0) {
109		debug_print(0, "%s could not startup!", g_prog_name);
110		exit(SMF_EXIT_ERR_FATAL);
111	}
112
113	/* Subscribe to the hotplug event */
114	if (acpihpd_init() != 0) {
115		debug_print(0, "%s could not startup!", g_prog_name);
116		daemon_quit(SMF_EXIT_ERR_FATAL);
117	}
118
119	debug_print(2, "daemon is running.");
120	/*CONSTCOND*/
121	while (1) {
122		(void) pause();
123	}
124
125	return (SMF_EXIT_OK);
126}
127
128static int
129daemon_init(void)
130{
131	int	i, ret;
132	pid_t	pid;
133	char	pid_str[32];
134
135	if (geteuid() != 0) {
136		debug_print(0, "must be root to execute %s", g_prog_name);
137		return (1);
138	}
139
140	if ((pid = fork()) < 0) {
141		return (1);
142	}
143
144	if (pid > 0) {
145		/* Parent to exit. */
146		exit(SMF_EXIT_OK);
147	}
148
149	(void) setsid();
150	(void) chdir("/");
151	(void) umask(0);
152	(void) closefrom(0);
153	(void) open("/dev/null", O_RDONLY);
154	(void) open("/dev/null", O_WRONLY);
155	(void) dup(1);
156	(void) openlog(g_prog_name, LOG_PID, LOG_DAEMON);
157
158	/*
159	 * Create the lock file for singleton
160	 */
161	if ((s_pid_fd = open(ACPIHPD_PID_FILE, O_RDWR | O_CREAT, 0644)) < 0) {
162		debug_print(0, "could not create pid file: %s",
163		    strerror(errno));
164		return (1);
165	}
166
167	if (lockf(s_pid_fd, F_TLOCK, 0L) < 0) {
168		if (errno == EACCES || errno == EAGAIN) {
169			debug_print(0, "another acpihpd is already running");
170		} else {
171			debug_print(0, "could not lock pid file");
172		}
173
174		return (1);
175	}
176
177	(void) ftruncate(s_pid_fd, 0);
178	i = sprintf(pid_str, "%ld", (long)getpid());
179	while ((ret = write(s_pid_fd, pid_str, i)) != i) {
180		if (errno == EINTR) {
181			continue;
182		}
183		if (ret < 0) {
184			debug_print(0, "pid file write failed: %s",
185			    strerror(errno));
186			return (1);
187		}
188	}
189
190	if (set_sig_handler(SIGTERM, (sig_handler_t *)daemon_quit) != 0) {
191		debug_print(2, "could not set signal handler(SIGTERM)");
192		return (1);
193	}
194
195	if (set_sig_handler(SIGQUIT, (sig_handler_t *)daemon_quit) != 0) {
196		debug_print(2, "could not set signal handler(SIGQUIT)");
197		return (1);
198	}
199
200	if (set_sig_handler(SIGINT, (sig_handler_t *)daemon_quit) != 0) {
201		debug_print(2, "could not set signal handler(SIGINT)");
202		return (1);
203	}
204
205	if (set_sig_handler(SIGCHLD, SIG_IGN) != 0) {
206		debug_print(2, "could not set signal handler(SIGCHLD)");
207		return (1);
208	}
209
210	return (0);
211}
212
213static void
214daemon_quit(int signo)
215{
216	int status = 0;
217	id_t pgid;
218
219	debug_print(1, "daemon quit [signal#:%d].", signo);
220
221	acpihpd_fini();
222	(void) set_sig_handler(SIGTERM, SIG_IGN);
223	pgid = getpgrp();
224	(void) kill(-pgid, SIGTERM);
225	(void) close(s_pid_fd);
226	(void) unlink(ACPIHPD_PID_FILE);
227
228	if (signo < 0) {
229		status = signo;
230	}
231	_exit(status);
232}
233
234static int
235set_sig_handler(int sig, sig_handler_t *handler)
236{
237	struct sigaction act;
238
239	act.sa_handler = handler;
240	act.sa_flags = 0;
241	if (sig == SIGCHLD && handler == SIG_IGN) {
242		act.sa_flags |= SA_NOCLDWAIT;
243	}
244
245	(void) sigemptyset(&act.sa_mask);
246	if (sigaction(sig, &act, NULL) < 0) {
247		return (1);
248	}
249
250	return (0);
251}
252
253static int
254acpihpd_init(void)
255{
256	const char *subclass = ESC_DR_REQ;
257
258	debug_print(2, "acpihpd_init");
259
260	if ((s_acpihpd_hdl = sysevent_bind_handle(acpihpd_event)) == NULL) {
261		debug_print(2, "could not bind to sysevent.");
262		return (-1);
263	}
264
265	if (sysevent_subscribe_event(s_acpihpd_hdl, EC_DR, &subclass, 1) != 0) {
266		debug_print(2, "could not subscribe an event.");
267		sysevent_unbind_handle(s_acpihpd_hdl);
268		s_acpihpd_hdl = NULL;
269		return (-1);
270	}
271
272	return (0);
273}
274
275static void
276acpihpd_fini(void)
277{
278	debug_print(2, "acpihpd_fini");
279
280	if (s_acpihpd_hdl != NULL) {
281		sysevent_unsubscribe_event(s_acpihpd_hdl, EC_DR);
282		sysevent_unbind_handle(s_acpihpd_hdl);
283	}
284}
285
286static void
287acpihpd_event(sysevent_t *ev)
288{
289	debug_print(2, "*** got an event ***");
290
291	/* Inform cfgadm of the hot-plug event. */
292	notify_hotplug(ev);
293}
294
295void
296debug_print(int level, const char *fmt, ...)
297{
298	va_list ap;
299	int pri, pr_out = 0;
300
301	if (level <= g_debuglevel) {
302		switch (level) {
303		case 0:
304			pri = LOG_ERR;
305			pr_out = 1;
306			break;
307
308		case 1:
309			pri = LOG_NOTICE;
310			pr_out = 1;
311			break;
312
313		case 2:
314			pri = LOG_DEBUG;
315			pr_out = 1;
316			break;
317		}
318
319		if (pr_out) {
320			va_start(ap, fmt);
321			vsyslog(pri, fmt, ap);
322			va_end(ap);
323		}
324	}
325}
326