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 /* 2315359501SGordon Ross * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24*fabf08aeSGordon Ross * Copyright 2012 Nexenta Systems, Inc. All rights reserved. 25613a2f6bSGordon Ross */ 26613a2f6bSGordon Ross 27613a2f6bSGordon Ross /* 28a547be5dSGordon Ross * SMBFS I/O Daemon (Per-user IOD) 29613a2f6bSGordon Ross */ 30613a2f6bSGordon Ross 31613a2f6bSGordon Ross #include <sys/types.h> 32613a2f6bSGordon Ross #include <sys/stat.h> 33613a2f6bSGordon Ross #include <sys/note.h> 34613a2f6bSGordon Ross 35613a2f6bSGordon Ross #include <errno.h> 36613a2f6bSGordon Ross #include <fcntl.h> 37613a2f6bSGordon Ross #include <signal.h> 38613a2f6bSGordon Ross #include <stdarg.h> 39613a2f6bSGordon Ross #include <stdio.h> 40613a2f6bSGordon Ross #include <string.h> 41613a2f6bSGordon Ross #include <strings.h> 42613a2f6bSGordon Ross #include <stdlib.h> 43613a2f6bSGordon Ross #include <synch.h> 44613a2f6bSGordon Ross #include <time.h> 45613a2f6bSGordon Ross #include <unistd.h> 46613a2f6bSGordon Ross #include <ucred.h> 47613a2f6bSGordon Ross #include <err.h> 48613a2f6bSGordon Ross #include <door.h> 49a547be5dSGordon Ross #include <libscf.h> 50a547be5dSGordon Ross #include <locale.h> 51613a2f6bSGordon Ross #include <thread.h> 52613a2f6bSGordon Ross 53613a2f6bSGordon Ross #include <netsmb/smb_lib.h> 54613a2f6bSGordon Ross 55613a2f6bSGordon Ross #define DPRINT(...) do \ 56613a2f6bSGordon Ross { \ 57613a2f6bSGordon Ross if (smb_debug) \ 58613a2f6bSGordon Ross fprintf(stderr, __VA_ARGS__); \ 59613a2f6bSGordon Ross _NOTE(CONSTCOND) \ 60613a2f6bSGordon Ross } while (0) 61613a2f6bSGordon Ross 62613a2f6bSGordon Ross mutex_t iod_mutex = DEFAULTMUTEX; 63613a2f6bSGordon Ross int iod_thr_count; /* threads, excluding main */ 64613a2f6bSGordon Ross int iod_terminating; 6515359501SGordon Ross int iod_alarm_time = 30; /* sec. */ 66613a2f6bSGordon Ross 67613a2f6bSGordon Ross void iod_dispatch(void *cookie, char *argp, size_t argsz, 68613a2f6bSGordon Ross door_desc_t *dp, uint_t n_desc); 69613a2f6bSGordon Ross int iod_newvc(smb_iod_ssn_t *clnt_ssn); 70613a2f6bSGordon Ross void * iod_work(void *arg); 71613a2f6bSGordon Ross 72613a2f6bSGordon Ross int 73613a2f6bSGordon Ross main(int argc, char **argv) 74613a2f6bSGordon Ross { 75613a2f6bSGordon Ross sigset_t oldmask, tmpmask; 76613a2f6bSGordon Ross char *env, *door_path = NULL; 77a547be5dSGordon Ross int door_fd = -1; 78a547be5dSGordon Ross int err, sig; 79a547be5dSGordon Ross int rc = SMF_EXIT_ERR_FATAL; 80a547be5dSGordon Ross boolean_t attached = B_FALSE; 81a547be5dSGordon Ross 82a547be5dSGordon Ross /* set locale and text domain for i18n */ 83a547be5dSGordon Ross (void) setlocale(LC_ALL, ""); 84a547be5dSGordon Ross (void) textdomain(TEXT_DOMAIN); 85613a2f6bSGordon Ross 86613a2f6bSGordon Ross /* Debugging support. */ 87613a2f6bSGordon Ross if ((env = getenv("SMBFS_DEBUG")) != NULL) { 88613a2f6bSGordon Ross smb_debug = atoi(env); 89613a2f6bSGordon Ross if (smb_debug < 1) 90613a2f6bSGordon Ross smb_debug = 1; 9115359501SGordon Ross iod_alarm_time = 300; 92613a2f6bSGordon Ross } 93613a2f6bSGordon Ross 94613a2f6bSGordon Ross /* 95a547be5dSGordon Ross * If a user runs this command (i.e. by accident) 96a547be5dSGordon Ross * don't interfere with any already running IOD. 97613a2f6bSGordon Ross */ 98613a2f6bSGordon Ross err = smb_iod_open_door(&door_fd); 99613a2f6bSGordon Ross if (err == 0) { 100613a2f6bSGordon Ross close(door_fd); 101613a2f6bSGordon Ross door_fd = -1; 102a547be5dSGordon Ross DPRINT("%s: already running\n", argv[0]); 103a547be5dSGordon Ross exit(SMF_EXIT_OK); 104613a2f6bSGordon Ross } 105613a2f6bSGordon Ross 106613a2f6bSGordon Ross /* 107a547be5dSGordon Ross * Want all signals blocked, as we're doing 108a547be5dSGordon Ross * synchronous delivery via sigwait below. 109613a2f6bSGordon Ross */ 110613a2f6bSGordon Ross sigfillset(&tmpmask); 111613a2f6bSGordon Ross sigprocmask(SIG_BLOCK, &tmpmask, &oldmask); 112613a2f6bSGordon Ross 113613a2f6bSGordon Ross /* Setup the door service. */ 114a547be5dSGordon Ross door_path = smb_iod_door_path(); 115a547be5dSGordon Ross door_fd = door_create(iod_dispatch, NULL, 116a547be5dSGordon Ross DOOR_REFUSE_DESC | DOOR_NO_CANCEL); 117a547be5dSGordon Ross if (door_fd == -1) { 118a547be5dSGordon Ross perror("iod door_create"); 119a547be5dSGordon Ross goto out; 120613a2f6bSGordon Ross } 121613a2f6bSGordon Ross fdetach(door_path); 122613a2f6bSGordon Ross if (fattach(door_fd, door_path) < 0) { 123a547be5dSGordon Ross fprintf(stderr, "%s: fattach failed, %s\n", 124a547be5dSGordon Ross door_path, strerror(errno)); 125a547be5dSGordon Ross goto out; 126613a2f6bSGordon Ross } 127a547be5dSGordon Ross attached = B_TRUE; 128a547be5dSGordon Ross 129a547be5dSGordon Ross /* Initializations done. */ 130a547be5dSGordon Ross rc = SMF_EXIT_OK; 131613a2f6bSGordon Ross 132613a2f6bSGordon Ross /* 133613a2f6bSGordon Ross * Post the initial alarm, and then just 134613a2f6bSGordon Ross * wait for signals. 135613a2f6bSGordon Ross */ 13615359501SGordon Ross alarm(iod_alarm_time); 137613a2f6bSGordon Ross again: 138613a2f6bSGordon Ross sig = sigwait(&tmpmask); 139613a2f6bSGordon Ross DPRINT("main: sig=%d\n", sig); 140a547be5dSGordon Ross switch (sig) { 141a547be5dSGordon Ross case SIGCONT: 142a547be5dSGordon Ross goto again; 143613a2f6bSGordon Ross 144a547be5dSGordon Ross case SIGALRM: 145a547be5dSGordon Ross /* No threads active for a while. */ 146613a2f6bSGordon Ross mutex_lock(&iod_mutex); 147a547be5dSGordon Ross if (iod_thr_count > 0) { 148a547be5dSGordon Ross /* 149a547be5dSGordon Ross * Door call thread creation raced with 150a547be5dSGordon Ross * the alarm. Ignore this alaram. 151a547be5dSGordon Ross */ 152613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 153613a2f6bSGordon Ross goto again; 154613a2f6bSGordon Ross } 155a547be5dSGordon Ross /* Prevent a race with iod_thr_count */ 156613a2f6bSGordon Ross iod_terminating = 1; 157613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 158a547be5dSGordon Ross break; 159613a2f6bSGordon Ross 160a547be5dSGordon Ross case SIGINT: 161a547be5dSGordon Ross case SIGTERM: 162a547be5dSGordon Ross break; /* normal termination */ 163a547be5dSGordon Ross 164a547be5dSGordon Ross default: 165a547be5dSGordon Ross /* Unexpected signal. */ 166a547be5dSGordon Ross fprintf(stderr, "iod_main: unexpected sig=%d\n", sig); 167a547be5dSGordon Ross break; 168a547be5dSGordon Ross } 169a547be5dSGordon Ross 170a547be5dSGordon Ross out: 171a547be5dSGordon Ross iod_terminating = 1; 172a547be5dSGordon Ross if (attached) 173613a2f6bSGordon Ross fdetach(door_path); 174a547be5dSGordon Ross if (door_fd != -1) 175613a2f6bSGordon Ross door_revoke(door_fd); 176a547be5dSGordon Ross 177a547be5dSGordon Ross /* 178a547be5dSGordon Ross * We need a reference in -lumem to satisfy check_rtime, 179a547be5dSGordon Ross * else we get build hoise. This is sufficient. 180a547be5dSGordon Ross */ 181a547be5dSGordon Ross free(NULL); 182613a2f6bSGordon Ross 183613a2f6bSGordon Ross return (rc); 184613a2f6bSGordon Ross } 185613a2f6bSGordon Ross 186613a2f6bSGordon Ross /*ARGSUSED*/ 187613a2f6bSGordon Ross void 188613a2f6bSGordon Ross iod_dispatch(void *cookie, char *argp, size_t argsz, 189613a2f6bSGordon Ross door_desc_t *dp, uint_t n_desc) 190613a2f6bSGordon Ross { 191613a2f6bSGordon Ross smb_iod_ssn_t *ssn; 192613a2f6bSGordon Ross ucred_t *ucred; 193613a2f6bSGordon Ross uid_t cl_uid; 194613a2f6bSGordon Ross int rc; 195613a2f6bSGordon Ross 196613a2f6bSGordon Ross /* 197613a2f6bSGordon Ross * Verify that the calling process has the same UID. 198613a2f6bSGordon Ross * Paranoia: The door we created has mode 0600, so 199613a2f6bSGordon Ross * this check is probably redundant. 200613a2f6bSGordon Ross */ 201613a2f6bSGordon Ross ucred = NULL; 202613a2f6bSGordon Ross if (door_ucred(&ucred) != 0) { 203613a2f6bSGordon Ross rc = EACCES; 204613a2f6bSGordon Ross goto out; 205613a2f6bSGordon Ross } 206613a2f6bSGordon Ross cl_uid = ucred_getruid(ucred); 207613a2f6bSGordon Ross ucred_free(ucred); 208613a2f6bSGordon Ross ucred = NULL; 209613a2f6bSGordon Ross if (cl_uid != getuid()) { 210613a2f6bSGordon Ross DPRINT("iod_dispatch: wrong UID\n"); 211613a2f6bSGordon Ross rc = EACCES; 212613a2f6bSGordon Ross goto out; 213613a2f6bSGordon Ross } 214613a2f6bSGordon Ross 215613a2f6bSGordon Ross /* 216613a2f6bSGordon Ross * The library uses a NULL arg call to check if 217a547be5dSGordon Ross * the daemon is running. Just return zero. 218613a2f6bSGordon Ross */ 219613a2f6bSGordon Ross if (argp == NULL) { 220613a2f6bSGordon Ross rc = 0; 221613a2f6bSGordon Ross goto out; 222613a2f6bSGordon Ross } 223613a2f6bSGordon Ross 224613a2f6bSGordon Ross /* 225613a2f6bSGordon Ross * Otherwise, the arg must be the (fixed size) 226613a2f6bSGordon Ross * smb_iod_ssn_t 227613a2f6bSGordon Ross */ 228613a2f6bSGordon Ross if (argsz != sizeof (*ssn)) { 229613a2f6bSGordon Ross rc = EINVAL; 230613a2f6bSGordon Ross goto out; 231613a2f6bSGordon Ross } 232613a2f6bSGordon Ross 233613a2f6bSGordon Ross mutex_lock(&iod_mutex); 234613a2f6bSGordon Ross if (iod_terminating) { 235613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 236613a2f6bSGordon Ross DPRINT("iod_dispatch: terminating\n"); 237613a2f6bSGordon Ross rc = EINTR; 238613a2f6bSGordon Ross goto out; 239613a2f6bSGordon Ross } 240613a2f6bSGordon Ross if (iod_thr_count++ == 0) { 241613a2f6bSGordon Ross alarm(0); 242613a2f6bSGordon Ross DPRINT("iod_dispatch: cancelled alarm\n"); 243613a2f6bSGordon Ross } 244613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 245613a2f6bSGordon Ross 246613a2f6bSGordon Ross ssn = (void *) argp; 247613a2f6bSGordon Ross rc = iod_newvc(ssn); 248613a2f6bSGordon Ross 249613a2f6bSGordon Ross mutex_lock(&iod_mutex); 250613a2f6bSGordon Ross if (--iod_thr_count == 0) { 251613a2f6bSGordon Ross DPRINT("iod_dispatch: schedule alarm\n"); 25215359501SGordon Ross alarm(iod_alarm_time); 253613a2f6bSGordon Ross } 254613a2f6bSGordon Ross mutex_unlock(&iod_mutex); 255613a2f6bSGordon Ross 256613a2f6bSGordon Ross out: 257613a2f6bSGordon Ross door_return((void *)&rc, sizeof (rc), NULL, 0); 258613a2f6bSGordon Ross } 259613a2f6bSGordon Ross 260613a2f6bSGordon Ross /* 261613a2f6bSGordon Ross * Try making a connection with the server described by 262613a2f6bSGordon Ross * the info in the smb_iod_ssn_t arg. If successful, 263613a2f6bSGordon Ross * start an IOD thread to service it, then return to 264613a2f6bSGordon Ross * the client side of the door. 265613a2f6bSGordon Ross */ 266613a2f6bSGordon Ross int 267613a2f6bSGordon Ross iod_newvc(smb_iod_ssn_t *clnt_ssn) 268613a2f6bSGordon Ross { 269613a2f6bSGordon Ross smb_ctx_t *ctx; 270613a2f6bSGordon Ross thread_t tid; 271613a2f6bSGordon Ross int err; 272613a2f6bSGordon Ross 273613a2f6bSGordon Ross 274613a2f6bSGordon Ross /* 275613a2f6bSGordon Ross * This needs to essentially "clone" the smb_ctx_t 276613a2f6bSGordon Ross * from the client side of the door, or at least 277613a2f6bSGordon Ross * as much of it as we need while creating a VC. 278613a2f6bSGordon Ross */ 279613a2f6bSGordon Ross err = smb_ctx_alloc(&ctx); 280613a2f6bSGordon Ross if (err) 281613a2f6bSGordon Ross return (err); 282613a2f6bSGordon Ross bcopy(clnt_ssn, &ctx->ct_iod_ssn, sizeof (ctx->ct_iod_ssn)); 283613a2f6bSGordon Ross 284613a2f6bSGordon Ross /* 285*fabf08aeSGordon Ross * Create the driver session first, so that any subsequent 286*fabf08aeSGordon Ross * requests for the same session will find this one and 287*fabf08aeSGordon Ross * wait, the same as when a reconnect is triggered. 288*fabf08aeSGordon Ross * 289*fabf08aeSGordon Ross * There is still an inherent race here, where two callers 290*fabf08aeSGordon Ross * both find no VC in the driver, and both come here trying 291*fabf08aeSGordon Ross * to create the VC. In this case, we want the first one 292*fabf08aeSGordon Ross * to actually do the VC setup, and the second to proceed 293*fabf08aeSGordon Ross * as if the VC had been found in the driver. The second 294*fabf08aeSGordon Ross * caller gets an EEXIST error from the ioctl in this case, 295*fabf08aeSGordon Ross * which we therefore ignore here so that the caller will 296*fabf08aeSGordon Ross * go ahead and look again in the driver for the new VC. 297*fabf08aeSGordon Ross */ 298*fabf08aeSGordon Ross if ((err = smb_ctx_gethandle(ctx)) != 0) 299*fabf08aeSGordon Ross goto out; 300*fabf08aeSGordon Ross if (ioctl(ctx->ct_dev_fd, SMBIOC_SSN_CREATE, &ctx->ct_ssn) < 0) { 301*fabf08aeSGordon Ross err = errno; 302*fabf08aeSGordon Ross if (err == EEXIST) 303*fabf08aeSGordon Ross err = 0; /* see above */ 304*fabf08aeSGordon Ross goto out; 305*fabf08aeSGordon Ross } 306*fabf08aeSGordon Ross 307*fabf08aeSGordon Ross /* 308613a2f6bSGordon Ross * Do the initial connection setup here, so we can 309613a2f6bSGordon Ross * report the outcome to the door client. 310613a2f6bSGordon Ross */ 311613a2f6bSGordon Ross err = smb_iod_connect(ctx); 312613a2f6bSGordon Ross if (err != 0) 313613a2f6bSGordon Ross goto out; 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"); 35615359501SGordon 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