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