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 2010 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * The ipmgmtd daemon is started by ip-interface-management SMF service. This 29 * daemon is used to manage, mapping of 'address object' to 'interface name' and 30 * 'logical interface number', on which the address is created. It also provides 31 * a means to update the ipadm persistent data-store. 32 * 33 * The daemon tracks the <addrobj, lifname> mapping in-memory using a linked 34 * list `aobjmap'. Access to this list is synchronized using a readers-writers 35 * lock. The active <addrobj, lifname> mapping is kept in 36 * /etc/svc/volatile/ipadm/aobjmap.conf cache file, so that the mapping can be 37 * recovered when ipmgmtd exits for some reason (e.g., when ipmgmtd is restarted 38 * using svcadm or accidentally killed). 39 * 40 * Today, the persistent configuration of interfaces, addresses and protocol 41 * properties is kept in /etc/ipadm/ipadm.conf. The access to the persistent 42 * data store is synchronized using reader-writers lock `ipmgmt_dbconf_lock'. 43 * 44 * The communication between the library, libipadm.so and the daemon, is through 45 * doors RPC. The library interacts with the daemon using the commands defined 46 * by `ipmgmt_door_cmd_type_t'. Further any 'write' operation would require 47 * the `NETWORK_INTERFACE_CONFIG_AUTH' authorization. 48 * 49 * On reboot, the aforementioned SMF service starts the daemon before any other 50 * networking service that configures network IP interfaces is started. 51 * Afterwards, the network/physical SMF script instantiates the persisted 52 * network interfaces, interface properties and addresses. 53 */ 54 55 #include <errno.h> 56 #include <fcntl.h> 57 #include <priv_utils.h> 58 #include <signal.h> 59 #include <stdlib.h> 60 #include <stdio.h> 61 #include <strings.h> 62 #include <sys/param.h> 63 #include <sys/stat.h> 64 #include <unistd.h> 65 #include "ipmgmt_impl.h" 66 67 const char *progname; 68 69 /* readers-writers lock for reading/writing daemon data store */ 70 pthread_rwlock_t ipmgmt_dbconf_lock; 71 72 /* tracks address object to {ifname|logical number|interface id} mapping */ 73 ipmgmt_aobjmap_list_t aobjmap; 74 75 /* used to communicate failure to parent process, which spawned the daemon */ 76 static int pfds[2]; 77 78 /* file descriptor to IPMGMT_DOOR */ 79 static int ipmgmt_door_fd = -1; 80 81 static void ipmgmt_exit(int); 82 static int ipmgmt_init(); 83 static int ipmgmt_init_privileges(); 84 85 static int 86 ipmgmt_db_init() 87 { 88 int fd, err; 89 90 /* creates the address object data store, if it doesn't exist */ 91 if ((fd = open(ADDROBJ_MAPPING_DB_FILE, O_CREAT|O_RDONLY, 92 IPADM_FILE_MODE)) == -1) { 93 err = errno; 94 ipmgmt_log(LOG_ERR, "could not open %s: %s", 95 ADDROBJ_MAPPING_DB_FILE, strerror(err)); 96 return (err); 97 } 98 (void) close(fd); 99 100 aobjmap.aobjmap_head = NULL; 101 (void) pthread_rwlock_init(&aobjmap.aobjmap_rwlock, NULL); 102 103 /* 104 * If the daemon is recovering from a crash or restart, read the 105 * address object to logical interface mapping and build an in-memory 106 * representation of the mapping. That is, build `aobjmap' structure 107 * from address object data store. 108 */ 109 if ((err = ipadm_rw_db(ipmgmt_aobjmap_init, NULL, 110 ADDROBJ_MAPPING_DB_FILE, 0, IPADM_DB_READ)) != 0) { 111 /* if there was nothing to initialize, it's fine */ 112 if (err != ENOENT) 113 return (err); 114 err = 0; 115 } 116 117 (void) pthread_rwlock_init(&ipmgmt_dbconf_lock, NULL); 118 return (err); 119 } 120 121 static int 122 ipmgmt_door_init() 123 { 124 int fd; 125 int err; 126 127 /* create the door file for ipmgmtd */ 128 if ((fd = open(IPMGMT_DOOR, O_CREAT|O_RDONLY, IPADM_FILE_MODE)) == -1) { 129 err = errno; 130 ipmgmt_log(LOG_ERR, "could not open %s: %s", 131 IPMGMT_DOOR, strerror(err)); 132 return (err); 133 } 134 (void) close(fd); 135 136 if ((ipmgmt_door_fd = door_create(ipmgmt_handler, NULL, 137 DOOR_REFUSE_DESC | DOOR_NO_CANCEL)) == -1) { 138 err = errno; 139 ipmgmt_log(LOG_ERR, "failed to create door: %s", strerror(err)); 140 return (err); 141 } 142 /* 143 * fdetach first in case a previous daemon instance exited 144 * ungracefully. 145 */ 146 (void) fdetach(IPMGMT_DOOR); 147 if (fattach(ipmgmt_door_fd, IPMGMT_DOOR) != 0) { 148 err = errno; 149 ipmgmt_log(LOG_ERR, "failed to attach door to %s: %s", 150 IPMGMT_DOOR, strerror(err)); 151 goto fail; 152 } 153 return (0); 154 fail: 155 (void) door_revoke(ipmgmt_door_fd); 156 ipmgmt_door_fd = -1; 157 return (err); 158 } 159 160 static void 161 ipmgmt_door_fini() 162 { 163 if (ipmgmt_door_fd == -1) 164 return; 165 166 (void) fdetach(IPMGMT_DOOR); 167 if (door_revoke(ipmgmt_door_fd) == -1) { 168 ipmgmt_log(LOG_ERR, "failed to revoke access to door %s: %s", 169 IPMGMT_DOOR, strerror(errno)); 170 } 171 } 172 173 static int 174 ipmgmt_init() 175 { 176 int err; 177 178 if (signal(SIGTERM, ipmgmt_exit) == SIG_ERR || 179 signal(SIGINT, ipmgmt_exit) == SIG_ERR) { 180 err = errno; 181 ipmgmt_log(LOG_ERR, "signal() for SIGTERM/INT failed: %s", 182 strerror(err)); 183 return (err); 184 } 185 if ((err = ipmgmt_db_init()) != 0 || (err = ipmgmt_door_init()) != 0) 186 return (err); 187 return (0); 188 } 189 190 /* 191 * This is called by the child process to inform the parent process to 192 * exit with the given return value. 193 */ 194 static void 195 ipmgmt_inform_parent_exit(int rv) 196 { 197 if (write(pfds[1], &rv, sizeof (int)) != sizeof (int)) { 198 ipmgmt_log(LOG_WARNING, 199 "failed to inform parent process of status: %s", 200 strerror(errno)); 201 (void) close(pfds[1]); 202 exit(EXIT_FAILURE); 203 } 204 (void) close(pfds[1]); 205 } 206 207 /*ARGSUSED*/ 208 static void 209 ipmgmt_exit(int signo) 210 { 211 (void) close(pfds[1]); 212 ipmgmt_door_fini(); 213 exit(EXIT_FAILURE); 214 } 215 216 /* 217 * Set the uid of this daemon to the "ipadm" user. Finish the following 218 * operations before setuid() because they need root privileges: 219 * 220 * - create the /etc/svc/volatile/ipadm directory; 221 * - change its uid/gid to "ipadm"/"sys"; 222 */ 223 static int 224 ipmgmt_init_privileges() 225 { 226 struct stat statbuf; 227 int err; 228 229 /* create the IPADM_TMPFS_DIR directory */ 230 if (stat(IPADM_TMPFS_DIR, &statbuf) < 0) { 231 if (mkdir(IPADM_TMPFS_DIR, (mode_t)0755) < 0) { 232 err = errno; 233 goto fail; 234 } 235 } else { 236 if ((statbuf.st_mode & S_IFMT) != S_IFDIR) { 237 err = ENOTDIR; 238 goto fail; 239 } 240 } 241 242 if ((chmod(IPADM_TMPFS_DIR, 0755) < 0) || 243 (chown(IPADM_TMPFS_DIR, UID_NETADM, GID_NETADM) < 0)) { 244 err = errno; 245 goto fail; 246 } 247 248 /* 249 * limit the privileges of this daemon and set the uid of this 250 * daemon to UID_NETADM 251 */ 252 if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, UID_NETADM, 253 GID_NETADM, NULL) == -1) { 254 err = EPERM; 255 goto fail; 256 } 257 258 return (0); 259 fail: 260 (void) ipmgmt_log(LOG_ERR, "failed to initialize the daemon: %s", 261 strerror(err)); 262 return (err); 263 } 264 265 /* 266 * Keep the pfds fd open, close other fds. 267 */ 268 /*ARGSUSED*/ 269 static int 270 closefunc(void *arg, int fd) 271 { 272 if (fd != pfds[1]) 273 (void) close(fd); 274 return (0); 275 } 276 277 /* 278 * We cannot use libc's daemon() because the door we create is associated with 279 * the process ID. If we create the door before the call to daemon(), it will 280 * be associated with the parent and it's incorrect. On the other hand if we 281 * create the door later, after the call to daemon(), parent process exits 282 * early and gives a false notion to SMF that 'ipmgmtd' is up and running, 283 * which is incorrect. So, we have our own daemon() equivalent. 284 */ 285 static boolean_t 286 ipmgmt_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 * Parent should not exit early, it should wait for the child 306 * to return Success/Failure. If the parent exits early, then 307 * SMF will think 'ipmgmtd' is up and would start all the 308 * depended services. 309 * 310 * If the child process exits unexpectedly, read() returns -1. 311 */ 312 if (read(pfds[0], &rv, sizeof (int)) != sizeof (int)) { 313 (void) kill(pid, SIGKILL); 314 rv = EXIT_FAILURE; 315 } 316 317 (void) close(pfds[0]); 318 exit(rv); 319 } 320 321 /* Child */ 322 (void) close(pfds[0]); 323 (void) setsid(); 324 325 /* close all files except pfds[1] */ 326 (void) fdwalk(closefunc, NULL); 327 (void) chdir("/"); 328 openlog(progname, LOG_PID, LOG_DAEMON); 329 return (B_TRUE); 330 } 331 332 int 333 main(int argc, char *argv[]) 334 { 335 int opt; 336 boolean_t fg = B_FALSE; 337 338 progname = strrchr(argv[0], '/'); 339 if (progname != NULL) 340 progname++; 341 else 342 progname = argv[0]; 343 344 /* Process options */ 345 while ((opt = getopt(argc, argv, "f")) != EOF) { 346 switch (opt) { 347 case 'f': 348 fg = B_TRUE; 349 break; 350 default: 351 (void) fprintf(stderr, "Usage: %s [-f]\n", progname); 352 return (EXIT_FAILURE); 353 } 354 } 355 356 if (!fg && getenv("SMF_FMRI") == NULL) { 357 (void) fprintf(stderr, 358 "ipmgmtd is a smf(5) managed service and cannot be run " 359 "from the command line.\n"); 360 return (EINVAL); 361 } 362 363 if (!fg && !ipmgmt_daemonize()) 364 return (EXIT_FAILURE); 365 366 if (ipmgmt_init_privileges() != 0) 367 goto child_out; 368 369 if (ipmgmt_init() != 0) 370 goto child_out; 371 372 /* Inform the parent process that it can successfully exit */ 373 ipmgmt_inform_parent_exit(EXIT_SUCCESS); 374 375 for (;;) 376 (void) pause(); 377 378 child_out: 379 /* return from main() forcibly exits an MT process */ 380 ipmgmt_inform_parent_exit(EXIT_FAILURE); 381 return (EXIT_FAILURE); 382 } 383