1*a547be5dSGordon Ross /* 2*a547be5dSGordon Ross * CDDL HEADER START 3*a547be5dSGordon Ross * 4*a547be5dSGordon Ross * The contents of this file are subject to the terms of the 5*a547be5dSGordon Ross * Common Development and Distribution License (the "License"). 6*a547be5dSGordon Ross * You may not use this file except in compliance with the License. 7*a547be5dSGordon Ross * 8*a547be5dSGordon Ross * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9*a547be5dSGordon Ross * or http://www.opensolaris.org/os/licensing. 10*a547be5dSGordon Ross * See the License for the specific language governing permissions 11*a547be5dSGordon Ross * and limitations under the License. 12*a547be5dSGordon Ross * 13*a547be5dSGordon Ross * When distributing Covered Code, include this CDDL HEADER in each 14*a547be5dSGordon Ross * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15*a547be5dSGordon Ross * If applicable, add the following below this CDDL HEADER, with the 16*a547be5dSGordon Ross * fields enclosed by brackets "[]" replaced with your own identifying 17*a547be5dSGordon Ross * information: Portions Copyright [yyyy] [name of copyright owner] 18*a547be5dSGordon Ross * 19*a547be5dSGordon Ross * CDDL HEADER END 20*a547be5dSGordon Ross */ 21*a547be5dSGordon Ross 22*a547be5dSGordon Ross /* 23*a547be5dSGordon Ross * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved. 24*a547be5dSGordon Ross */ 25*a547be5dSGordon Ross 26*a547be5dSGordon Ross /* 27*a547be5dSGordon Ross * SMBFS I/O Daemon (SMF service) 28*a547be5dSGordon Ross */ 29*a547be5dSGordon Ross 30*a547be5dSGordon Ross #include <sys/types.h> 31*a547be5dSGordon Ross #include <sys/stat.h> 32*a547be5dSGordon Ross #include <sys/note.h> 33*a547be5dSGordon Ross #include <sys/queue.h> 34*a547be5dSGordon Ross 35*a547be5dSGordon Ross #include <errno.h> 36*a547be5dSGordon Ross #include <fcntl.h> 37*a547be5dSGordon Ross #include <signal.h> 38*a547be5dSGordon Ross #include <stdarg.h> 39*a547be5dSGordon Ross #include <stdio.h> 40*a547be5dSGordon Ross #include <string.h> 41*a547be5dSGordon Ross #include <strings.h> 42*a547be5dSGordon Ross #include <stdlib.h> 43*a547be5dSGordon Ross #include <synch.h> 44*a547be5dSGordon Ross #include <time.h> 45*a547be5dSGordon Ross #include <unistd.h> 46*a547be5dSGordon Ross #include <ucred.h> 47*a547be5dSGordon Ross #include <wait.h> 48*a547be5dSGordon Ross #include <priv_utils.h> 49*a547be5dSGordon Ross #include <err.h> 50*a547be5dSGordon Ross #include <door.h> 51*a547be5dSGordon Ross #include <libscf.h> 52*a547be5dSGordon Ross #include <locale.h> 53*a547be5dSGordon Ross #include <thread.h> 54*a547be5dSGordon Ross #include <assert.h> 55*a547be5dSGordon Ross 56*a547be5dSGordon Ross #include <netsmb/smb_lib.h> 57*a547be5dSGordon Ross 58*a547be5dSGordon Ross static boolean_t d_flag = B_FALSE; 59*a547be5dSGordon Ross 60*a547be5dSGordon Ross /* Keep a list of child processes. */ 61*a547be5dSGordon Ross typedef struct _child { 62*a547be5dSGordon Ross LIST_ENTRY(_child) list; 63*a547be5dSGordon Ross pid_t pid; 64*a547be5dSGordon Ross uid_t uid; 65*a547be5dSGordon Ross } child_t; 66*a547be5dSGordon Ross static LIST_HEAD(, _child) child_list = { 0 }; 67*a547be5dSGordon Ross mutex_t cl_mutex = DEFAULTMUTEX; 68*a547be5dSGordon Ross 69*a547be5dSGordon Ross static const char smbiod_path[] = "/usr/lib/smbfs/smbiod"; 70*a547be5dSGordon Ross static const char door_path[] = SMBIOD_SVC_DOOR; 71*a547be5dSGordon Ross 72*a547be5dSGordon Ross void svc_dispatch(void *cookie, char *argp, size_t argsz, 73*a547be5dSGordon Ross door_desc_t *dp, uint_t n_desc); 74*a547be5dSGordon Ross static int cmd_start(uid_t uid, gid_t gid); 75*a547be5dSGordon Ross static int new_child(uid_t uid, gid_t gid); 76*a547be5dSGordon Ross static void svc_sigchld(void); 77*a547be5dSGordon Ross static void child_gone(uid_t, pid_t, int); 78*a547be5dSGordon Ross static void svc_cleanup(void); 79*a547be5dSGordon Ross 80*a547be5dSGordon Ross static child_t * 81*a547be5dSGordon Ross child_find_by_pid(pid_t pid) 82*a547be5dSGordon Ross { 83*a547be5dSGordon Ross child_t *cp; 84*a547be5dSGordon Ross 85*a547be5dSGordon Ross assert(MUTEX_HELD(&cl_mutex)); 86*a547be5dSGordon Ross LIST_FOREACH(cp, &child_list, list) { 87*a547be5dSGordon Ross if (cp->pid == pid) 88*a547be5dSGordon Ross return (cp); 89*a547be5dSGordon Ross } 90*a547be5dSGordon Ross return (NULL); 91*a547be5dSGordon Ross } 92*a547be5dSGordon Ross 93*a547be5dSGordon Ross static child_t * 94*a547be5dSGordon Ross child_find_by_uid(uid_t uid) 95*a547be5dSGordon Ross { 96*a547be5dSGordon Ross child_t *cp; 97*a547be5dSGordon Ross 98*a547be5dSGordon Ross assert(MUTEX_HELD(&cl_mutex)); 99*a547be5dSGordon Ross LIST_FOREACH(cp, &child_list, list) { 100*a547be5dSGordon Ross if (cp->uid == uid) 101*a547be5dSGordon Ross return (cp); 102*a547be5dSGordon Ross } 103*a547be5dSGordon Ross return (NULL); 104*a547be5dSGordon Ross } 105*a547be5dSGordon Ross 106*a547be5dSGordon Ross /* 107*a547be5dSGordon Ross * Find out if the service is already running. 108*a547be5dSGordon Ross * Return: true, false. 109*a547be5dSGordon Ross */ 110*a547be5dSGordon Ross static boolean_t 111*a547be5dSGordon Ross already_running(void) 112*a547be5dSGordon Ross { 113*a547be5dSGordon Ross door_info_t info; 114*a547be5dSGordon Ross int fd, rc; 115*a547be5dSGordon Ross 116*a547be5dSGordon Ross if ((fd = open(door_path, O_RDONLY)) < 0) 117*a547be5dSGordon Ross return (B_FALSE); 118*a547be5dSGordon Ross 119*a547be5dSGordon Ross rc = door_info(fd, &info); 120*a547be5dSGordon Ross close(fd); 121*a547be5dSGordon Ross if (rc < 0) 122*a547be5dSGordon Ross return (B_FALSE); 123*a547be5dSGordon Ross 124*a547be5dSGordon Ross return (B_TRUE); 125*a547be5dSGordon Ross } 126*a547be5dSGordon Ross 127*a547be5dSGordon Ross /* 128*a547be5dSGordon Ross * This function will fork off a child process, 129*a547be5dSGordon Ross * from which only the child will return. 130*a547be5dSGordon Ross * 131*a547be5dSGordon Ross * The parent exit status is taken as the SMF start method 132*a547be5dSGordon Ross * success or failure, so the parent waits (via pipe read) 133*a547be5dSGordon Ross * for the child to finish initialization before it exits. 134*a547be5dSGordon Ross * Use SMF error codes only on exit. 135*a547be5dSGordon Ross */ 136*a547be5dSGordon Ross static int 137*a547be5dSGordon Ross daemonize_init(void) 138*a547be5dSGordon Ross { 139*a547be5dSGordon Ross int pid, st; 140*a547be5dSGordon Ross int pfds[2]; 141*a547be5dSGordon Ross 142*a547be5dSGordon Ross chdir("/"); 143*a547be5dSGordon Ross 144*a547be5dSGordon Ross if (pipe(pfds) < 0) { 145*a547be5dSGordon Ross perror("pipe"); 146*a547be5dSGordon Ross exit(SMF_EXIT_ERR_FATAL); 147*a547be5dSGordon Ross } 148*a547be5dSGordon Ross if ((pid = fork1()) == -1) { 149*a547be5dSGordon Ross perror("fork"); 150*a547be5dSGordon Ross exit(SMF_EXIT_ERR_FATAL); 151*a547be5dSGordon Ross } 152*a547be5dSGordon Ross 153*a547be5dSGordon Ross /* 154*a547be5dSGordon Ross * If we're the parent process, wait for either the child to send us 155*a547be5dSGordon Ross * the appropriate exit status over the pipe or for the read to fail 156*a547be5dSGordon Ross * (presumably with 0 for EOF if our child terminated abnormally). 157*a547be5dSGordon Ross * If the read fails, exit with either the child's exit status if it 158*a547be5dSGordon Ross * exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal. 159*a547be5dSGordon Ross */ 160*a547be5dSGordon Ross if (pid != 0) { 161*a547be5dSGordon Ross /* parent */ 162*a547be5dSGordon Ross close(pfds[1]); 163*a547be5dSGordon Ross if (read(pfds[0], &st, sizeof (st)) == sizeof (st)) 164*a547be5dSGordon Ross _exit(st); 165*a547be5dSGordon Ross if (waitpid(pid, &st, 0) == pid && WIFEXITED(st)) 166*a547be5dSGordon Ross _exit(WEXITSTATUS(st)); 167*a547be5dSGordon Ross _exit(SMF_EXIT_ERR_FATAL); 168*a547be5dSGordon Ross } 169*a547be5dSGordon Ross 170*a547be5dSGordon Ross /* child */ 171*a547be5dSGordon Ross close(pfds[0]); 172*a547be5dSGordon Ross 173*a547be5dSGordon Ross return (pfds[1]); 174*a547be5dSGordon Ross } 175*a547be5dSGordon Ross 176*a547be5dSGordon Ross static void 177*a547be5dSGordon Ross daemonize_fini(int pfd, int rc) 178*a547be5dSGordon Ross { 179*a547be5dSGordon Ross /* Tell parent we're ready. */ 180*a547be5dSGordon Ross (void) write(pfd, &rc, sizeof (rc)); 181*a547be5dSGordon Ross close(pfd); 182*a547be5dSGordon Ross } 183*a547be5dSGordon Ross 184*a547be5dSGordon Ross int 185*a547be5dSGordon Ross main(int argc, char **argv) 186*a547be5dSGordon Ross { 187*a547be5dSGordon Ross sigset_t oldmask, tmpmask; 188*a547be5dSGordon Ross struct sigaction sa; 189*a547be5dSGordon Ross struct rlimit rl; 190*a547be5dSGordon Ross int door_fd = -1, tmp_fd = -1, pfd = -1; 191*a547be5dSGordon Ross int c, sig; 192*a547be5dSGordon Ross int rc = SMF_EXIT_ERR_FATAL; 193*a547be5dSGordon Ross boolean_t created = B_FALSE, attached = B_FALSE; 194*a547be5dSGordon Ross 195*a547be5dSGordon Ross /* set locale and text domain for i18n */ 196*a547be5dSGordon Ross (void) setlocale(LC_ALL, ""); 197*a547be5dSGordon Ross (void) textdomain(TEXT_DOMAIN); 198*a547be5dSGordon Ross 199*a547be5dSGordon Ross while ((c = getopt(argc, argv, "d")) != -1) { 200*a547be5dSGordon Ross switch (c) { 201*a547be5dSGordon Ross case 'd': 202*a547be5dSGordon Ross /* Do debug messages. */ 203*a547be5dSGordon Ross d_flag = B_TRUE; 204*a547be5dSGordon Ross break; 205*a547be5dSGordon Ross default: 206*a547be5dSGordon Ross fprintf(stderr, "Usage: %s [-d]\n", argv[0]); 207*a547be5dSGordon Ross return (SMF_EXIT_ERR_CONFIG); 208*a547be5dSGordon Ross } 209*a547be5dSGordon Ross } 210*a547be5dSGordon Ross 211*a547be5dSGordon Ross if (already_running()) { 212*a547be5dSGordon Ross fprintf(stderr, "%s: already running", argv[0]); 213*a547be5dSGordon Ross return (rc); 214*a547be5dSGordon Ross } 215*a547be5dSGordon Ross 216*a547be5dSGordon Ross /* 217*a547be5dSGordon Ross * Raise the fd limit to max 218*a547be5dSGordon Ross * errors here are non-fatal 219*a547be5dSGordon Ross */ 220*a547be5dSGordon Ross if (getrlimit(RLIMIT_NOFILE, &rl) != 0) { 221*a547be5dSGordon Ross fprintf(stderr, "getrlimit failed, err %d\n", errno); 222*a547be5dSGordon Ross } else if (rl.rlim_cur < rl.rlim_max) { 223*a547be5dSGordon Ross rl.rlim_cur = rl.rlim_max; 224*a547be5dSGordon Ross if (setrlimit(RLIMIT_NOFILE, &rl) != 0) 225*a547be5dSGordon Ross fprintf(stderr, "setrlimit " 226*a547be5dSGordon Ross "RLIMIT_NOFILE %d, err %d", 227*a547be5dSGordon Ross (int)rl.rlim_cur, errno); 228*a547be5dSGordon Ross } 229*a547be5dSGordon Ross 230*a547be5dSGordon Ross /* 231*a547be5dSGordon Ross * Want all signals blocked, as we're doing 232*a547be5dSGordon Ross * synchronous delivery via sigwait below. 233*a547be5dSGordon Ross */ 234*a547be5dSGordon Ross sigfillset(&tmpmask); 235*a547be5dSGordon Ross sigprocmask(SIG_BLOCK, &tmpmask, &oldmask); 236*a547be5dSGordon Ross 237*a547be5dSGordon Ross /* 238*a547be5dSGordon Ross * Do want SIGCHLD, and will waitpid(). 239*a547be5dSGordon Ross */ 240*a547be5dSGordon Ross sa.sa_flags = SA_NOCLDSTOP; 241*a547be5dSGordon Ross sa.sa_handler = SIG_DFL; 242*a547be5dSGordon Ross sigemptyset(&sa.sa_mask); 243*a547be5dSGordon Ross sigaction(SIGCHLD, &sa, NULL); 244*a547be5dSGordon Ross 245*a547be5dSGordon Ross /* 246*a547be5dSGordon Ross * Daemonize, unless debugging. 247*a547be5dSGordon Ross */ 248*a547be5dSGordon Ross if (d_flag) { 249*a547be5dSGordon Ross /* debug: run in foregound (not a service) */ 250*a547be5dSGordon Ross putenv("SMBFS_DEBUG=1"); 251*a547be5dSGordon Ross } else { 252*a547be5dSGordon Ross /* Non-debug: start daemon in the background. */ 253*a547be5dSGordon Ross pfd = daemonize_init(); 254*a547be5dSGordon Ross } 255*a547be5dSGordon Ross 256*a547be5dSGordon Ross /* 257*a547be5dSGordon Ross * Create directory for all smbiod doors. 258*a547be5dSGordon Ross */ 259*a547be5dSGordon Ross if ((mkdir(SMBIOD_RUNDIR, 0755) < 0) && errno != EEXIST) { 260*a547be5dSGordon Ross perror(SMBIOD_RUNDIR); 261*a547be5dSGordon Ross goto out; 262*a547be5dSGordon Ross } 263*a547be5dSGordon Ross 264*a547be5dSGordon Ross /* 265*a547be5dSGordon Ross * Create a file for the main service door. 266*a547be5dSGordon Ross */ 267*a547be5dSGordon Ross unlink(door_path); 268*a547be5dSGordon Ross tmp_fd = open(door_path, O_RDWR|O_CREAT|O_EXCL, 0644); 269*a547be5dSGordon Ross if (tmp_fd < 0) { 270*a547be5dSGordon Ross perror(door_path); 271*a547be5dSGordon Ross goto out; 272*a547be5dSGordon Ross } 273*a547be5dSGordon Ross close(tmp_fd); 274*a547be5dSGordon Ross tmp_fd = -1; 275*a547be5dSGordon Ross created = B_TRUE; 276*a547be5dSGordon Ross 277*a547be5dSGordon Ross /* Setup the door service. */ 278*a547be5dSGordon Ross door_fd = door_create(svc_dispatch, NULL, 279*a547be5dSGordon Ross DOOR_REFUSE_DESC | DOOR_NO_CANCEL); 280*a547be5dSGordon Ross if (door_fd == -1) { 281*a547be5dSGordon Ross perror("svc door_create"); 282*a547be5dSGordon Ross goto out; 283*a547be5dSGordon Ross } 284*a547be5dSGordon Ross fdetach(door_path); 285*a547be5dSGordon Ross if (fattach(door_fd, door_path) < 0) { 286*a547be5dSGordon Ross fprintf(stderr, "%s: fattach failed, %s\n", 287*a547be5dSGordon Ross door_path, strerror(errno)); 288*a547be5dSGordon Ross goto out; 289*a547be5dSGordon Ross } 290*a547be5dSGordon Ross attached = B_TRUE; 291*a547be5dSGordon Ross 292*a547be5dSGordon Ross /* 293*a547be5dSGordon Ross * Initializations done. Tell start method we're up. 294*a547be5dSGordon Ross */ 295*a547be5dSGordon Ross rc = SMF_EXIT_OK; 296*a547be5dSGordon Ross if (pfd != -1) { 297*a547be5dSGordon Ross daemonize_fini(pfd, rc); 298*a547be5dSGordon Ross pfd = -1; 299*a547be5dSGordon Ross } 300*a547be5dSGordon Ross 301*a547be5dSGordon Ross /* 302*a547be5dSGordon Ross * Main thread just waits for signals. 303*a547be5dSGordon Ross */ 304*a547be5dSGordon Ross again: 305*a547be5dSGordon Ross sig = sigwait(&tmpmask); 306*a547be5dSGordon Ross if (d_flag) 307*a547be5dSGordon Ross fprintf(stderr, "main: sig=%d\n", sig); 308*a547be5dSGordon Ross switch (sig) { 309*a547be5dSGordon Ross case SIGINT: 310*a547be5dSGordon Ross case SIGTERM: 311*a547be5dSGordon Ross /* 312*a547be5dSGordon Ross * The whole process contract gets a SIGTERM 313*a547be5dSGordon Ross * at once. Give children a chance to exit 314*a547be5dSGordon Ross * so we can do normal SIGCHLD cleanup. 315*a547be5dSGordon Ross * Prevent new door_open calls. 316*a547be5dSGordon Ross */ 317*a547be5dSGordon Ross fdetach(door_path); 318*a547be5dSGordon Ross attached = B_FALSE; 319*a547be5dSGordon Ross alarm(2); 320*a547be5dSGordon Ross goto again; 321*a547be5dSGordon Ross case SIGALRM: 322*a547be5dSGordon Ross break; /* normal termination */ 323*a547be5dSGordon Ross case SIGCHLD: 324*a547be5dSGordon Ross svc_sigchld(); 325*a547be5dSGordon Ross goto again; 326*a547be5dSGordon Ross case SIGCONT: 327*a547be5dSGordon Ross goto again; 328*a547be5dSGordon Ross default: 329*a547be5dSGordon Ross /* Unexpected signal. */ 330*a547be5dSGordon Ross fprintf(stderr, "svc_main: unexpected sig=%d\n", sig); 331*a547be5dSGordon Ross break; 332*a547be5dSGordon Ross } 333*a547be5dSGordon Ross 334*a547be5dSGordon Ross out: 335*a547be5dSGordon Ross if (attached) 336*a547be5dSGordon Ross fdetach(door_path); 337*a547be5dSGordon Ross if (door_fd != -1) 338*a547be5dSGordon Ross door_revoke(door_fd); 339*a547be5dSGordon Ross if (created) 340*a547be5dSGordon Ross unlink(door_path); 341*a547be5dSGordon Ross 342*a547be5dSGordon Ross /* NB: door threads gone now. */ 343*a547be5dSGordon Ross svc_cleanup(); 344*a547be5dSGordon Ross 345*a547be5dSGordon Ross /* If startup error, report to parent. */ 346*a547be5dSGordon Ross if (pfd != -1) 347*a547be5dSGordon Ross daemonize_fini(pfd, rc); 348*a547be5dSGordon Ross 349*a547be5dSGordon Ross return (rc); 350*a547be5dSGordon Ross } 351*a547be5dSGordon Ross 352*a547be5dSGordon Ross /*ARGSUSED*/ 353*a547be5dSGordon Ross void 354*a547be5dSGordon Ross svc_dispatch(void *cookie, char *argp, size_t argsz, 355*a547be5dSGordon Ross door_desc_t *dp, uint_t n_desc) 356*a547be5dSGordon Ross { 357*a547be5dSGordon Ross ucred_t *ucred = NULL; 358*a547be5dSGordon Ross uid_t uid; 359*a547be5dSGordon Ross gid_t gid; 360*a547be5dSGordon Ross int32_t cmd, rc; 361*a547be5dSGordon Ross 362*a547be5dSGordon Ross /* 363*a547be5dSGordon Ross * Allow a NULL arg call to check if this 364*a547be5dSGordon Ross * daemon is running. Just return zero. 365*a547be5dSGordon Ross */ 366*a547be5dSGordon Ross if (argp == NULL) { 367*a547be5dSGordon Ross rc = 0; 368*a547be5dSGordon Ross goto out; 369*a547be5dSGordon Ross } 370*a547be5dSGordon Ross 371*a547be5dSGordon Ross /* 372*a547be5dSGordon Ross * Get the caller's credentials. 373*a547be5dSGordon Ross * (from client side of door) 374*a547be5dSGordon Ross */ 375*a547be5dSGordon Ross if (door_ucred(&ucred) != 0) { 376*a547be5dSGordon Ross rc = EACCES; 377*a547be5dSGordon Ross goto out; 378*a547be5dSGordon Ross } 379*a547be5dSGordon Ross uid = ucred_getruid(ucred); 380*a547be5dSGordon Ross gid = ucred_getrgid(ucred); 381*a547be5dSGordon Ross 382*a547be5dSGordon Ross /* 383*a547be5dSGordon Ross * Arg is just an int command code. 384*a547be5dSGordon Ross * Reply is also an int. 385*a547be5dSGordon Ross */ 386*a547be5dSGordon Ross if (argsz != sizeof (cmd)) { 387*a547be5dSGordon Ross rc = EINVAL; 388*a547be5dSGordon Ross goto out; 389*a547be5dSGordon Ross } 390*a547be5dSGordon Ross bcopy(argp, &cmd, sizeof (cmd)); 391*a547be5dSGordon Ross switch (cmd) { 392*a547be5dSGordon Ross case SMBIOD_START: 393*a547be5dSGordon Ross rc = cmd_start(uid, gid); 394*a547be5dSGordon Ross break; 395*a547be5dSGordon Ross default: 396*a547be5dSGordon Ross rc = EINVAL; 397*a547be5dSGordon Ross goto out; 398*a547be5dSGordon Ross } 399*a547be5dSGordon Ross 400*a547be5dSGordon Ross out: 401*a547be5dSGordon Ross if (ucred != NULL) 402*a547be5dSGordon Ross ucred_free(ucred); 403*a547be5dSGordon Ross 404*a547be5dSGordon Ross door_return((void *)&rc, sizeof (rc), NULL, 0); 405*a547be5dSGordon Ross } 406*a547be5dSGordon Ross 407*a547be5dSGordon Ross /* 408*a547be5dSGordon Ross * Start a per-user smbiod, if not already running. 409*a547be5dSGordon Ross */ 410*a547be5dSGordon Ross int 411*a547be5dSGordon Ross cmd_start(uid_t uid, gid_t gid) 412*a547be5dSGordon Ross { 413*a547be5dSGordon Ross char door_file[64]; 414*a547be5dSGordon Ross child_t *cp; 415*a547be5dSGordon Ross int pid, fd = -1; 416*a547be5dSGordon Ross 417*a547be5dSGordon Ross mutex_lock(&cl_mutex); 418*a547be5dSGordon Ross cp = child_find_by_uid(uid); 419*a547be5dSGordon Ross if (cp != NULL) { 420*a547be5dSGordon Ross /* This UID already has an IOD. */ 421*a547be5dSGordon Ross mutex_unlock(&cl_mutex); 422*a547be5dSGordon Ross if (d_flag) { 423*a547be5dSGordon Ross fprintf(stderr, "cmd_start: uid %d" 424*a547be5dSGordon Ross " already has an iod\n", uid); 425*a547be5dSGordon Ross } 426*a547be5dSGordon Ross return (0); 427*a547be5dSGordon Ross } 428*a547be5dSGordon Ross 429*a547be5dSGordon Ross /* 430*a547be5dSGordon Ross * OK, create a new child. 431*a547be5dSGordon Ross */ 432*a547be5dSGordon Ross cp = malloc(sizeof (*cp)); 433*a547be5dSGordon Ross if (cp == NULL) { 434*a547be5dSGordon Ross mutex_unlock(&cl_mutex); 435*a547be5dSGordon Ross return (ENOMEM); 436*a547be5dSGordon Ross } 437*a547be5dSGordon Ross cp->pid = 0; /* update below */ 438*a547be5dSGordon Ross cp->uid = uid; 439*a547be5dSGordon Ross LIST_INSERT_HEAD(&child_list, cp, list); 440*a547be5dSGordon Ross mutex_unlock(&cl_mutex); 441*a547be5dSGordon Ross 442*a547be5dSGordon Ross /* 443*a547be5dSGordon Ross * The child will not have permission to create or 444*a547be5dSGordon Ross * destroy files in SMBIOD_RUNDIR so do that here. 445*a547be5dSGordon Ross */ 446*a547be5dSGordon Ross snprintf(door_file, sizeof (door_file), 447*a547be5dSGordon Ross SMBIOD_USR_DOOR, cp->uid); 448*a547be5dSGordon Ross unlink(door_file); 449*a547be5dSGordon Ross fd = open(door_file, O_RDWR|O_CREAT|O_EXCL, 0600); 450*a547be5dSGordon Ross if (fd < 0) { 451*a547be5dSGordon Ross perror(door_file); 452*a547be5dSGordon Ross goto errout; 453*a547be5dSGordon Ross } 454*a547be5dSGordon Ross if (fchown(fd, uid, gid) < 0) { 455*a547be5dSGordon Ross perror(door_file); 456*a547be5dSGordon Ross goto errout; 457*a547be5dSGordon Ross } 458*a547be5dSGordon Ross close(fd); 459*a547be5dSGordon Ross fd = -1; 460*a547be5dSGordon Ross 461*a547be5dSGordon Ross if ((pid = fork1()) == -1) { 462*a547be5dSGordon Ross perror("fork"); 463*a547be5dSGordon Ross goto errout; 464*a547be5dSGordon Ross } 465*a547be5dSGordon Ross if (pid == 0) { 466*a547be5dSGordon Ross (void) new_child(uid, gid); 467*a547be5dSGordon Ross _exit(1); 468*a547be5dSGordon Ross } 469*a547be5dSGordon Ross /* parent */ 470*a547be5dSGordon Ross cp->pid = pid; 471*a547be5dSGordon Ross 472*a547be5dSGordon Ross if (d_flag) { 473*a547be5dSGordon Ross fprintf(stderr, "cmd_start: uid %d new iod, pid %d\n", 474*a547be5dSGordon Ross uid, pid); 475*a547be5dSGordon Ross } 476*a547be5dSGordon Ross 477*a547be5dSGordon Ross return (0); 478*a547be5dSGordon Ross 479*a547be5dSGordon Ross errout: 480*a547be5dSGordon Ross if (fd != -1) 481*a547be5dSGordon Ross close(fd); 482*a547be5dSGordon Ross mutex_lock(&cl_mutex); 483*a547be5dSGordon Ross LIST_REMOVE(cp, list); 484*a547be5dSGordon Ross mutex_unlock(&cl_mutex); 485*a547be5dSGordon Ross free(cp); 486*a547be5dSGordon Ross return (errno); 487*a547be5dSGordon Ross } 488*a547be5dSGordon Ross 489*a547be5dSGordon Ross /* 490*a547be5dSGordon Ross * Assume the passed credentials (from the door client), 491*a547be5dSGordon Ross * drop any extra privileges, and exec the per-user iod. 492*a547be5dSGordon Ross */ 493*a547be5dSGordon Ross static int 494*a547be5dSGordon Ross new_child(uid_t uid, gid_t gid) 495*a547be5dSGordon Ross { 496*a547be5dSGordon Ross char *argv[2]; 497*a547be5dSGordon Ross int flags, rc; 498*a547be5dSGordon Ross 499*a547be5dSGordon Ross flags = PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS; 500*a547be5dSGordon Ross rc = __init_daemon_priv(flags, uid, gid, PRIV_NET_ACCESS, NULL); 501*a547be5dSGordon Ross if (rc != 0) 502*a547be5dSGordon Ross return (errno); 503*a547be5dSGordon Ross 504*a547be5dSGordon Ross argv[0] = "smbiod"; 505*a547be5dSGordon Ross argv[1] = NULL; 506*a547be5dSGordon Ross (void) execv(smbiod_path, argv); 507*a547be5dSGordon Ross return (errno); 508*a547be5dSGordon Ross } 509*a547be5dSGordon Ross 510*a547be5dSGordon Ross static void 511*a547be5dSGordon Ross svc_sigchld(void) 512*a547be5dSGordon Ross { 513*a547be5dSGordon Ross child_t *cp; 514*a547be5dSGordon Ross pid_t pid; 515*a547be5dSGordon Ross int err, status, found = 0; 516*a547be5dSGordon Ross 517*a547be5dSGordon Ross mutex_lock(&cl_mutex); 518*a547be5dSGordon Ross 519*a547be5dSGordon Ross while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { 520*a547be5dSGordon Ross 521*a547be5dSGordon Ross found++; 522*a547be5dSGordon Ross if (d_flag) 523*a547be5dSGordon Ross fprintf(stderr, "svc_sigchld: pid %d\n", (int)pid); 524*a547be5dSGordon Ross 525*a547be5dSGordon Ross cp = child_find_by_pid(pid); 526*a547be5dSGordon Ross if (cp == NULL) { 527*a547be5dSGordon Ross fprintf(stderr, "Unknown pid %d\n", (int)pid); 528*a547be5dSGordon Ross continue; 529*a547be5dSGordon Ross } 530*a547be5dSGordon Ross child_gone(cp->uid, cp->pid, status); 531*a547be5dSGordon Ross LIST_REMOVE(cp, list); 532*a547be5dSGordon Ross free(cp); 533*a547be5dSGordon Ross } 534*a547be5dSGordon Ross err = errno; 535*a547be5dSGordon Ross 536*a547be5dSGordon Ross mutex_unlock(&cl_mutex); 537*a547be5dSGordon Ross 538*a547be5dSGordon Ross /* ECHILD is the normal end of loop. */ 539*a547be5dSGordon Ross if (pid < 0 && err != ECHILD) 540*a547be5dSGordon Ross fprintf(stderr, "svc_sigchld: waitpid err %d\n", err); 541*a547be5dSGordon Ross if (found == 0) 542*a547be5dSGordon Ross fprintf(stderr, "svc_sigchld: no children?\n"); 543*a547be5dSGordon Ross } 544*a547be5dSGordon Ross 545*a547be5dSGordon Ross static void 546*a547be5dSGordon Ross child_gone(uid_t uid, pid_t pid, int status) 547*a547be5dSGordon Ross { 548*a547be5dSGordon Ross char door_file[64]; 549*a547be5dSGordon Ross int x; 550*a547be5dSGordon Ross 551*a547be5dSGordon Ross if (d_flag) 552*a547be5dSGordon Ross fprintf(stderr, "child_gone: uid %d pid %d\n", 553*a547be5dSGordon Ross uid, (int)pid); 554*a547be5dSGordon Ross 555*a547be5dSGordon Ross snprintf(door_file, sizeof (door_file), 556*a547be5dSGordon Ross SMBIOD_RUNDIR "/%d", uid); 557*a547be5dSGordon Ross unlink(door_file); 558*a547be5dSGordon Ross 559*a547be5dSGordon Ross if (WIFEXITED(status)) { 560*a547be5dSGordon Ross x = WEXITSTATUS(status); 561*a547be5dSGordon Ross if (x != 0) { 562*a547be5dSGordon Ross fprintf(stderr, 563*a547be5dSGordon Ross "uid %d, pid %d exit %d", 564*a547be5dSGordon Ross uid, (int)pid, x); 565*a547be5dSGordon Ross } 566*a547be5dSGordon Ross } 567*a547be5dSGordon Ross if (WIFSIGNALED(status)) { 568*a547be5dSGordon Ross x = WTERMSIG(status); 569*a547be5dSGordon Ross fprintf(stderr, 570*a547be5dSGordon Ross "uid %d, pid %d signal %d", 571*a547be5dSGordon Ross uid, (int)pid, x); 572*a547be5dSGordon Ross } 573*a547be5dSGordon Ross } 574*a547be5dSGordon Ross 575*a547be5dSGordon Ross /* 576*a547be5dSGordon Ross * Final cleanup before exit. Unlink child doors, etc. 577*a547be5dSGordon Ross * Called while single threaded, so no locks needed here. 578*a547be5dSGordon Ross * The list is normally empty by now due to svc_sigchld 579*a547be5dSGordon Ross * calls during shutdown. But in case there were any 580*a547be5dSGordon Ross * straglers, do cleanup here. Don't bother freeing any 581*a547be5dSGordon Ross * list elements here, as we're exiting. 582*a547be5dSGordon Ross */ 583*a547be5dSGordon Ross static void 584*a547be5dSGordon Ross svc_cleanup(void) 585*a547be5dSGordon Ross { 586*a547be5dSGordon Ross child_t *cp; 587*a547be5dSGordon Ross 588*a547be5dSGordon Ross LIST_FOREACH(cp, &child_list, list) { 589*a547be5dSGordon Ross child_gone(cp->uid, cp->pid, 0); 590*a547be5dSGordon Ross } 591*a547be5dSGordon Ross } 592