xref: /illumos-gate/usr/src/cmd/dlmgmtd/dlmgmt_main.c (revision d62bc4ba)
1*d62bc4baSyz /*
2*d62bc4baSyz  * CDDL HEADER START
3*d62bc4baSyz  *
4*d62bc4baSyz  * The contents of this file are subject to the terms of the
5*d62bc4baSyz  * Common Development and Distribution License (the "License").
6*d62bc4baSyz  * You may not use this file except in compliance with the License.
7*d62bc4baSyz  *
8*d62bc4baSyz  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*d62bc4baSyz  * or http://www.opensolaris.org/os/licensing.
10*d62bc4baSyz  * See the License for the specific language governing permissions
11*d62bc4baSyz  * and limitations under the License.
12*d62bc4baSyz  *
13*d62bc4baSyz  * When distributing Covered Code, include this CDDL HEADER in each
14*d62bc4baSyz  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*d62bc4baSyz  * If applicable, add the following below this CDDL HEADER, with the
16*d62bc4baSyz  * fields enclosed by brackets "[]" replaced with your own identifying
17*d62bc4baSyz  * information: Portions Copyright [yyyy] [name of copyright owner]
18*d62bc4baSyz  *
19*d62bc4baSyz  * CDDL HEADER END
20*d62bc4baSyz  */
21*d62bc4baSyz 
22*d62bc4baSyz /*
23*d62bc4baSyz  * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24*d62bc4baSyz  * Use is subject to license terms.
25*d62bc4baSyz  */
26*d62bc4baSyz 
27*d62bc4baSyz #pragma ident	"%Z%%M%	%I%	%E% SMI"
28*d62bc4baSyz 
29*d62bc4baSyz /*
30*d62bc4baSyz  * The dlmgmtd daemon is started by the datalink-management SMF service.
31*d62bc4baSyz  * This daemon is used to manage <link name, linkid> mapping and the
32*d62bc4baSyz  * persistent datalink configuration.
33*d62bc4baSyz  *
34*d62bc4baSyz  * Today, the <link name, linkid> mapping and the persistent configuration
35*d62bc4baSyz  * of datalinks is kept in /etc/dladm/datalink.conf, and the daemon keeps
36*d62bc4baSyz  * a copy of the datalinks in the memory (see dlmgmt_id_avl and
37*d62bc4baSyz  * dlmgmt_name_avl). The active <link name, linkid> mapping is kept in
38*d62bc4baSyz  * /etc/svc/volatile cache file, so that the mapping can be recovered when
39*d62bc4baSyz  * dlmgmtd exits for some reason (e.g., when dlmgmtd is accidentally killed).
40*d62bc4baSyz  */
41*d62bc4baSyz 
42*d62bc4baSyz #include <assert.h>
43*d62bc4baSyz #include <errno.h>
44*d62bc4baSyz #include <fcntl.h>
45*d62bc4baSyz #include <priv.h>
46*d62bc4baSyz #include <signal.h>
47*d62bc4baSyz #include <stdlib.h>
48*d62bc4baSyz #include <stdio.h>
49*d62bc4baSyz #include <stropts.h>
50*d62bc4baSyz #include <strings.h>
51*d62bc4baSyz #include <syslog.h>
52*d62bc4baSyz #include <sys/dld.h>
53*d62bc4baSyz #include <unistd.h>
54*d62bc4baSyz #include <libdlmgmt.h>
55*d62bc4baSyz #include "dlmgmt_impl.h"
56*d62bc4baSyz 
57*d62bc4baSyz const char		*progname;
58*d62bc4baSyz boolean_t		debug;
59*d62bc4baSyz static int		pfds[2];
60*d62bc4baSyz static char		dlmgmt_door_file[] = DLMGMT_DOOR;
61*d62bc4baSyz static int		dlmgmt_door_fd = -1;
62*d62bc4baSyz 
63*d62bc4baSyz static int
64*d62bc4baSyz dlmgmt_set_doorfd(boolean_t start)
65*d62bc4baSyz {
66*d62bc4baSyz 	dld_ioc_door_t did;
67*d62bc4baSyz 	struct strioctl iocb;
68*d62bc4baSyz 	int fd;
69*d62bc4baSyz 	int err = 0;
70*d62bc4baSyz 
71*d62bc4baSyz 	if ((fd = open(DLD_CONTROL_DEV, O_RDWR)) < 0)
72*d62bc4baSyz 		return (EINVAL);
73*d62bc4baSyz 
74*d62bc4baSyz 	did.did_start_door = start;
75*d62bc4baSyz 
76*d62bc4baSyz 	iocb.ic_cmd	= DLDIOC_DOORSERVER;
77*d62bc4baSyz 	iocb.ic_timout	= 0;
78*d62bc4baSyz 	iocb.ic_len	= sizeof (did);
79*d62bc4baSyz 	iocb.ic_dp	= (char *)&did;
80*d62bc4baSyz 
81*d62bc4baSyz 	if (ioctl(fd, I_STR, &iocb) == -1)
82*d62bc4baSyz 		err = errno;
83*d62bc4baSyz 
84*d62bc4baSyz 	(void) close(fd);
85*d62bc4baSyz 	return (err);
86*d62bc4baSyz }
87*d62bc4baSyz 
88*d62bc4baSyz static int
89*d62bc4baSyz dlmgmt_door_init()
90*d62bc4baSyz {
91*d62bc4baSyz 	int err;
92*d62bc4baSyz 
93*d62bc4baSyz 	if ((dlmgmt_door_fd = door_create(dlmgmt_handler, NULL,
94*d62bc4baSyz 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) {
95*d62bc4baSyz 		err = errno;
96*d62bc4baSyz 		dlmgmt_log(LOG_WARNING, "door_create() failed: %s",
97*d62bc4baSyz 		    strerror(err));
98*d62bc4baSyz 		return (err);
99*d62bc4baSyz 	}
100*d62bc4baSyz 	if (fattach(dlmgmt_door_fd, DLMGMT_DOOR) != 0) {
101*d62bc4baSyz 		err = errno;
102*d62bc4baSyz 		dlmgmt_log(LOG_WARNING, "fattach(%s) failed: %s",
103*d62bc4baSyz 		    DLMGMT_DOOR, strerror(err));
104*d62bc4baSyz 		goto fail;
105*d62bc4baSyz 	}
106*d62bc4baSyz 	if ((err = dlmgmt_set_doorfd(B_TRUE)) != 0) {
107*d62bc4baSyz 		dlmgmt_log(LOG_WARNING, "cannot set kernel doorfd: %s",
108*d62bc4baSyz 		    strerror(err));
109*d62bc4baSyz 		goto fail;
110*d62bc4baSyz 	}
111*d62bc4baSyz 
112*d62bc4baSyz 	return (0);
113*d62bc4baSyz fail:
114*d62bc4baSyz 	if (dlmgmt_door_fd != -1) {
115*d62bc4baSyz 		(void) door_revoke(dlmgmt_door_fd);
116*d62bc4baSyz 		dlmgmt_door_fd = -1;
117*d62bc4baSyz 	}
118*d62bc4baSyz 	(void) fdetach(DLMGMT_DOOR);
119*d62bc4baSyz 	return (err);
120*d62bc4baSyz }
121*d62bc4baSyz 
122*d62bc4baSyz static void
123*d62bc4baSyz dlmgmt_door_fini()
124*d62bc4baSyz {
125*d62bc4baSyz 	(void) dlmgmt_set_doorfd(B_FALSE);
126*d62bc4baSyz 	if ((dlmgmt_door_fd != -1) && (door_revoke(dlmgmt_door_fd) == -1)) {
127*d62bc4baSyz 		dlmgmt_log(LOG_WARNING, "door_revoke(%s) failed: %s",
128*d62bc4baSyz 		    dlmgmt_door_file, strerror(errno));
129*d62bc4baSyz 	}
130*d62bc4baSyz 	(void) fdetach(DLMGMT_DOOR);
131*d62bc4baSyz }
132*d62bc4baSyz 
133*d62bc4baSyz static int
134*d62bc4baSyz dlmgmt_init()
135*d62bc4baSyz {
136*d62bc4baSyz 	int err;
137*d62bc4baSyz 
138*d62bc4baSyz 	if ((err = dlmgmt_linktable_init()) != 0)
139*d62bc4baSyz 		return (err);
140*d62bc4baSyz 
141*d62bc4baSyz 	if ((err = dlmgmt_db_init()) != 0 || (err = dlmgmt_door_init()) != 0)
142*d62bc4baSyz 		dlmgmt_linktable_fini();
143*d62bc4baSyz 
144*d62bc4baSyz 	return (err);
145*d62bc4baSyz }
146*d62bc4baSyz 
147*d62bc4baSyz static void
148*d62bc4baSyz dlmgmt_fini()
149*d62bc4baSyz {
150*d62bc4baSyz 	dlmgmt_door_fini();
151*d62bc4baSyz 	dlmgmt_linktable_fini();
152*d62bc4baSyz }
153*d62bc4baSyz 
154*d62bc4baSyz /*
155*d62bc4baSyz  * This is called by the child process to inform the parent process to
156*d62bc4baSyz  * exit with the given return value.
157*d62bc4baSyz  */
158*d62bc4baSyz static void
159*d62bc4baSyz dlmgmt_inform_parent_exit(int rv)
160*d62bc4baSyz {
161*d62bc4baSyz 	if (debug)
162*d62bc4baSyz 		return;
163*d62bc4baSyz 
164*d62bc4baSyz 	if (write(pfds[1], &rv, sizeof (int)) != sizeof (int)) {
165*d62bc4baSyz 		dlmgmt_log(LOG_WARNING,
166*d62bc4baSyz 		    "dlmgmt_inform_parent_exit() failed: %s", strerror(errno));
167*d62bc4baSyz 		(void) close(pfds[1]);
168*d62bc4baSyz 		exit(EXIT_FAILURE);
169*d62bc4baSyz 	}
170*d62bc4baSyz 	(void) close(pfds[1]);
171*d62bc4baSyz }
172*d62bc4baSyz 
173*d62bc4baSyz /*ARGSUSED*/
174*d62bc4baSyz static void
175*d62bc4baSyz dlmgmtd_exit(int signo)
176*d62bc4baSyz {
177*d62bc4baSyz 	(void) close(pfds[1]);
178*d62bc4baSyz 	dlmgmt_fini();
179*d62bc4baSyz 	exit(EXIT_FAILURE);
180*d62bc4baSyz }
181*d62bc4baSyz 
182*d62bc4baSyz static void
183*d62bc4baSyz usage(void)
184*d62bc4baSyz {
185*d62bc4baSyz 	(void) fprintf(stderr, "Usage: %s [-d]\n", progname);
186*d62bc4baSyz 	exit(EXIT_FAILURE);
187*d62bc4baSyz }
188*d62bc4baSyz 
189*d62bc4baSyz static int
190*d62bc4baSyz dlmgmt_setup_privs()
191*d62bc4baSyz {
192*d62bc4baSyz 	priv_set_t *priv_set = NULL;
193*d62bc4baSyz 	char *p;
194*d62bc4baSyz 
195*d62bc4baSyz 	priv_set = priv_allocset();
196*d62bc4baSyz 	if (priv_set == NULL || getppriv(PRIV_PERMITTED, priv_set) == -1) {
197*d62bc4baSyz 		dlmgmt_log(LOG_WARNING, "failed to get the permitted set of "
198*d62bc4baSyz 		    "privileges %s", strerror(errno));
199*d62bc4baSyz 		return (-1);
200*d62bc4baSyz 	}
201*d62bc4baSyz 
202*d62bc4baSyz 	p = priv_set_to_str(priv_set, ',', 0);
203*d62bc4baSyz 	dlmgmt_log(LOG_DEBUG, "start with privs %s", p != NULL ? p : "Unknown");
204*d62bc4baSyz 	free(p);
205*d62bc4baSyz 
206*d62bc4baSyz 	priv_emptyset(priv_set);
207*d62bc4baSyz 	(void) priv_addset(priv_set, "file_dac_write");
208*d62bc4baSyz 	(void) priv_addset(priv_set, "file_chown_self");
209*d62bc4baSyz 	(void) priv_addset(priv_set, "sys_mount");
210*d62bc4baSyz 	(void) priv_addset(priv_set, "sys_net_config");
211*d62bc4baSyz 
212*d62bc4baSyz 	if (setppriv(PRIV_SET, PRIV_INHERITABLE, priv_set) == -1) {
213*d62bc4baSyz 		dlmgmt_log(LOG_WARNING, "failed to set the inheritable set of "
214*d62bc4baSyz 		    "privileges %s", strerror(errno));
215*d62bc4baSyz 		priv_freeset(priv_set);
216*d62bc4baSyz 		return (-1);
217*d62bc4baSyz 	}
218*d62bc4baSyz 
219*d62bc4baSyz 	if (setppriv(PRIV_SET, PRIV_PERMITTED, priv_set) == -1) {
220*d62bc4baSyz 		dlmgmt_log(LOG_WARNING, "failed to set the permitted set of "
221*d62bc4baSyz 		    "privileges %s", strerror(errno));
222*d62bc4baSyz 		priv_freeset(priv_set);
223*d62bc4baSyz 		return (-1);
224*d62bc4baSyz 	}
225*d62bc4baSyz 
226*d62bc4baSyz 	if (setppriv(PRIV_SET, PRIV_EFFECTIVE, priv_set) == -1) {
227*d62bc4baSyz 		dlmgmt_log(LOG_WARNING, "failed to set the effective set of "
228*d62bc4baSyz 		    "privileges %s", strerror(errno));
229*d62bc4baSyz 		priv_freeset(priv_set);
230*d62bc4baSyz 		return (-1);
231*d62bc4baSyz 	}
232*d62bc4baSyz 
233*d62bc4baSyz 	priv_freeset(priv_set);
234*d62bc4baSyz 	return (0);
235*d62bc4baSyz }
236*d62bc4baSyz 
237*d62bc4baSyz /*
238*d62bc4baSyz  * Keep the pfds fd open, close other fds.
239*d62bc4baSyz  */
240*d62bc4baSyz /*ARGSUSED*/
241*d62bc4baSyz static int
242*d62bc4baSyz closefunc(void *arg, int fd)
243*d62bc4baSyz {
244*d62bc4baSyz 	if (fd != pfds[1])
245*d62bc4baSyz 		(void) close(fd);
246*d62bc4baSyz 	return (0);
247*d62bc4baSyz }
248*d62bc4baSyz 
249*d62bc4baSyz static boolean_t
250*d62bc4baSyz dlmgmt_daemonize(void)
251*d62bc4baSyz {
252*d62bc4baSyz 	pid_t pid;
253*d62bc4baSyz 	int rv;
254*d62bc4baSyz 
255*d62bc4baSyz 	if (pipe(pfds) < 0) {
256*d62bc4baSyz 		(void) fprintf(stderr, "%s: pipe() failed: %s\n",
257*d62bc4baSyz 		    progname, strerror(errno));
258*d62bc4baSyz 		exit(EXIT_FAILURE);
259*d62bc4baSyz 	}
260*d62bc4baSyz 
261*d62bc4baSyz 	if ((pid = fork()) == -1) {
262*d62bc4baSyz 		(void) fprintf(stderr, "%s: fork() failed: %s\n",
263*d62bc4baSyz 		    progname, strerror(errno));
264*d62bc4baSyz 		exit(EXIT_FAILURE);
265*d62bc4baSyz 	} else if (pid > 0) { /* Parent */
266*d62bc4baSyz 		(void) close(pfds[1]);
267*d62bc4baSyz 
268*d62bc4baSyz 		/*
269*d62bc4baSyz 		 * Read the child process's return value from the pfds.
270*d62bc4baSyz 		 * If the child process exits unexpected, read() returns -1.
271*d62bc4baSyz 		 */
272*d62bc4baSyz 		if (read(pfds[0], &rv, sizeof (int)) != sizeof (int)) {
273*d62bc4baSyz 			(void) kill(pid, SIGKILL);
274*d62bc4baSyz 			rv = EXIT_FAILURE;
275*d62bc4baSyz 		}
276*d62bc4baSyz 
277*d62bc4baSyz 		(void) close(pfds[0]);
278*d62bc4baSyz 		exit(rv);
279*d62bc4baSyz 	}
280*d62bc4baSyz 
281*d62bc4baSyz 	/* Child */
282*d62bc4baSyz 	(void) close(pfds[0]);
283*d62bc4baSyz 	(void) setsid();
284*d62bc4baSyz 
285*d62bc4baSyz 	/*
286*d62bc4baSyz 	 * Close all files except pfds[1].
287*d62bc4baSyz 	 */
288*d62bc4baSyz 	(void) fdwalk(closefunc, NULL);
289*d62bc4baSyz 	(void) chdir("/");
290*d62bc4baSyz 	openlog(progname, LOG_PID, LOG_DAEMON);
291*d62bc4baSyz 	return (B_TRUE);
292*d62bc4baSyz }
293*d62bc4baSyz 
294*d62bc4baSyz int
295*d62bc4baSyz main(int argc, char *argv[])
296*d62bc4baSyz {
297*d62bc4baSyz 	int opt;
298*d62bc4baSyz 
299*d62bc4baSyz 	progname = strrchr(argv[0], '/');
300*d62bc4baSyz 	if (progname != NULL)
301*d62bc4baSyz 		progname++;
302*d62bc4baSyz 	else
303*d62bc4baSyz 		progname = argv[0];
304*d62bc4baSyz 
305*d62bc4baSyz 	/*
306*d62bc4baSyz 	 * Process options.
307*d62bc4baSyz 	 */
308*d62bc4baSyz 	while ((opt = getopt(argc, argv, "d")) != EOF) {
309*d62bc4baSyz 		switch (opt) {
310*d62bc4baSyz 		case 'd':
311*d62bc4baSyz 			debug = B_TRUE;
312*d62bc4baSyz 			break;
313*d62bc4baSyz 		default:
314*d62bc4baSyz 			usage();
315*d62bc4baSyz 		}
316*d62bc4baSyz 	}
317*d62bc4baSyz 
318*d62bc4baSyz 	if (!debug && !dlmgmt_daemonize())
319*d62bc4baSyz 		return (EXIT_FAILURE);
320*d62bc4baSyz 
321*d62bc4baSyz 	if (signal(SIGTERM, dlmgmtd_exit) == SIG_ERR) {
322*d62bc4baSyz 		dlmgmt_log(LOG_WARNING, "signal() for SIGTERM failed: %s",
323*d62bc4baSyz 		    strerror(errno));
324*d62bc4baSyz 		goto child_out;
325*d62bc4baSyz 	}
326*d62bc4baSyz 
327*d62bc4baSyz 	if (dlmgmt_init() != 0)
328*d62bc4baSyz 		goto child_out;
329*d62bc4baSyz 
330*d62bc4baSyz 	if (dlmgmt_setup_privs() != 0)
331*d62bc4baSyz 		goto child_out;
332*d62bc4baSyz 
333*d62bc4baSyz 	/*
334*d62bc4baSyz 	 * Inform the parent process that it can successfully exit.
335*d62bc4baSyz 	 */
336*d62bc4baSyz 	dlmgmt_inform_parent_exit(EXIT_SUCCESS);
337*d62bc4baSyz 
338*d62bc4baSyz 	for (;;)
339*d62bc4baSyz 		(void) pause();
340*d62bc4baSyz 
341*d62bc4baSyz child_out:
342*d62bc4baSyz 	/* return from main() forcibly exits an MT process */
343*d62bc4baSyz 	dlmgmt_inform_parent_exit(EXIT_FAILURE);
344*d62bc4baSyz 	return (EXIT_FAILURE);
345*d62bc4baSyz }
346