xref: /illumos-gate/usr/src/cmd/acpihpd/acpihpd.c (revision 5422785d352a2bb398daceab3d1898a8aa64d006)
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 */
53 typedef void (sig_handler_t)(int);
54 
55 #define	ACPIHPD_PID_FILE "/var/run/acpihpd.pid" /* lock file path */
56 
57 /* Program Name */
58 char *g_prog_name;
59 int g_debuglevel = 0;
60 
61 static int s_pid_fd;
62 static sysevent_handle_t *s_acpihpd_hdl;
63 
64 static int daemon_init(void);
65 static void daemon_quit(int);
66 static int set_sig_handler(int, sig_handler_t *);
67 static int acpihpd_init(void);
68 static void acpihpd_fini(void);
69 static void acpihpd_event(sysevent_t *);
70 extern void notify_hotplug(sysevent_t *ev);
71 void debug_print(int, const char *, ...);
72 
73 int
74 main(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 
128 static int
129 daemon_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 
213 static void
214 daemon_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 
234 static int
235 set_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 
253 static int
254 acpihpd_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 
275 static void
276 acpihpd_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 
286 static void
287 acpihpd_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 
295 void
296 debug_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