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 (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved. 24 */ 25 26 /* 27 * SMBFS I/O Daemon (Per-user IOD) 28 */ 29 30 #include <sys/types.h> 31 #include <sys/stat.h> 32 #include <sys/note.h> 33 34 #include <errno.h> 35 #include <fcntl.h> 36 #include <signal.h> 37 #include <stdarg.h> 38 #include <stdio.h> 39 #include <string.h> 40 #include <strings.h> 41 #include <stdlib.h> 42 #include <synch.h> 43 #include <time.h> 44 #include <unistd.h> 45 #include <ucred.h> 46 #include <err.h> 47 #include <door.h> 48 #include <libscf.h> 49 #include <locale.h> 50 #include <thread.h> 51 52 #include <netsmb/smb_lib.h> 53 54 #define DPRINT(...) do \ 55 { \ 56 if (smb_debug) \ 57 fprintf(stderr, __VA_ARGS__); \ 58 _NOTE(CONSTCOND) \ 59 } while (0) 60 61 mutex_t iod_mutex = DEFAULTMUTEX; 62 int iod_thr_count; /* threads, excluding main */ 63 int iod_terminating; 64 int iod_alarm_time = 30; /* sec. */ 65 66 void iod_dispatch(void *cookie, char *argp, size_t argsz, 67 door_desc_t *dp, uint_t n_desc); 68 int iod_newvc(smb_iod_ssn_t *clnt_ssn); 69 void * iod_work(void *arg); 70 71 int 72 main(int argc, char **argv) 73 { 74 sigset_t oldmask, tmpmask; 75 char *env, *door_path = NULL; 76 int door_fd = -1; 77 int err, sig; 78 int rc = SMF_EXIT_ERR_FATAL; 79 boolean_t attached = B_FALSE; 80 81 /* set locale and text domain for i18n */ 82 (void) setlocale(LC_ALL, ""); 83 (void) textdomain(TEXT_DOMAIN); 84 85 /* Debugging support. */ 86 if ((env = getenv("SMBFS_DEBUG")) != NULL) { 87 smb_debug = atoi(env); 88 if (smb_debug < 1) 89 smb_debug = 1; 90 iod_alarm_time = 300; 91 } 92 93 /* 94 * If a user runs this command (i.e. by accident) 95 * don't interfere with any already running IOD. 96 */ 97 err = smb_iod_open_door(&door_fd); 98 if (err == 0) { 99 close(door_fd); 100 door_fd = -1; 101 DPRINT("%s: already running\n", argv[0]); 102 exit(SMF_EXIT_OK); 103 } 104 105 /* 106 * Want all signals blocked, as we're doing 107 * synchronous delivery via sigwait below. 108 */ 109 sigfillset(&tmpmask); 110 sigprocmask(SIG_BLOCK, &tmpmask, &oldmask); 111 112 /* Setup the door service. */ 113 door_path = smb_iod_door_path(); 114 door_fd = door_create(iod_dispatch, NULL, 115 DOOR_REFUSE_DESC | DOOR_NO_CANCEL); 116 if (door_fd == -1) { 117 perror("iod door_create"); 118 goto out; 119 } 120 fdetach(door_path); 121 if (fattach(door_fd, door_path) < 0) { 122 fprintf(stderr, "%s: fattach failed, %s\n", 123 door_path, strerror(errno)); 124 goto out; 125 } 126 attached = B_TRUE; 127 128 /* Initializations done. */ 129 rc = SMF_EXIT_OK; 130 131 /* 132 * Post the initial alarm, and then just 133 * wait for signals. 134 */ 135 alarm(iod_alarm_time); 136 again: 137 sig = sigwait(&tmpmask); 138 DPRINT("main: sig=%d\n", sig); 139 switch (sig) { 140 case SIGCONT: 141 goto again; 142 143 case SIGALRM: 144 /* No threads active for a while. */ 145 mutex_lock(&iod_mutex); 146 if (iod_thr_count > 0) { 147 /* 148 * Door call thread creation raced with 149 * the alarm. Ignore this alaram. 150 */ 151 mutex_unlock(&iod_mutex); 152 goto again; 153 } 154 /* Prevent a race with iod_thr_count */ 155 iod_terminating = 1; 156 mutex_unlock(&iod_mutex); 157 break; 158 159 case SIGINT: 160 case SIGTERM: 161 break; /* normal termination */ 162 163 default: 164 /* Unexpected signal. */ 165 fprintf(stderr, "iod_main: unexpected sig=%d\n", sig); 166 break; 167 } 168 169 out: 170 iod_terminating = 1; 171 if (attached) 172 fdetach(door_path); 173 if (door_fd != -1) 174 door_revoke(door_fd); 175 176 /* 177 * We need a reference in -lumem to satisfy check_rtime, 178 * else we get build hoise. This is sufficient. 179 */ 180 free(NULL); 181 182 return (rc); 183 } 184 185 /*ARGSUSED*/ 186 void 187 iod_dispatch(void *cookie, char *argp, size_t argsz, 188 door_desc_t *dp, uint_t n_desc) 189 { 190 smb_iod_ssn_t *ssn; 191 ucred_t *ucred; 192 uid_t cl_uid; 193 int rc; 194 195 /* 196 * Verify that the calling process has the same UID. 197 * Paranoia: The door we created has mode 0600, so 198 * this check is probably redundant. 199 */ 200 ucred = NULL; 201 if (door_ucred(&ucred) != 0) { 202 rc = EACCES; 203 goto out; 204 } 205 cl_uid = ucred_getruid(ucred); 206 ucred_free(ucred); 207 ucred = NULL; 208 if (cl_uid != getuid()) { 209 DPRINT("iod_dispatch: wrong UID\n"); 210 rc = EACCES; 211 goto out; 212 } 213 214 /* 215 * The library uses a NULL arg call to check if 216 * the daemon is running. Just return zero. 217 */ 218 if (argp == NULL) { 219 rc = 0; 220 goto out; 221 } 222 223 /* 224 * Otherwise, the arg must be the (fixed size) 225 * smb_iod_ssn_t 226 */ 227 if (argsz != sizeof (*ssn)) { 228 rc = EINVAL; 229 goto out; 230 } 231 232 mutex_lock(&iod_mutex); 233 if (iod_terminating) { 234 mutex_unlock(&iod_mutex); 235 DPRINT("iod_dispatch: terminating\n"); 236 rc = EINTR; 237 goto out; 238 } 239 if (iod_thr_count++ == 0) { 240 alarm(0); 241 DPRINT("iod_dispatch: cancelled alarm\n"); 242 } 243 mutex_unlock(&iod_mutex); 244 245 ssn = (void *) argp; 246 rc = iod_newvc(ssn); 247 248 mutex_lock(&iod_mutex); 249 if (--iod_thr_count == 0) { 250 DPRINT("iod_dispatch: schedule alarm\n"); 251 alarm(iod_alarm_time); 252 } 253 mutex_unlock(&iod_mutex); 254 255 out: 256 door_return((void *)&rc, sizeof (rc), NULL, 0); 257 } 258 259 /* 260 * Try making a connection with the server described by 261 * the info in the smb_iod_ssn_t arg. If successful, 262 * start an IOD thread to service it, then return to 263 * the client side of the door. 264 */ 265 int 266 iod_newvc(smb_iod_ssn_t *clnt_ssn) 267 { 268 smb_ctx_t *ctx; 269 thread_t tid; 270 int err; 271 272 273 /* 274 * This needs to essentially "clone" the smb_ctx_t 275 * from the client side of the door, or at least 276 * as much of it as we need while creating a VC. 277 */ 278 err = smb_ctx_alloc(&ctx); 279 if (err) 280 return (err); 281 bcopy(clnt_ssn, &ctx->ct_iod_ssn, sizeof (ctx->ct_iod_ssn)); 282 283 /* 284 * Do the initial connection setup here, so we can 285 * report the outcome to the door client. 286 */ 287 err = smb_iod_connect(ctx); 288 if (err != 0) 289 goto out; 290 291 /* 292 * Create the driver session now, so we don't 293 * race with the door client findvc call. 294 */ 295 if ((err = smb_ctx_gethandle(ctx)) != 0) 296 goto out; 297 if (ioctl(ctx->ct_dev_fd, SMBIOC_SSN_CREATE, &ctx->ct_ssn) < 0) { 298 err = errno; 299 goto out; 300 } 301 302 /* The rest happens in the iod_work thread. */ 303 err = thr_create(NULL, 0, iod_work, ctx, THR_DETACHED, &tid); 304 if (err == 0) { 305 /* 306 * Given to the new thread. 307 * free at end of iod_work 308 */ 309 ctx = NULL; 310 } 311 312 out: 313 if (ctx) 314 smb_ctx_free(ctx); 315 316 return (err); 317 } 318 319 /* 320 * Be the reader thread for some VC. 321 * 322 * This is started by a door call thread, which means 323 * this is always at least the 2nd thread, therefore 324 * it should never see thr_count==0 or terminating. 325 */ 326 void * 327 iod_work(void *arg) 328 { 329 smb_ctx_t *ctx = arg; 330 331 mutex_lock(&iod_mutex); 332 if (iod_thr_count++ == 0) { 333 alarm(0); 334 DPRINT("iod_work: cancelled alarm\n"); 335 } 336 mutex_unlock(&iod_mutex); 337 338 (void) smb_iod_work(ctx); 339 340 mutex_lock(&iod_mutex); 341 if (--iod_thr_count == 0) { 342 DPRINT("iod_work: schedule alarm\n"); 343 alarm(iod_alarm_time); 344 } 345 mutex_unlock(&iod_mutex); 346 347 smb_ctx_free(ctx); 348 return (NULL); 349 } 350