xref: /illumos-gate/usr/src/cmd/fs.d/smbclnt/smbiod/smbiod.c (revision e0731422366620894c16c1ee6515551c5f00733d)
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