1613a2f6bSGordon Ross /* 2613a2f6bSGordon Ross * CDDL HEADER START 3613a2f6bSGordon Ross * 4613a2f6bSGordon Ross * The contents of this file are subject to the terms of the 5613a2f6bSGordon Ross * Common Development and Distribution License (the "License"). 6613a2f6bSGordon Ross * You may not use this file except in compliance with the License. 7613a2f6bSGordon Ross * 8613a2f6bSGordon Ross * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE 9613a2f6bSGordon Ross * or http://www.opensolaris.org/os/licensing. 10613a2f6bSGordon Ross * See the License for the specific language governing permissions 11613a2f6bSGordon Ross * and limitations under the License. 12613a2f6bSGordon Ross * 13613a2f6bSGordon Ross * When distributing Covered Code, include this CDDL HEADER in each 14613a2f6bSGordon Ross * file and include the License file at usr/src/OPENSOLARIS.LICENSE. 15613a2f6bSGordon Ross * If applicable, add the following below this CDDL HEADER, with the 16613a2f6bSGordon Ross * fields enclosed by brackets "[]" replaced with your own identifying 17613a2f6bSGordon Ross * information: Portions Copyright [yyyy] [name of copyright owner] 18613a2f6bSGordon Ross * 19613a2f6bSGordon Ross * CDDL HEADER END 20613a2f6bSGordon Ross */ 21613a2f6bSGordon Ross 22613a2f6bSGordon Ross /* 23*15359501SGordon Ross * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24613a2f6bSGordon Ross */ 25613a2f6bSGordon Ross 26613a2f6bSGordon Ross /* 27613a2f6bSGordon Ross * SMBFS I/O Deamon (smbiod) 28613a2f6bSGordon Ross */ 29613a2f6bSGordon Ross 30613a2f6bSGordon Ross #include <sys/types.h> 31613a2f6bSGordon Ross #include <sys/stat.h> 32613a2f6bSGordon Ross #include <sys/note.h> 33613a2f6bSGordon Ross 34613a2f6bSGordon Ross #include <errno.h> 35613a2f6bSGordon Ross #include <fcntl.h> 36613a2f6bSGordon Ross #include <signal.h> 37613a2f6bSGordon Ross #include <stdarg.h> 38613a2f6bSGordon Ross #include <stdio.h> 39613a2f6bSGordon Ross #include <string.h> 40613a2f6bSGordon Ross #include <strings.h> 41613a2f6bSGordon Ross #include <stdlib.h> 42613a2f6bSGordon Ross #include <synch.h> 43613a2f6bSGordon Ross #include <time.h> 44613a2f6bSGordon Ross #include <unistd.h> 45613a2f6bSGordon Ross #include <ucred.h> 46613a2f6bSGordon Ross 47613a2f6bSGordon Ross #include <err.h> 48613a2f6bSGordon Ross #include <door.h> 49613a2f6bSGordon Ross #include <thread.h> 50613a2f6bSGordon Ross 51613a2f6bSGordon Ross #include <netsmb/smb_lib.h> 52613a2f6bSGordon Ross 53613a2f6bSGordon Ross #define EXIT_FAIL 1 54613a2f6bSGordon Ross #define EXIT_OK 0 55613a2f6bSGordon Ross 56613a2f6bSGordon Ross #if defined(DEBUG) || defined(__lint) 57613a2f6bSGordon Ross #define DPRINT(...) do \ 58613a2f6bSGordon Ross { \ 59613a2f6bSGordon Ross if (smb_debug) \ 60613a2f6bSGordon Ross fprintf(stderr, __VA_ARGS__); \ 61613a2f6bSGordon Ross _NOTE(CONSTCOND) \ 62613a2f6bSGordon Ross } while (0) 63613a2f6bSGordon Ross #else 64613a2f6bSGordon Ross #define DPRINT(...) ((void)0) 65613a2f6bSGordon Ross #endif 66613a2f6bSGordon Ross 67613a2f6bSGordon Ross mutex_t iod_mutex = DEFAULTMUTEX; 68613a2f6bSGordon Ross int iod_thr_count; /* threads, excluding main */ 69613a2f6bSGordon Ross int iod_terminating; 70*15359501SGordon Ross int iod_alarm_time = 30; /* sec. */ 71613a2f6bSGordon Ross 72613a2f6bSGordon Ross void iod_dispatch(void *cookie, char *argp, size_t argsz, 73613a2f6bSGordon Ross door_desc_t *dp, uint_t n_desc); 74613a2f6bSGordon Ross int iod_newvc(smb_iod_ssn_t *clnt_ssn); 75613a2f6bSGordon Ross void * iod_work(void *arg); 76613a2f6bSGordon Ross 77613a2f6bSGordon Ross int 78613a2f6bSGordon Ross main(int argc, char **argv) 79613a2f6bSGordon Ross { 80613a2f6bSGordon Ross static const int door_attrs = 81613a2f6bSGordon Ross DOOR_REFUSE_DESC | DOOR_NO_CANCEL; 82613a2f6bSGordon Ross sigset_t oldmask, tmpmask; 83613a2f6bSGordon Ross char *env, *door_path = NULL; 84613a2f6bSGordon Ross int door_fd = -1, tmp_fd = -1; 85613a2f6bSGordon Ross int err, i, sig; 86613a2f6bSGordon Ross int rc = EXIT_FAIL; 87613a2f6bSGordon Ross 88613a2f6bSGordon Ross /* Debugging support. */ 89613a2f6bSGordon Ross if ((env = getenv("SMBFS_DEBUG")) != NULL) { 90613a2f6bSGordon Ross smb_debug = atoi(env); 91613a2f6bSGordon Ross if (smb_debug < 1) 92613a2f6bSGordon Ross smb_debug = 1; 93*15359501SGordon Ross iod_alarm_time = 300; 94613a2f6bSGordon Ross } 95613a2f6bSGordon Ross 96613a2f6bSGordon Ross /* 97613a2f6bSGordon Ross * Find out if an IOD is already running. 98613a2f6bSGordon Ross * If so, we lost a harmless startup race. 99613a2f6bSGordon Ross * An IOD did start, so exit success. 100613a2f6bSGordon Ross */ 101613a2f6bSGordon Ross err = smb_iod_open_door(&door_fd); 102613a2f6bSGordon Ross if (err == 0) { 103613a2f6bSGordon Ross close(door_fd); 104613a2f6bSGordon Ross door_fd = -1; 105613a2f6bSGordon Ross DPRINT("main: already running\n"); 106613a2f6bSGordon Ross exit(EXIT_OK); 107613a2f6bSGordon Ross } 108613a2f6bSGordon Ross 109613a2f6bSGordon Ross /* 110613a2f6bSGordon Ross * Create a file for the door. 111613a2f6bSGordon Ross */ 112613a2f6bSGordon Ross door_path = smb_iod_door_path(); 113613a2f6bSGordon Ross unlink(door_path); 114613a2f6bSGordon Ross tmp_fd = open(door_path, O_RDWR|O_CREAT|O_EXCL, 0600); 115613a2f6bSGordon Ross if (tmp_fd < 0) { 116613a2f6bSGordon Ross perror(door_path); 117613a2f6bSGordon Ross exit(EXIT_FAIL); 118613a2f6bSGordon Ross } 119613a2f6bSGordon Ross close(tmp_fd); 120613a2f6bSGordon Ross tmp_fd = -1; 121613a2f6bSGordon Ross 122613a2f6bSGordon Ross 123613a2f6bSGordon Ross /* 124613a2f6bSGordon Ross * Close FDs 0,1,2 so we don't have a TTY, and 125613a2f6bSGordon Ross * re-open them on /dev/null so they won't be 126613a2f6bSGordon Ross * used for device handles (etc.) later, and 127613a2f6bSGordon Ross * we don't have to worry about printf calls 128613a2f6bSGordon Ross * or whatever going to these FDs. 129613a2f6bSGordon Ross */ 130613a2f6bSGordon Ross for (i = 0; i < 3; i++) { 131613a2f6bSGordon Ross /* Exception: If smb_debug, keep stderr */ 132613a2f6bSGordon Ross if (smb_debug && i == 2) 133613a2f6bSGordon Ross break; 134613a2f6bSGordon Ross close(i); 135613a2f6bSGordon Ross tmp_fd = open("/dev/null", O_RDWR); 136613a2f6bSGordon Ross if (tmp_fd < 0) 137613a2f6bSGordon Ross perror("/dev/null"); 138613a2f6bSGordon Ross if (tmp_fd != i) 139613a2f6bSGordon Ross DPRINT("Open /dev/null - wrong fd?\n"); 140613a2f6bSGordon Ross } 141613a2f6bSGordon Ross 142613a2f6bSGordon Ross /* 143613a2f6bSGordon Ross * Become session leader. 144613a2f6bSGordon Ross */ 145613a2f6bSGordon Ross setsid(); 146613a2f6bSGordon Ross 147613a2f6bSGordon Ross /* 148613a2f6bSGordon Ross * Create door service threads with signals blocked. 149613a2f6bSGordon Ross */ 150613a2f6bSGordon Ross sigfillset(&tmpmask); 151613a2f6bSGordon Ross sigprocmask(SIG_BLOCK, &tmpmask, &oldmask); 152613a2f6bSGordon Ross 153613a2f6bSGordon Ross /* Setup the door service. */ 154613a2f6bSGordon Ross door_fd = door_create(iod_dispatch, NULL, door_attrs); 155613a2f6bSGordon Ross if (door_fd < 0) { 156613a2f6bSGordon Ross fprintf(stderr, "%s: door_create failed\n", argv[0]); 157613a2f6bSGordon Ross rc = EXIT_FAIL; 158613a2f6bSGordon Ross goto errout; 159613a2f6bSGordon Ross } 160613a2f6bSGordon Ross fdetach(door_path); 161613a2f6bSGordon Ross if (fattach(door_fd, door_path) < 0) { 162613a2f6bSGordon Ross fprintf(stderr, "%s: fattach failed\n", argv[0]); 163613a2f6bSGordon Ross rc = EXIT_FAIL; 164613a2f6bSGordon Ross goto errout; 165613a2f6bSGordon Ross } 166613a2f6bSGordon Ross 167613a2f6bSGordon Ross /* 168613a2f6bSGordon Ross * Post the initial alarm, and then just 169613a2f6bSGordon Ross * wait for signals. 170613a2f6bSGordon Ross */ 171*15359501SGordon Ross alarm(iod_alarm_time); 172613a2f6bSGordon Ross again: 173613a2f6bSGordon Ross sig = sigwait(&tmpmask); 174613a2f6bSGordon Ross DPRINT("main: sig=%d\n", sig); 175613a2f6bSGordon Ross 176613a2f6bSGordon Ross /* 177613a2f6bSGordon Ross * If a door call races with the alarm, ignore the alarm. 178613a2f6bSGordon Ross * It will be rescheduled when the threads go away. 179613a2f6bSGordon Ross */ 180613a2f6bSGordon Ross mutex_lock(&iod_mutex); 181613a2f6bSGordon Ross if (sig == SIGALRM && iod_thr_count > 0) { 182613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 183613a2f6bSGordon Ross goto again; 184613a2f6bSGordon Ross } 185613a2f6bSGordon Ross iod_terminating = 1; 186613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 187613a2f6bSGordon Ross rc = EXIT_OK; 188613a2f6bSGordon Ross 189613a2f6bSGordon Ross errout: 190613a2f6bSGordon Ross fdetach(door_path); 191613a2f6bSGordon Ross door_revoke(door_fd); 192613a2f6bSGordon Ross door_fd = -1; 193613a2f6bSGordon Ross unlink(door_path); 194613a2f6bSGordon Ross 195613a2f6bSGordon Ross return (rc); 196613a2f6bSGordon Ross } 197613a2f6bSGordon Ross 198613a2f6bSGordon Ross /*ARGSUSED*/ 199613a2f6bSGordon Ross void 200613a2f6bSGordon Ross iod_dispatch(void *cookie, char *argp, size_t argsz, 201613a2f6bSGordon Ross door_desc_t *dp, uint_t n_desc) 202613a2f6bSGordon Ross { 203613a2f6bSGordon Ross smb_iod_ssn_t *ssn; 204613a2f6bSGordon Ross ucred_t *ucred; 205613a2f6bSGordon Ross uid_t cl_uid; 206613a2f6bSGordon Ross int rc; 207613a2f6bSGordon Ross 208613a2f6bSGordon Ross /* 209613a2f6bSGordon Ross * Verify that the calling process has the same UID. 210613a2f6bSGordon Ross * Paranoia: The door we created has mode 0600, so 211613a2f6bSGordon Ross * this check is probably redundant. 212613a2f6bSGordon Ross */ 213613a2f6bSGordon Ross ucred = NULL; 214613a2f6bSGordon Ross if (door_ucred(&ucred) != 0) { 215613a2f6bSGordon Ross rc = EACCES; 216613a2f6bSGordon Ross goto out; 217613a2f6bSGordon Ross } 218613a2f6bSGordon Ross cl_uid = ucred_getruid(ucred); 219613a2f6bSGordon Ross ucred_free(ucred); 220613a2f6bSGordon Ross ucred = NULL; 221613a2f6bSGordon Ross if (cl_uid != getuid()) { 222613a2f6bSGordon Ross DPRINT("iod_dispatch: wrong UID\n"); 223613a2f6bSGordon Ross rc = EACCES; 224613a2f6bSGordon Ross goto out; 225613a2f6bSGordon Ross } 226613a2f6bSGordon Ross 227613a2f6bSGordon Ross /* 228613a2f6bSGordon Ross * The library uses a NULL arg call to check if 229613a2f6bSGordon Ross * the deamon is running. Just return zero. 230613a2f6bSGordon Ross */ 231613a2f6bSGordon Ross if (argp == NULL) { 232613a2f6bSGordon Ross rc = 0; 233613a2f6bSGordon Ross goto out; 234613a2f6bSGordon Ross } 235613a2f6bSGordon Ross 236613a2f6bSGordon Ross /* 237613a2f6bSGordon Ross * Otherwise, the arg must be the (fixed size) 238613a2f6bSGordon Ross * smb_iod_ssn_t 239613a2f6bSGordon Ross */ 240613a2f6bSGordon Ross if (argsz != sizeof (*ssn)) { 241613a2f6bSGordon Ross rc = EINVAL; 242613a2f6bSGordon Ross goto out; 243613a2f6bSGordon Ross } 244613a2f6bSGordon Ross 245613a2f6bSGordon Ross mutex_lock(&iod_mutex); 246613a2f6bSGordon Ross if (iod_terminating) { 247613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 248613a2f6bSGordon Ross DPRINT("iod_dispatch: terminating\n"); 249613a2f6bSGordon Ross rc = EINTR; 250613a2f6bSGordon Ross goto out; 251613a2f6bSGordon Ross } 252613a2f6bSGordon Ross if (iod_thr_count++ == 0) { 253613a2f6bSGordon Ross alarm(0); 254613a2f6bSGordon Ross DPRINT("iod_dispatch: cancelled alarm\n"); 255613a2f6bSGordon Ross } 256613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 257613a2f6bSGordon Ross 258613a2f6bSGordon Ross ssn = (void *) argp; 259613a2f6bSGordon Ross rc = iod_newvc(ssn); 260613a2f6bSGordon Ross 261613a2f6bSGordon Ross mutex_lock(&iod_mutex); 262613a2f6bSGordon Ross if (--iod_thr_count == 0) { 263613a2f6bSGordon Ross DPRINT("iod_dispatch: schedule alarm\n"); 264*15359501SGordon Ross alarm(iod_alarm_time); 265613a2f6bSGordon Ross } 266613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 267613a2f6bSGordon Ross 268613a2f6bSGordon Ross out: 269613a2f6bSGordon Ross door_return((void *)&rc, sizeof (rc), NULL, 0); 270613a2f6bSGordon Ross } 271613a2f6bSGordon Ross 272613a2f6bSGordon Ross /* 273613a2f6bSGordon Ross * Try making a connection with the server described by 274613a2f6bSGordon Ross * the info in the smb_iod_ssn_t arg. If successful, 275613a2f6bSGordon Ross * start an IOD thread to service it, then return to 276613a2f6bSGordon Ross * the client side of the door. 277613a2f6bSGordon Ross */ 278613a2f6bSGordon Ross int 279613a2f6bSGordon Ross iod_newvc(smb_iod_ssn_t *clnt_ssn) 280613a2f6bSGordon Ross { 281613a2f6bSGordon Ross smb_ctx_t *ctx; 282613a2f6bSGordon Ross thread_t tid; 283613a2f6bSGordon Ross int err; 284613a2f6bSGordon Ross 285613a2f6bSGordon Ross 286613a2f6bSGordon Ross /* 287613a2f6bSGordon Ross * This needs to essentially "clone" the smb_ctx_t 288613a2f6bSGordon Ross * from the client side of the door, or at least 289613a2f6bSGordon Ross * as much of it as we need while creating a VC. 290613a2f6bSGordon Ross */ 291613a2f6bSGordon Ross err = smb_ctx_alloc(&ctx); 292613a2f6bSGordon Ross if (err) 293613a2f6bSGordon Ross return (err); 294613a2f6bSGordon Ross bcopy(clnt_ssn, &ctx->ct_iod_ssn, sizeof (ctx->ct_iod_ssn)); 295613a2f6bSGordon Ross 296613a2f6bSGordon Ross /* 297613a2f6bSGordon Ross * Do the initial connection setup here, so we can 298613a2f6bSGordon Ross * report the outcome to the door client. 299613a2f6bSGordon Ross */ 300613a2f6bSGordon Ross err = smb_iod_connect(ctx); 301613a2f6bSGordon Ross if (err != 0) 302613a2f6bSGordon Ross goto out; 303613a2f6bSGordon Ross 304613a2f6bSGordon Ross /* 305613a2f6bSGordon Ross * Create the driver session now, so we don't 306613a2f6bSGordon Ross * race with the door client findvc call. 307613a2f6bSGordon Ross */ 308613a2f6bSGordon Ross if ((err = smb_ctx_gethandle(ctx)) != 0) 309613a2f6bSGordon Ross goto out; 310613a2f6bSGordon Ross if (ioctl(ctx->ct_dev_fd, SMBIOC_SSN_CREATE, &ctx->ct_ssn) < 0) { 311613a2f6bSGordon Ross err = errno; 312613a2f6bSGordon Ross goto out; 313613a2f6bSGordon Ross } 314613a2f6bSGordon Ross 315613a2f6bSGordon Ross /* The rest happens in the iod_work thread. */ 316613a2f6bSGordon Ross err = thr_create(NULL, 0, iod_work, ctx, THR_DETACHED, &tid); 317613a2f6bSGordon Ross if (err == 0) { 318613a2f6bSGordon Ross /* 319613a2f6bSGordon Ross * Given to the new thread. 320613a2f6bSGordon Ross * free at end of iod_work 321613a2f6bSGordon Ross */ 322613a2f6bSGordon Ross ctx = NULL; 323613a2f6bSGordon Ross } 324613a2f6bSGordon Ross 325613a2f6bSGordon Ross out: 326613a2f6bSGordon Ross if (ctx) 327613a2f6bSGordon Ross smb_ctx_free(ctx); 328613a2f6bSGordon Ross 329613a2f6bSGordon Ross return (err); 330613a2f6bSGordon Ross } 331613a2f6bSGordon Ross 332613a2f6bSGordon Ross /* 333613a2f6bSGordon Ross * Be the reader thread for some VC. 334613a2f6bSGordon Ross * 335613a2f6bSGordon Ross * This is started by a door call thread, which means 336613a2f6bSGordon Ross * this is always at least the 2nd thread, therefore 337613a2f6bSGordon Ross * it should never see thr_count==0 or terminating. 338613a2f6bSGordon Ross */ 339613a2f6bSGordon Ross void * 340613a2f6bSGordon Ross iod_work(void *arg) 341613a2f6bSGordon Ross { 342613a2f6bSGordon Ross smb_ctx_t *ctx = arg; 343613a2f6bSGordon Ross 344613a2f6bSGordon Ross mutex_lock(&iod_mutex); 345613a2f6bSGordon Ross if (iod_thr_count++ == 0) { 346613a2f6bSGordon Ross alarm(0); 347613a2f6bSGordon Ross DPRINT("iod_work: cancelled alarm\n"); 348613a2f6bSGordon Ross } 349613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 350613a2f6bSGordon Ross 351613a2f6bSGordon Ross (void) smb_iod_work(ctx); 352613a2f6bSGordon Ross 353613a2f6bSGordon Ross mutex_lock(&iod_mutex); 354613a2f6bSGordon Ross if (--iod_thr_count == 0) { 355613a2f6bSGordon Ross DPRINT("iod_work: schedule alarm\n"); 356*15359501SGordon Ross alarm(iod_alarm_time); 357613a2f6bSGordon Ross } 358613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 359613a2f6bSGordon Ross 360613a2f6bSGordon Ross smb_ctx_free(ctx); 361613a2f6bSGordon Ross return (NULL); 362613a2f6bSGordon Ross } 363