xref: /illumos-gate/usr/src/cmd/dlmgmtd/dlmgmt_main.c (revision b6c3f786)
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 cache file, so that the mapping can be recovered when
39  * dlmgmtd exits for some reason (e.g., when dlmgmtd is accidentally killed).
40  */
41 
42 #include <assert.h>
43 #include <errno.h>
44 #include <fcntl.h>
45 #include <priv.h>
46 #include <signal.h>
47 #include <stdlib.h>
48 #include <stdio.h>
49 #include <stropts.h>
50 #include <strings.h>
51 #include <syslog.h>
52 #include <sys/dld.h>
53 #include <unistd.h>
54 #include <libdlmgmt.h>
55 #include "dlmgmt_impl.h"
56 
57 const char		*progname;
58 boolean_t		debug;
59 static int		pfds[2];
60 static char		dlmgmt_door_file[] = DLMGMT_DOOR;
61 static int		dlmgmt_door_fd = -1;
62 
63 static int
64 dlmgmt_set_doorfd(boolean_t start)
65 {
66 	dld_ioc_door_t did;
67 	struct strioctl iocb;
68 	int fd;
69 	int err = 0;
70 
71 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
72 		return (EINVAL);
73 
74 	did.did_start_door = start;
75 
76 	iocb.ic_cmd	= DLDIOC_DOORSERVER;
77 	iocb.ic_timout	= 0;
78 	iocb.ic_len	= sizeof (did);
79 	iocb.ic_dp	= (char *)&did;
80 
81 	if (ioctl(fd, I_STR, &iocb) == -1)
82 		err = errno;
83 
84 	(void) close(fd);
85 	return (err);
86 }
87 
88 static int
89 dlmgmt_door_init()
90 {
91 	int err;
92 
93 	if ((dlmgmt_door_fd = door_create(dlmgmt_handler, NULL,
94 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
95 		err = errno;
96 		dlmgmt_log(LOG_WARNING, "door_create() failed: %s",
97 		    strerror(err));
98 		return (err);
99 	}
100 	if (fattach(dlmgmt_door_fd, DLMGMT_DOOR) != 0) {
101 		err = errno;
102 		dlmgmt_log(LOG_WARNING, "fattach(%s) failed: %s",
103 		    DLMGMT_DOOR, strerror(err));
104 		goto fail;
105 	}
106 	if ((err = dlmgmt_set_doorfd(B_TRUE)) != 0) {
107 		dlmgmt_log(LOG_WARNING, "cannot set kernel doorfd: %s",
108 		    strerror(err));
109 		goto fail;
110 	}
111 
112 	return (0);
113 fail:
114 	if (dlmgmt_door_fd != -1) {
115 		(void) door_revoke(dlmgmt_door_fd);
116 		dlmgmt_door_fd = -1;
117 	}
118 	(void) fdetach(DLMGMT_DOOR);
119 	return (err);
120 }
121 
122 static void
123 dlmgmt_door_fini()
124 {
125 	(void) dlmgmt_set_doorfd(B_FALSE);
126 	if ((dlmgmt_door_fd != -1) && (door_revoke(dlmgmt_door_fd) == -1)) {
127 		dlmgmt_log(LOG_WARNING, "door_revoke(%s) failed: %s",
128 		    dlmgmt_door_file, strerror(errno));
129 	}
130 	(void) fdetach(DLMGMT_DOOR);
131 }
132 
133 static int
134 dlmgmt_init()
135 {
136 	int err;
137 
138 	if ((err = dlmgmt_linktable_init()) != 0)
139 		return (err);
140 
141 	if ((err = dlmgmt_db_init()) != 0 || (err = dlmgmt_door_init()) != 0)
142 		dlmgmt_linktable_fini();
143 
144 	return (err);
145 }
146 
147 static void
148 dlmgmt_fini()
149 {
150 	dlmgmt_door_fini();
151 	dlmgmt_linktable_fini();
152 }
153 
154 /*
155  * This is called by the child process to inform the parent process to
156  * exit with the given return value.
157  */
158 static void
159 dlmgmt_inform_parent_exit(int rv)
160 {
161 	if (debug)
162 		return;
163 
164 	if (write(pfds[1], &rv, sizeof (int)) != sizeof (int)) {
165 		dlmgmt_log(LOG_WARNING,
166 		    "dlmgmt_inform_parent_exit() failed: %s", strerror(errno));
167 		(void) close(pfds[1]);
168 		exit(EXIT_FAILURE);
169 	}
170 	(void) close(pfds[1]);
171 }
172 
173 /*ARGSUSED*/
174 static void
175 dlmgmtd_exit(int signo)
176 {
177 	(void) close(pfds[1]);
178 	dlmgmt_fini();
179 	exit(EXIT_FAILURE);
180 }
181 
182 static void
183 usage(void)
184 {
185 	(void) fprintf(stderr, "Usage: %s [-d]\n", progname);
186 	exit(EXIT_FAILURE);
187 }
188 
189 static int
190 dlmgmt_setup_privs()
191 {
192 	priv_set_t *priv_set = NULL;
193 	char *p;
194 
195 	priv_set = priv_allocset();
196 	if (priv_set == NULL || getppriv(PRIV_PERMITTED, priv_set) == -1) {
197 		dlmgmt_log(LOG_WARNING, "failed to get the permitted set of "
198 		    "privileges %s", strerror(errno));
199 		return (-1);
200 	}
201 
202 	p = priv_set_to_str(priv_set, ',', 0);
203 	dlmgmt_log(LOG_DEBUG, "start with privs %s", p != NULL ? p : "Unknown");
204 	free(p);
205 
206 	priv_emptyset(priv_set);
207 	(void) priv_addset(priv_set, "file_dac_write");
208 	(void) priv_addset(priv_set, "file_chown_self");
209 	(void) priv_addset(priv_set, "sys_mount");
210 	(void) priv_addset(priv_set, "sys_net_config");
211 
212 	if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) {
213 		dlmgmt_log(LOG_WARNING, "failed to set the inheritable set of "
214 		    "privileges %s", strerror(errno));
215 		priv_freeset(priv_set);
216 		return (-1);
217 	}
218 
219 	if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) {
220 		dlmgmt_log(LOG_WARNING, "failed to set the permitted set of "
221 		    "privileges %s", strerror(errno));
222 		priv_freeset(priv_set);
223 		return (-1);
224 	}
225 
226 	if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
227 		dlmgmt_log(LOG_WARNING, "failed to set the effective set of "
228 		    "privileges %s", strerror(errno));
229 		priv_freeset(priv_set);
230 		return (-1);
231 	}
232 
233 	priv_freeset(priv_set);
234 	return (0);
235 }
236 
237 /*
238  * Keep the pfds fd open, close other fds.
239  */
240 /*ARGSUSED*/
241 static int
242 closefunc(void *arg, int fd)
243 {
244 	if (fd != pfds[1])
245 		(void) close(fd);
246 	return (0);
247 }
248 
249 static boolean_t
250 dlmgmt_daemonize(void)
251 {
252 	pid_t pid;
253 	int rv;
254 
255 	if (pipe(pfds) < 0) {
256 		(void) fprintf(stderr, "%s: pipe() failed: %s\n",
257 		    progname, strerror(errno));
258 		exit(EXIT_FAILURE);
259 	}
260 
261 	if ((pid = fork()) == -1) {
262 		(void) fprintf(stderr, "%s: fork() failed: %s\n",
263 		    progname, strerror(errno));
264 		exit(EXIT_FAILURE);
265 	} else if (pid > 0) { /* Parent */
266 		(void) close(pfds[1]);
267 
268 		/*
269 		 * Read the child process's return value from the pfds.
270 		 * If the child process exits unexpected, read() returns -1.
271 		 */
272 		if (read(pfds[0], &rv, sizeof (int)) != sizeof (int)) {
273 			(void) kill(pid, SIGKILL);
274 			rv = EXIT_FAILURE;
275 		}
276 
277 		(void) close(pfds[0]);
278 		exit(rv);
279 	}
280 
281 	/* Child */
282 	(void) close(pfds[0]);
283 	(void) setsid();
284 
285 	/*
286 	 * Close all files except pfds[1].
287 	 */
288 	(void) fdwalk(closefunc, NULL);
289 	(void) chdir("/");
290 	openlog(progname, LOG_PID, LOG_DAEMON);
291 	return (B_TRUE);
292 }
293 
294 int
295 main(int argc, char *argv[])
296 {
297 	int opt;
298 
299 	progname = strrchr(argv[0], '/');
300 	if (progname != NULL)
301 		progname++;
302 	else
303 		progname = argv[0];
304 
305 	/*
306 	 * Process options.
307 	 */
308 	while ((opt = getopt(argc, argv, "d")) != EOF) {
309 		switch (opt) {
310 		case 'd':
311 			debug = B_TRUE;
312 			break;
313 		default:
314 			usage();
315 		}
316 	}
317 
318 	if (!debug && !dlmgmt_daemonize())
319 		return (EXIT_FAILURE);
320 
321 	if (signal(SIGTERM, dlmgmtd_exit) == SIG_ERR) {
322 		dlmgmt_log(LOG_WARNING, "signal() for SIGTERM failed: %s",
323 		    strerror(errno));
324 		goto child_out;
325 	}
326 
327 	if (dlmgmt_init() != 0)
328 		goto child_out;
329 
330 	if (dlmgmt_setup_privs() != 0)
331 		goto child_out;
332 
333 	/*
334 	 * Inform the parent process that it can successfully exit.
335 	 */
336 	dlmgmt_inform_parent_exit(EXIT_SUCCESS);
337 
338 	for (;;)
339 		(void) pause();
340 
341 child_out:
342 	/* return from main() forcibly exits an MT process */
343 	dlmgmt_inform_parent_exit(EXIT_FAILURE);
344 	return (EXIT_FAILURE);
345 }
346