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.
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.
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]
23 * Copyright 2010 Sun Microsystems, Inc. All rights reserved.
24 * Use is subject to license terms.
28 * The dlmgmtd daemon is started by the datalink-management SMF service.
29 * This daemon is used to manage <link name, linkid> mapping and the
30 * persistent datalink configuration.
32 * Today, the <link name, linkid> mapping and the persistent configuration
33 * of datalinks is kept in /etc/dladm/datalink.conf, and the daemon keeps
34 * a copy of the datalinks in the memory (see dlmgmt_id_avl and
35 * dlmgmt_name_avl). The active <link name, linkid> mapping is kept in
36 * /etc/svc/volatile/dladm cache file, so that the mapping can be recovered
37 * when dlmgmtd exits for some reason (e.g., when dlmgmtd is accidentally
52 #include <sys/dld_ioc.h>
53 #include <sys/param.h>
56 #include <libdladm_impl.h>
57 #include <libdlmgmt.h>
58 #include "dlmgmt_impl.h"
64 * This file descriptor to DLMGMT_DOOR cannot be in the libdladm
65 * handle because the door isn't created when the handle is created.
67 static int dlmgmt_door_fd
= -1;
70 * This libdladm handle is global so that dlmgmt_upcall_linkprop_init() can
71 * pass to libdladm. The handle is opened with "ALL" privileges, before
72 * privileges are dropped in dlmgmt_drop_privileges(). It is not able to open
73 * DLMGMT_DOOR at that time as it hasn't been created yet. This door in the
74 * handle is opened in the first call to dladm_door_fd().
76 dladm_handle_t dld_handle
= NULL
;
78 static void dlmgmtd_exit(int);
79 static int dlmgmt_init();
80 static void dlmgmt_fini();
81 static int dlmgmt_set_privileges();
84 dlmgmt_set_doorfd(boolean_t start
)
89 assert(dld_handle
!= NULL
);
91 did
.did_start_door
= start
;
93 if (ioctl(dladm_dld_fd(dld_handle
), DLDIOC_DOORSERVER
, &did
) == -1)
100 dlmgmt_door_init(void)
104 if ((dlmgmt_door_fd
= door_create(dlmgmt_handler
, NULL
,
105 DOOR_REFUSE_DESC
| DOOR_NO_CANCEL
)) == -1) {
107 dlmgmt_log(LOG_ERR
, "door_create() failed: %s",
115 dlmgmt_door_fini(void)
117 if (dlmgmt_door_fd
== -1)
120 if (door_revoke(dlmgmt_door_fd
) == -1) {
121 dlmgmt_log(LOG_WARNING
, "door_revoke(%s) failed: %s",
122 DLMGMT_DOOR
, strerror(errno
));
124 (void) dlmgmt_set_doorfd(B_FALSE
);
129 dlmgmt_door_attach(zoneid_t zoneid
, char *rootdir
)
133 char doorpath
[MAXPATHLEN
];
135 (void) snprintf(doorpath
, sizeof (doorpath
), "%s%s", rootdir
,
139 * Create the door file for dlmgmtd.
141 if ((fd
= open(doorpath
, O_CREAT
|O_RDONLY
, 0644)) == -1) {
143 dlmgmt_log(LOG_ERR
, "open(%s) failed: %s", doorpath
,
148 if (chown(doorpath
, UID_DLADM
, GID_NETADM
) == -1)
152 * fdetach first in case a previous daemon instance exited
155 (void) fdetach(doorpath
);
156 if (fattach(dlmgmt_door_fd
, doorpath
) != 0) {
158 dlmgmt_log(LOG_ERR
, "fattach(%s) failed: %s", doorpath
,
160 } else if (zoneid
== GLOBAL_ZONEID
) {
161 if ((err
= dlmgmt_set_doorfd(B_TRUE
)) != 0) {
162 dlmgmt_log(LOG_ERR
, "cannot set kernel doorfd: %s",
171 * Create the /etc/svc/volatile/dladm/ directory if it doesn't exist, load the
172 * datalink.conf data for this zone, and create/attach the door rendezvous
176 dlmgmt_zone_init(zoneid_t zoneid
)
178 char rootdir
[MAXPATHLEN
], tmpfsdir
[MAXPATHLEN
];
182 if (zoneid
== GLOBAL_ZONEID
) {
184 } else if (zone_getattr(zoneid
, ZONE_ATTR_ROOT
, rootdir
,
185 sizeof (rootdir
)) < 0) {
190 * Create the DLMGMT_TMPFS_DIR directory.
192 (void) snprintf(tmpfsdir
, sizeof (tmpfsdir
), "%s%s", rootdir
,
194 if (stat(tmpfsdir
, &statbuf
) < 0) {
195 if (mkdir(tmpfsdir
, (mode_t
)0755) < 0)
197 } else if ((statbuf
.st_mode
& S_IFMT
) != S_IFDIR
) {
201 if ((chmod(tmpfsdir
, 0755) < 0) ||
202 (chown(tmpfsdir
, UID_DLADM
, GID_NETADM
) < 0)) {
206 if ((err
= dlmgmt_db_init(zoneid
)) != 0)
208 return (dlmgmt_door_attach(zoneid
, rootdir
));
212 * Initialize each running zone.
215 dlmgmt_allzones_init(void)
218 zoneid_t
*zids
= NULL
;
219 uint_t nzids
, nzids_saved
;
221 if (zone_list(NULL
, &nzids
) != 0)
225 if ((zids
= malloc(nzids
* sizeof (zoneid_t
))) == NULL
)
228 if (zone_list(zids
, &nzids
) != 0) {
232 if (nzids
> nzids_saved
) {
237 for (i
= 0; i
< nzids
; i
++) {
238 if ((err
= dlmgmt_zone_init(zids
[i
])) != 0)
250 char filename
[MAXPATHLEN
];
252 if (dladm_open(&dld_handle
) != DLADM_STATUS_OK
) {
253 dlmgmt_log(LOG_ERR
, "dladm_open() failed");
257 if (signal(SIGTERM
, dlmgmtd_exit
) == SIG_ERR
||
258 signal(SIGINT
, dlmgmtd_exit
) == SIG_ERR
) {
260 dlmgmt_log(LOG_ERR
, "signal() for SIGTERM/INT failed: %s",
266 * First derive the name of the cache file from the FMRI name. This
267 * cache name is used to keep active datalink configuration.
270 (void) snprintf(cachefile
, MAXPATHLEN
, "%s/%s%s",
271 DLMGMT_TMPFS_DIR
, progname
, ".debug.cache");
273 if ((fmri
= getenv("SMF_FMRI")) == NULL
) {
274 dlmgmt_log(LOG_ERR
, "dlmgmtd is an smf(5) managed "
275 "service and should not be run from the command "
281 * The FMRI name is in the form of
282 * svc:/service/service:instance. We need to remove the
283 * prefix "svc:/" and replace '/' with '-'. The cache file
284 * name is in the form of "service:instance.cache".
286 if ((c
= strchr(fmri
, '/')) != NULL
)
290 (void) snprintf(filename
, MAXPATHLEN
, "%s.cache", c
);
292 while ((c
= strchr(c
, '/')) != NULL
)
295 (void) snprintf(cachefile
, MAXPATHLEN
, "%s/%s",
296 DLMGMT_TMPFS_DIR
, filename
);
299 dlmgmt_linktable_init();
300 if ((err
= dlmgmt_door_init()) != 0)
304 * Load datalink configuration and create dlmgmtd door files for all
305 * currently running zones.
307 if ((err
= dlmgmt_allzones_init()) != 0)
312 dlmgmt_linktable_fini();
320 dlmgmt_linktable_fini();
321 if (dld_handle
!= NULL
) {
322 dladm_close(dld_handle
);
328 * This is called by the child process to inform the parent process to
329 * exit with the given return value.
332 dlmgmt_inform_parent_exit(int rv
)
337 if (write(pfds
[1], &rv
, sizeof (int)) != sizeof (int)) {
338 dlmgmt_log(LOG_WARNING
,
339 "dlmgmt_inform_parent_exit() failed: %s", strerror(errno
));
340 (void) close(pfds
[1]);
343 (void) close(pfds
[1]);
348 dlmgmtd_exit(int signo
)
350 (void) close(pfds
[1]);
358 (void) fprintf(stderr
, "Usage: %s [-d]\n", progname
);
363 * Restrict privileges to only those needed.
366 dlmgmt_drop_privileges(void)
370 zoneid_t zoneid
= getzoneid();
373 if ((pset
= priv_allocset()) == NULL
)
377 * The global zone needs PRIV_PROC_FORK so that it can fork() when it
378 * issues db ops in non-global zones, PRIV_SYS_CONFIG to post
379 * sysevents, and PRIV_SYS_DL_CONFIG to initialize link properties in
380 * dlmgmt_upcall_linkprop_init().
382 * We remove non-basic privileges from the permitted (and thus
383 * effective) set. When executing in a non-global zone, dlmgmtd
384 * only needs to read and write to files that it already owns.
387 (void) priv_delset(pset
, PRIV_PROC_EXEC
);
388 (void) priv_delset(pset
, PRIV_PROC_INFO
);
389 (void) priv_delset(pset
, PRIV_PROC_SESSION
);
390 (void) priv_delset(pset
, PRIV_FILE_LINK_ANY
);
391 if (zoneid
== GLOBAL_ZONEID
) {
392 ptype
= PRIV_EFFECTIVE
;
393 if (priv_addset(pset
, PRIV_SYS_CONFIG
) == -1 ||
394 priv_addset(pset
, PRIV_SYS_DL_CONFIG
) == -1)
397 (void) priv_delset(pset
, PRIV_PROC_FORK
);
398 ptype
= PRIV_PERMITTED
;
400 if (err
== 0 && setppriv(PRIV_SET
, ptype
, pset
) == -1)
408 dlmgmt_elevate_privileges(void)
413 if ((privset
= priv_str_to_set("zone", ",", NULL
)) == NULL
)
415 if (setppriv(PRIV_SET
, PRIV_EFFECTIVE
, privset
) == -1)
417 priv_freeset(privset
);
422 * Set the uid of this daemon to the "dladm" user and drop privileges to only
426 dlmgmt_set_privileges(void)
430 (void) setgroups(0, NULL
);
431 if (setegid(GID_NETADM
) == -1 || seteuid(UID_DLADM
) == -1)
434 err
= dlmgmt_drop_privileges();
440 * Keep the pfds fd open, close other fds.
444 closefunc(void *arg
, int fd
)
452 dlmgmt_daemonize(void)
457 if (pipe(pfds
) < 0) {
458 (void) fprintf(stderr
, "%s: pipe() failed: %s\n",
459 progname
, strerror(errno
));
463 if ((pid
= fork()) == -1) {
464 (void) fprintf(stderr
, "%s: fork() failed: %s\n",
465 progname
, strerror(errno
));
467 } else if (pid
> 0) { /* Parent */
468 (void) close(pfds
[1]);
471 * Read the child process's return value from the pfds.
472 * If the child process exits unexpected, read() returns -1.
474 if (read(pfds
[0], &rv
, sizeof (int)) != sizeof (int)) {
475 (void) kill(pid
, SIGKILL
);
479 (void) close(pfds
[0]);
484 (void) close(pfds
[0]);
488 * Close all files except pfds[1].
490 (void) fdwalk(closefunc
, NULL
);
492 openlog(progname
, LOG_PID
, LOG_DAEMON
);
497 main(int argc
, char *argv
[])
501 progname
= strrchr(argv
[0], '/');
502 if (progname
!= NULL
)
510 while ((opt
= getopt(argc
, argv
, "d")) != EOF
) {
520 if (!debug
&& !dlmgmt_daemonize())
521 return (EXIT_FAILURE
);
523 if ((err
= dlmgmt_init()) != 0) {
524 dlmgmt_log(LOG_ERR
, "unable to initialize daemon: %s",
527 } else if ((err
= dlmgmt_set_privileges()) != 0) {
528 dlmgmt_log(LOG_ERR
, "unable to set daemon privileges: %s",
535 * Inform the parent process that it can successfully exit.
537 dlmgmt_inform_parent_exit(EXIT_SUCCESS
);
543 /* return from main() forcibly exits an MT process */
544 dlmgmt_inform_parent_exit(EXIT_FAILURE
);
545 return (EXIT_FAILURE
);