xref: /illumos-gate/usr/src/cmd/dlmgmtd/dlmgmt_main.c (revision 51ccf66eff01cf7e19106ce91bc35ef397259faf)
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 2008 Sun Microsystems, Inc.  All rights reserved.
24  * Use is subject to license terms.
25  */
26 
27 #pragma ident	"%Z%%M%	%I%	%E% SMI"
28 
29 /*
30  * The dlmgmtd daemon is started by the datalink-management SMF service.
31  * This daemon is used to manage <link name, linkid> mapping and the
32  * persistent datalink configuration.
33  *
34  * Today, the <link name, linkid> mapping and the persistent configuration
35  * of datalinks is kept in /etc/dladm/datalink.conf, and the daemon keeps
36  * a copy of the datalinks in the memory (see dlmgmt_id_avl and
37  * dlmgmt_name_avl). The active <link name, linkid> mapping is kept in
38  * /etc/svc/volatile/dladm cache file, so that the mapping can be recovered
39  * when dlmgmtd exits for some reason (e.g., when dlmgmtd is accidentally
40  * killed).
41  */
42 
43 #include <assert.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <priv_utils.h>
47 #include <signal.h>
48 #include <stdlib.h>
49 #include <stdio.h>
50 #include <stropts.h>
51 #include <strings.h>
52 #include <syslog.h>
53 #include <sys/dld.h>
54 #include <sys/param.h>
55 #include <sys/stat.h>
56 #include <unistd.h>
57 #include <libdlmgmt.h>
58 #include "dlmgmt_impl.h"
59 
60 const char		*progname;
61 boolean_t		debug;
62 static int		pfds[2];
63 static int		dlmgmt_door_fd = -1;
64 static int		dld_control_fd = -1;
65 
66 static void		dlmgmtd_exit(int);
67 static int		dlmgmt_init();
68 static void		dlmgmt_fini();
69 static int		dlmgmt_init_privileges();
70 static void		dlmgmt_fini_privileges();
71 
72 static int
73 dlmgmt_set_doorfd(boolean_t start)
74 {
75 	dld_ioc_door_t did;
76 	struct strioctl iocb;
77 	int err = 0;
78 
79 	assert(dld_control_fd != -1);
80 
81 	did.did_start_door = start;
82 
83 	iocb.ic_cmd	= DLDIOC_DOORSERVER;
84 	iocb.ic_timout	= 0;
85 	iocb.ic_len	= sizeof (did);
86 	iocb.ic_dp	= (char *)&did;
87 
88 	if (ioctl(dld_control_fd, I_STR, &iocb) == -1)
89 		err = errno;
90 
91 	return (err);
92 }
93 
94 static int
95 dlmgmt_door_init()
96 {
97 	int fd;
98 	int err;
99 
100 	/*
101 	 * Create the door file for dlmgmtd.
102 	 */
103 	if ((fd = open(DLMGMT_DOOR, O_CREAT|O_RDONLY, 0644)) == -1) {
104 		err = errno;
105 		dlmgmt_log(LOG_ERR, "open(%s) failed: %s",
106 		    DLMGMT_DOOR, strerror(err));
107 		return (err);
108 	}
109 	(void) close(fd);
110 
111 	if ((dlmgmt_door_fd = door_create(dlmgmt_handler, NULL,
112 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
113 		err = errno;
114 		dlmgmt_log(LOG_ERR, "door_create() failed: %s",
115 		    strerror(err));
116 		return (err);
117 	}
118 	if (fattach(dlmgmt_door_fd, DLMGMT_DOOR) != 0) {
119 		err = errno;
120 		dlmgmt_log(LOG_ERR, "fattach(%s) failed: %s",
121 		    DLMGMT_DOOR, strerror(err));
122 		goto fail;
123 	}
124 	if ((err = dlmgmt_set_doorfd(B_TRUE)) != 0) {
125 		dlmgmt_log(LOG_ERR, "cannot set kernel doorfd: %s",
126 		    strerror(err));
127 		(void) fdetach(DLMGMT_DOOR);
128 		goto fail;
129 	}
130 
131 	return (0);
132 fail:
133 	(void) door_revoke(dlmgmt_door_fd);
134 	dlmgmt_door_fd = -1;
135 	return (err);
136 }
137 
138 static void
139 dlmgmt_door_fini()
140 {
141 	if (dlmgmt_door_fd == -1)
142 		return;
143 
144 	if (door_revoke(dlmgmt_door_fd) == -1) {
145 		dlmgmt_log(LOG_WARNING, "door_revoke(%s) failed: %s",
146 		    DLMGMT_DOOR, strerror(errno));
147 	}
148 
149 	(void) fdetach(DLMGMT_DOOR);
150 	(void) dlmgmt_set_doorfd(B_FALSE);
151 }
152 
153 static int
154 dlmgmt_init()
155 {
156 	int		err;
157 
158 	if (signal(SIGTERM, dlmgmtd_exit) == SIG_ERR ||
159 	    signal(SIGINT, dlmgmtd_exit) == SIG_ERR) {
160 		err = errno;
161 		dlmgmt_log(LOG_ERR, "signal() for SIGTERM/INT failed: %s",
162 		    strerror(err));
163 		return (err);
164 	}
165 
166 	if ((err = dlmgmt_linktable_init()) != 0)
167 		return (err);
168 
169 	if ((err = dlmgmt_db_init()) != 0 || (err = dlmgmt_door_init()) != 0)
170 		dlmgmt_linktable_fini();
171 
172 	return (err);
173 }
174 
175 static void
176 dlmgmt_fini()
177 {
178 	dlmgmt_door_fini();
179 	dlmgmt_linktable_fini();
180 }
181 
182 /*
183  * This is called by the child process to inform the parent process to
184  * exit with the given return value.
185  */
186 static void
187 dlmgmt_inform_parent_exit(int rv)
188 {
189 	if (debug)
190 		return;
191 
192 	if (write(pfds[1], &rv, sizeof (int)) != sizeof (int)) {
193 		dlmgmt_log(LOG_WARNING,
194 		    "dlmgmt_inform_parent_exit() failed: %s", strerror(errno));
195 		(void) close(pfds[1]);
196 		exit(EXIT_FAILURE);
197 	}
198 	(void) close(pfds[1]);
199 }
200 
201 /*ARGSUSED*/
202 static void
203 dlmgmtd_exit(int signo)
204 {
205 	(void) close(pfds[1]);
206 	dlmgmt_fini();
207 	dlmgmt_fini_privileges();
208 	exit(EXIT_FAILURE);
209 }
210 
211 static void
212 usage(void)
213 {
214 	(void) fprintf(stderr, "Usage: %s [-d]\n", progname);
215 	exit(EXIT_FAILURE);
216 }
217 
218 /*
219  * Set the uid of this daemon to the "dladm" user. Finish the following
220  * operations before setuid() because they need root privileges:
221  *
222  *    - create the /etc/svc/volatile/dladm directory;
223  *    - change its uid/gid to "dladm"/"sys";
224  *    - open the dld control node
225  */
226 static int
227 dlmgmt_init_privileges()
228 {
229 	struct stat	statbuf;
230 
231 	/*
232 	 * Create the DLMGMT_TMPFS_DIR directory.
233 	 */
234 	if (stat(DLMGMT_TMPFS_DIR, &statbuf) < 0) {
235 		if (mkdir(DLMGMT_TMPFS_DIR, (mode_t)0755) < 0)
236 			return (errno);
237 	} else {
238 		if ((statbuf.st_mode & S_IFMT) != S_IFDIR)
239 			return (ENOTDIR);
240 	}
241 
242 	if ((chmod(DLMGMT_TMPFS_DIR, 0755) < 0) ||
243 	    (chown(DLMGMT_TMPFS_DIR, UID_DLADM, GID_SYS) < 0)) {
244 		return (EPERM);
245 	}
246 
247 	/*
248 	 * When dlmgmtd is started at boot, "ALL" privilege is required
249 	 * to open the dld control node.
250 	 */
251 	if ((dld_control_fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
252 		return (errno);
253 
254 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, UID_DLADM,
255 	    GID_SYS, NULL) == -1) {
256 		(void) close(dld_control_fd);
257 		dld_control_fd = -1;
258 		return (EPERM);
259 	}
260 
261 	return (0);
262 }
263 
264 static void
265 dlmgmt_fini_privileges()
266 {
267 	if (dld_control_fd != -1) {
268 		(void) close(dld_control_fd);
269 		dld_control_fd = -1;
270 	}
271 }
272 
273 /*
274  * Keep the pfds fd open, close other fds.
275  */
276 /*ARGSUSED*/
277 static int
278 closefunc(void *arg, int fd)
279 {
280 	if (fd != pfds[1])
281 		(void) close(fd);
282 	return (0);
283 }
284 
285 static boolean_t
286 dlmgmt_daemonize(void)
287 {
288 	pid_t pid;
289 	int rv;
290 
291 	if (pipe(pfds) < 0) {
292 		(void) fprintf(stderr, "%s: pipe() failed: %s\n",
293 		    progname, strerror(errno));
294 		exit(EXIT_FAILURE);
295 	}
296 
297 	if ((pid = fork()) == -1) {
298 		(void) fprintf(stderr, "%s: fork() failed: %s\n",
299 		    progname, strerror(errno));
300 		exit(EXIT_FAILURE);
301 	} else if (pid > 0) { /* Parent */
302 		(void) close(pfds[1]);
303 
304 		/*
305 		 * Read the child process's return value from the pfds.
306 		 * If the child process exits unexpected, read() returns -1.
307 		 */
308 		if (read(pfds[0], &rv, sizeof (int)) != sizeof (int)) {
309 			(void) kill(pid, SIGKILL);
310 			rv = EXIT_FAILURE;
311 		}
312 
313 		(void) close(pfds[0]);
314 		exit(rv);
315 	}
316 
317 	/* Child */
318 	(void) close(pfds[0]);
319 	(void) setsid();
320 
321 	/*
322 	 * Close all files except pfds[1].
323 	 */
324 	(void) fdwalk(closefunc, NULL);
325 	(void) chdir("/");
326 	openlog(progname, LOG_PID, LOG_DAEMON);
327 	return (B_TRUE);
328 }
329 
330 int
331 main(int argc, char *argv[])
332 {
333 	int		opt;
334 
335 	progname = strrchr(argv[0], '/');
336 	if (progname != NULL)
337 		progname++;
338 	else
339 		progname = argv[0];
340 
341 	/*
342 	 * Process options.
343 	 */
344 	while ((opt = getopt(argc, argv, "d")) != EOF) {
345 		switch (opt) {
346 		case 'd':
347 			debug = B_TRUE;
348 			break;
349 		default:
350 			usage();
351 		}
352 	}
353 
354 	if (!debug && !dlmgmt_daemonize())
355 		return (EXIT_FAILURE);
356 
357 	if ((errno = dlmgmt_init_privileges()) != 0) {
358 		dlmgmt_log(LOG_ERR, "dlmgmt_init_privileges() failed: %s",
359 		    strerror(errno));
360 		goto child_out;
361 	}
362 
363 	if (dlmgmt_init() != 0) {
364 		dlmgmt_fini_privileges();
365 		goto child_out;
366 	}
367 
368 	/*
369 	 * Inform the parent process that it can successfully exit.
370 	 */
371 	dlmgmt_inform_parent_exit(EXIT_SUCCESS);
372 
373 	for (;;)
374 		(void) pause();
375 
376 child_out:
377 	/* return from main() forcibly exits an MT process */
378 	dlmgmt_inform_parent_exit(EXIT_FAILURE);
379 	return (EXIT_FAILURE);
380 }
381