xref: /illumos-gate/usr/src/cmd/fs.d/smbclnt/smbiod/smbiod.c (revision 24f5a37652e188ebdcdd6da454511686935025df)
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  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
25  */
26 
27 /*
28  * SMBFS I/O Daemon (Per-user IOD)
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 #include <err.h>
48 #include <door.h>
49 #include <libscf.h>
50 #include <locale.h>
51 #include <thread.h>
52 
53 #include <netsmb/smb_lib.h>
54 
55 #define	DPRINT(...)	do \
56 { \
57 	if (smb_debug) \
58 		fprintf(stderr, __VA_ARGS__); \
59 	_NOTE(CONSTCOND) \
60 } while (0)
61 
62 mutex_t	iod_mutex = DEFAULTMUTEX;
63 int iod_thr_count;	/* threads, excluding main */
64 int iod_terminating;
65 int iod_alarm_time = 30; /* sec. */
66 
67 void iod_dispatch(void *cookie, char *argp, size_t argsz,
68     door_desc_t *dp, uint_t n_desc);
69 int iod_newvc(smb_iod_ssn_t *clnt_ssn);
70 void * iod_work(void *arg);
71 
72 int
73 main(int argc, char **argv)
74 {
75 	sigset_t oldmask, tmpmask;
76 	char *env, *door_path = NULL;
77 	int door_fd = -1;
78 	int err, sig;
79 	int rc = SMF_EXIT_ERR_FATAL;
80 	boolean_t attached = B_FALSE;
81 
82 	/* set locale and text domain for i18n */
83 	(void) setlocale(LC_ALL, "");
84 	(void) textdomain(TEXT_DOMAIN);
85 
86 	/* Debugging support. */
87 	if ((env = getenv("SMBFS_DEBUG")) != NULL) {
88 		smb_debug = atoi(env);
89 		if (smb_debug < 1)
90 			smb_debug = 1;
91 		iod_alarm_time = 300;
92 	}
93 
94 	/*
95 	 * If a user runs this command (i.e. by accident)
96 	 * don't interfere with any already running IOD.
97 	 */
98 	err = smb_iod_open_door(&door_fd);
99 	if (err == 0) {
100 		close(door_fd);
101 		door_fd = -1;
102 		DPRINT("%s: already running\n", argv[0]);
103 		exit(SMF_EXIT_OK);
104 	}
105 
106 	/*
107 	 * Want all signals blocked, as we're doing
108 	 * synchronous delivery via sigwait below.
109 	 */
110 	sigfillset(&tmpmask);
111 	sigprocmask(SIG_BLOCK, &tmpmask, &oldmask);
112 
113 	/* Setup the door service. */
114 	door_path = smb_iod_door_path();
115 	door_fd = door_create(iod_dispatch, NULL,
116 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
117 	if (door_fd == -1) {
118 		perror("iod door_create");
119 		goto out;
120 	}
121 	fdetach(door_path);
122 	if (fattach(door_fd, door_path) < 0) {
123 		fprintf(stderr, "%s: fattach failed, %s\n",
124 		    door_path, strerror(errno));
125 		goto out;
126 	}
127 	attached = B_TRUE;
128 
129 	/* Initializations done. */
130 	rc = SMF_EXIT_OK;
131 
132 	/*
133 	 * Post the initial alarm, and then just
134 	 * wait for signals.
135 	 */
136 	alarm(iod_alarm_time);
137 again:
138 	sig = sigwait(&tmpmask);
139 	DPRINT("main: sig=%d\n", sig);
140 	switch (sig) {
141 	case SIGCONT:
142 		goto again;
143 
144 	case SIGALRM:
145 		/* No threads active for a while. */
146 		mutex_lock(&iod_mutex);
147 		if (iod_thr_count > 0) {
148 			/*
149 			 * Door call thread creation raced with
150 			 * the alarm.  Ignore this alaram.
151 			 */
152 			mutex_unlock(&iod_mutex);
153 			goto again;
154 		}
155 		/* Prevent a race with iod_thr_count */
156 		iod_terminating = 1;
157 		mutex_unlock(&iod_mutex);
158 		break;
159 
160 	case SIGINT:
161 	case SIGTERM:
162 		break;	/* normal termination */
163 
164 	default:
165 		/* Unexpected signal. */
166 		fprintf(stderr, "iod_main: unexpected sig=%d\n", sig);
167 		break;
168 	}
169 
170 out:
171 	iod_terminating = 1;
172 	if (attached)
173 		fdetach(door_path);
174 	if (door_fd != -1)
175 		door_revoke(door_fd);
176 
177 	/*
178 	 * We need a reference in -lumem to satisfy check_rtime,
179 	 * else we get build hoise.  This is sufficient.
180 	 */
181 	free(NULL);
182 
183 	return (rc);
184 }
185 
186 /*ARGSUSED*/
187 void
188 iod_dispatch(void *cookie, char *argp, size_t argsz,
189     door_desc_t *dp, uint_t n_desc)
190 {
191 	smb_iod_ssn_t *ssn;
192 	ucred_t *ucred;
193 	uid_t cl_uid;
194 	int rc;
195 
196 	/*
197 	 * Verify that the calling process has the same UID.
198 	 * Paranoia:  The door we created has mode 0600, so
199 	 * this check is probably redundant.
200 	 */
201 	ucred = NULL;
202 	if (door_ucred(&ucred) != 0) {
203 		rc = EACCES;
204 		goto out;
205 	}
206 	cl_uid = ucred_getruid(ucred);
207 	ucred_free(ucred);
208 	ucred = NULL;
209 	if (cl_uid != getuid()) {
210 		DPRINT("iod_dispatch: wrong UID\n");
211 		rc = EACCES;
212 		goto out;
213 	}
214 
215 	/*
216 	 * The library uses a NULL arg call to check if
217 	 * the daemon is running.  Just return zero.
218 	 */
219 	if (argp == NULL) {
220 		rc = 0;
221 		goto out;
222 	}
223 
224 	/*
225 	 * Otherwise, the arg must be the (fixed size)
226 	 * smb_iod_ssn_t
227 	 */
228 	if (argsz != sizeof (*ssn)) {
229 		rc = EINVAL;
230 		goto out;
231 	}
232 
233 	mutex_lock(&iod_mutex);
234 	if (iod_terminating) {
235 		mutex_unlock(&iod_mutex);
236 		DPRINT("iod_dispatch: terminating\n");
237 		rc = EINTR;
238 		goto out;
239 	}
240 	if (iod_thr_count++ == 0) {
241 		alarm(0);
242 		DPRINT("iod_dispatch: cancelled alarm\n");
243 	}
244 	mutex_unlock(&iod_mutex);
245 
246 	ssn = (void *) argp;
247 	rc = iod_newvc(ssn);
248 
249 	mutex_lock(&iod_mutex);
250 	if (--iod_thr_count == 0) {
251 		DPRINT("iod_dispatch: schedule alarm\n");
252 		alarm(iod_alarm_time);
253 	}
254 	mutex_unlock(&iod_mutex);
255 
256 out:
257 	door_return((void *)&rc, sizeof (rc), NULL, 0);
258 }
259 
260 /*
261  * Try making a connection with the server described by
262  * the info in the smb_iod_ssn_t arg.  If successful,
263  * start an IOD thread to service it, then return to
264  * the client side of the door.
265  */
266 int
267 iod_newvc(smb_iod_ssn_t *clnt_ssn)
268 {
269 	smb_ctx_t *ctx;
270 	thread_t tid;
271 	int err;
272 
273 
274 	/*
275 	 * This needs to essentially "clone" the smb_ctx_t
276 	 * from the client side of the door, or at least
277 	 * as much of it as we need while creating a VC.
278 	 */
279 	err = smb_ctx_alloc(&ctx);
280 	if (err)
281 		return (err);
282 	bcopy(clnt_ssn, &ctx->ct_iod_ssn, sizeof (ctx->ct_iod_ssn));
283 
284 	/*
285 	 * Create the driver session first, so that any subsequent
286 	 * requests for the same session will find this one and
287 	 * wait, the same as when a reconnect is triggered.
288 	 *
289 	 * There is still an inherent race here, where two callers
290 	 * both find no VC in the driver, and both come here trying
291 	 * to create the VC.  In this case, we want the first one
292 	 * to actually do the VC setup, and the second to proceed
293 	 * as if the VC had been found in the driver.  The second
294 	 * caller gets an EEXIST error from the ioctl in this case,
295 	 * which we therefore ignore here so that the caller will
296 	 * go ahead and look again in the driver for the new VC.
297 	 */
298 	if ((err = smb_ctx_gethandle(ctx)) != 0)
299 		goto out;
300 	if (ioctl(ctx->ct_dev_fd, SMBIOC_SSN_CREATE, &ctx->ct_ssn) < 0) {
301 		err = errno;
302 		if (err == EEXIST)
303 			err = 0; /* see above */
304 		goto out;
305 	}
306 
307 	/*
308 	 * Do the initial connection setup here, so we can
309 	 * report the outcome to the door client.
310 	 */
311 	err = smb_iod_connect(ctx);
312 	if (err != 0)
313 		goto out;
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(iod_alarm_time);
357 	}
358 	mutex_unlock(&iod_mutex);
359 
360 	smb_ctx_free(ctx);
361 	return (NULL);
362 }
363