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