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 2009 Sun Microsystems, Inc. All rights reserved. 24 * Use is subject to license terms. 25 */ 26 27 /* 28 * SMBFS I/O Deamon (smbiod) 29 */ 30 31 #include <sys/types.h> 32 #include <sys/stat.h> 33 #include <sys/note.h> 34 35 #include <errno.h> 36 #include <fcntl.h> 37 #include <signal.h> 38 #include <stdarg.h> 39 #include <stdio.h> 40 #include <string.h> 41 #include <strings.h> 42 #include <stdlib.h> 43 #include <synch.h> 44 #include <time.h> 45 #include <unistd.h> 46 #include <ucred.h> 47 48 #include <err.h> 49 #include <door.h> 50 #include <thread.h> 51 52 #include <netsmb/smb_lib.h> 53 54 #define ALARM_TIME 30 /* sec. */ 55 #define EXIT_FAIL 1 56 #define EXIT_OK 0 57 58 #if defined(DEBUG) || defined(__lint) 59 #define DPRINT(...) do \ 60 { \ 61 if (smb_debug) \ 62 fprintf(stderr, __VA_ARGS__); \ 63 _NOTE(CONSTCOND) \ 64 } while (0) 65 #else 66 #define DPRINT(...) ((void)0) 67 #endif 68 69 mutex_t iod_mutex = DEFAULTMUTEX; 70 int iod_thr_count; /* threads, excluding main */ 71 int iod_terminating; 72 73 void iod_dispatch(void *cookie, char *argp, size_t argsz, 74 door_desc_t *dp, uint_t n_desc); 75 int iod_newvc(smb_iod_ssn_t *clnt_ssn); 76 void * iod_work(void *arg); 77 78 int 79 main(int argc, char **argv) 80 { 81 static const int door_attrs = 82 DOOR_REFUSE_DESC | DOOR_NO_CANCEL; 83 sigset_t oldmask, tmpmask; 84 char *env, *door_path = NULL; 85 int door_fd = -1, tmp_fd = -1; 86 int err, i, sig; 87 int rc = EXIT_FAIL; 88 89 /* Debugging support. */ 90 if ((env = getenv("SMBFS_DEBUG")) != NULL) { 91 smb_debug = atoi(env); 92 if (smb_debug < 1) 93 smb_debug = 1; 94 } 95 96 /* 97 * Find out if an IOD is already running. 98 * If so, we lost a harmless startup race. 99 * An IOD did start, so exit success. 100 */ 101 err = smb_iod_open_door(&door_fd); 102 if (err == 0) { 103 close(door_fd); 104 door_fd = -1; 105 DPRINT("main: already running\n"); 106 exit(EXIT_OK); 107 } 108 109 /* 110 * Create a file for the door. 111 */ 112 door_path = smb_iod_door_path(); 113 unlink(door_path); 114 tmp_fd = open(door_path, O_RDWR|O_CREAT|O_EXCL, 0600); 115 if (tmp_fd < 0) { 116 perror(door_path); 117 exit(EXIT_FAIL); 118 } 119 close(tmp_fd); 120 tmp_fd = -1; 121 122 123 /* 124 * Close FDs 0,1,2 so we don't have a TTY, and 125 * re-open them on /dev/null so they won't be 126 * used for device handles (etc.) later, and 127 * we don't have to worry about printf calls 128 * or whatever going to these FDs. 129 */ 130 for (i = 0; i < 3; i++) { 131 /* Exception: If smb_debug, keep stderr */ 132 if (smb_debug && i == 2) 133 break; 134 close(i); 135 tmp_fd = open("/dev/null", O_RDWR); 136 if (tmp_fd < 0) 137 perror("/dev/null"); 138 if (tmp_fd != i) 139 DPRINT("Open /dev/null - wrong fd?\n"); 140 } 141 142 /* 143 * Become session leader. 144 */ 145 setsid(); 146 147 /* 148 * Create door service threads with signals blocked. 149 */ 150 sigfillset(&tmpmask); 151 sigprocmask(SIG_BLOCK, &tmpmask, &oldmask); 152 153 /* Setup the door service. */ 154 door_fd = door_create(iod_dispatch, NULL, door_attrs); 155 if (door_fd < 0) { 156 fprintf(stderr, "%s: door_create failed\n", argv[0]); 157 rc = EXIT_FAIL; 158 goto errout; 159 } 160 fdetach(door_path); 161 if (fattach(door_fd, door_path) < 0) { 162 fprintf(stderr, "%s: fattach failed\n", argv[0]); 163 rc = EXIT_FAIL; 164 goto errout; 165 } 166 167 /* 168 * Post the initial alarm, and then just 169 * wait for signals. 170 */ 171 alarm(ALARM_TIME); 172 again: 173 sig = sigwait(&tmpmask); 174 DPRINT("main: sig=%d\n", sig); 175 176 /* 177 * If a door call races with the alarm, ignore the alarm. 178 * It will be rescheduled when the threads go away. 179 */ 180 mutex_lock(&iod_mutex); 181 if (sig == SIGALRM && iod_thr_count > 0) { 182 mutex_unlock(&iod_mutex); 183 goto again; 184 } 185 iod_terminating = 1; 186 mutex_unlock(&iod_mutex); 187 rc = EXIT_OK; 188 189 errout: 190 fdetach(door_path); 191 door_revoke(door_fd); 192 door_fd = -1; 193 unlink(door_path); 194 195 return (rc); 196 } 197 198 /*ARGSUSED*/ 199 void 200 iod_dispatch(void *cookie, char *argp, size_t argsz, 201 door_desc_t *dp, uint_t n_desc) 202 { 203 smb_iod_ssn_t *ssn; 204 ucred_t *ucred; 205 uid_t cl_uid; 206 int rc; 207 208 /* 209 * Verify that the calling process has the same UID. 210 * Paranoia: The door we created has mode 0600, so 211 * this check is probably redundant. 212 */ 213 ucred = NULL; 214 if (door_ucred(&ucred) != 0) { 215 rc = EACCES; 216 goto out; 217 } 218 cl_uid = ucred_getruid(ucred); 219 ucred_free(ucred); 220 ucred = NULL; 221 if (cl_uid != getuid()) { 222 DPRINT("iod_dispatch: wrong UID\n"); 223 rc = EACCES; 224 goto out; 225 } 226 227 /* 228 * The library uses a NULL arg call to check if 229 * the deamon is running. Just return zero. 230 */ 231 if (argp == NULL) { 232 rc = 0; 233 goto out; 234 } 235 236 /* 237 * Otherwise, the arg must be the (fixed size) 238 * smb_iod_ssn_t 239 */ 240 if (argsz != sizeof (*ssn)) { 241 rc = EINVAL; 242 goto out; 243 } 244 245 mutex_lock(&iod_mutex); 246 if (iod_terminating) { 247 mutex_unlock(&iod_mutex); 248 DPRINT("iod_dispatch: terminating\n"); 249 rc = EINTR; 250 goto out; 251 } 252 if (iod_thr_count++ == 0) { 253 alarm(0); 254 DPRINT("iod_dispatch: cancelled alarm\n"); 255 } 256 mutex_unlock(&iod_mutex); 257 258 ssn = (void *) argp; 259 rc = iod_newvc(ssn); 260 261 mutex_lock(&iod_mutex); 262 if (--iod_thr_count == 0) { 263 DPRINT("iod_dispatch: schedule alarm\n"); 264 alarm(ALARM_TIME); 265 } 266 mutex_unlock(&iod_mutex); 267 268 out: 269 door_return((void *)&rc, sizeof (rc), NULL, 0); 270 } 271 272 /* 273 * Try making a connection with the server described by 274 * the info in the smb_iod_ssn_t arg. If successful, 275 * start an IOD thread to service it, then return to 276 * the client side of the door. 277 */ 278 int 279 iod_newvc(smb_iod_ssn_t *clnt_ssn) 280 { 281 smb_ctx_t *ctx; 282 thread_t tid; 283 int err; 284 285 286 /* 287 * This needs to essentially "clone" the smb_ctx_t 288 * from the client side of the door, or at least 289 * as much of it as we need while creating a VC. 290 */ 291 err = smb_ctx_alloc(&ctx); 292 if (err) 293 return (err); 294 bcopy(clnt_ssn, &ctx->ct_iod_ssn, sizeof (ctx->ct_iod_ssn)); 295 296 /* 297 * Do the initial connection setup here, so we can 298 * report the outcome to the door client. 299 */ 300 err = smb_iod_connect(ctx); 301 if (err != 0) 302 goto out; 303 304 /* 305 * Create the driver session now, so we don't 306 * race with the door client findvc call. 307 */ 308 if ((err = smb_ctx_gethandle(ctx)) != 0) 309 goto out; 310 if (ioctl(ctx->ct_dev_fd, SMBIOC_SSN_CREATE, &ctx->ct_ssn) < 0) { 311 err = errno; 312 goto out; 313 } 314 315 /* The rest happens in the iod_work thread. */ 316 err = thr_create(NULL, 0, iod_work, ctx, THR_DETACHED, &tid); 317 if (err == 0) { 318 /* 319 * Given to the new thread. 320 * free at end of iod_work 321 */ 322 ctx = NULL; 323 } 324 325 out: 326 if (ctx) 327 smb_ctx_free(ctx); 328 329 return (err); 330 } 331 332 /* 333 * Be the reader thread for some VC. 334 * 335 * This is started by a door call thread, which means 336 * this is always at least the 2nd thread, therefore 337 * it should never see thr_count==0 or terminating. 338 */ 339 void * 340 iod_work(void *arg) 341 { 342 smb_ctx_t *ctx = arg; 343 344 mutex_lock(&iod_mutex); 345 if (iod_thr_count++ == 0) { 346 alarm(0); 347 DPRINT("iod_work: cancelled alarm\n"); 348 } 349 mutex_unlock(&iod_mutex); 350 351 (void) smb_iod_work(ctx); 352 353 mutex_lock(&iod_mutex); 354 if (--iod_thr_count == 0) { 355 DPRINT("iod_work: schedule alarm\n"); 356 alarm(ALARM_TIME); 357 } 358 mutex_unlock(&iod_mutex); 359 360 smb_ctx_free(ctx); 361 return (NULL); 362 } 363