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