xref: /illumos-gate/usr/src/cmd/fs.d/smbclnt/smbiod/smbiod.c (revision 15359501f7d4b9abebd7b7bf6efd5982a8e7eb27)
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 /*
23*15359501SGordon Ross  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24613a2f6bSGordon Ross  */
25613a2f6bSGordon Ross 
26613a2f6bSGordon Ross /*
27613a2f6bSGordon Ross  * SMBFS I/O Deamon (smbiod)
28613a2f6bSGordon Ross  */
29613a2f6bSGordon Ross 
30613a2f6bSGordon Ross #include <sys/types.h>
31613a2f6bSGordon Ross #include <sys/stat.h>
32613a2f6bSGordon Ross #include <sys/note.h>
33613a2f6bSGordon Ross 
34613a2f6bSGordon Ross #include <errno.h>
35613a2f6bSGordon Ross #include <fcntl.h>
36613a2f6bSGordon Ross #include <signal.h>
37613a2f6bSGordon Ross #include <stdarg.h>
38613a2f6bSGordon Ross #include <stdio.h>
39613a2f6bSGordon Ross #include <string.h>
40613a2f6bSGordon Ross #include <strings.h>
41613a2f6bSGordon Ross #include <stdlib.h>
42613a2f6bSGordon Ross #include <synch.h>
43613a2f6bSGordon Ross #include <time.h>
44613a2f6bSGordon Ross #include <unistd.h>
45613a2f6bSGordon Ross #include <ucred.h>
46613a2f6bSGordon Ross 
47613a2f6bSGordon Ross #include <err.h>
48613a2f6bSGordon Ross #include <door.h>
49613a2f6bSGordon Ross #include <thread.h>
50613a2f6bSGordon Ross 
51613a2f6bSGordon Ross #include <netsmb/smb_lib.h>
52613a2f6bSGordon Ross 
53613a2f6bSGordon Ross #define	EXIT_FAIL	1
54613a2f6bSGordon Ross #define	EXIT_OK		0
55613a2f6bSGordon Ross 
56613a2f6bSGordon Ross #if defined(DEBUG) || defined(__lint)
57613a2f6bSGordon Ross #define	DPRINT(...)	do \
58613a2f6bSGordon Ross { \
59613a2f6bSGordon Ross 	if (smb_debug) \
60613a2f6bSGordon Ross 		fprintf(stderr, __VA_ARGS__); \
61613a2f6bSGordon Ross 	_NOTE(CONSTCOND) \
62613a2f6bSGordon Ross } while (0)
63613a2f6bSGordon Ross #else
64613a2f6bSGordon Ross #define	DPRINT(...) ((void)0)
65613a2f6bSGordon Ross #endif
66613a2f6bSGordon Ross 
67613a2f6bSGordon Ross mutex_t	iod_mutex = DEFAULTMUTEX;
68613a2f6bSGordon Ross int iod_thr_count;	/* threads, excluding main */
69613a2f6bSGordon Ross int iod_terminating;
70*15359501SGordon Ross int iod_alarm_time = 30; /* sec. */
71613a2f6bSGordon Ross 
72613a2f6bSGordon Ross void iod_dispatch(void *cookie, char *argp, size_t argsz,
73613a2f6bSGordon Ross     door_desc_t *dp, uint_t n_desc);
74613a2f6bSGordon Ross int iod_newvc(smb_iod_ssn_t *clnt_ssn);
75613a2f6bSGordon Ross void * iod_work(void *arg);
76613a2f6bSGordon Ross 
77613a2f6bSGordon Ross int
78613a2f6bSGordon Ross main(int argc, char **argv)
79613a2f6bSGordon Ross {
80613a2f6bSGordon Ross 	static const int door_attrs =
81613a2f6bSGordon Ross 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL;
82613a2f6bSGordon Ross 	sigset_t oldmask, tmpmask;
83613a2f6bSGordon Ross 	char *env, *door_path = NULL;
84613a2f6bSGordon Ross 	int door_fd = -1, tmp_fd = -1;
85613a2f6bSGordon Ross 	int err, i, sig;
86613a2f6bSGordon Ross 	int rc = EXIT_FAIL;
87613a2f6bSGordon Ross 
88613a2f6bSGordon Ross 	/* Debugging support. */
89613a2f6bSGordon Ross 	if ((env = getenv("SMBFS_DEBUG")) != NULL) {
90613a2f6bSGordon Ross 		smb_debug = atoi(env);
91613a2f6bSGordon Ross 		if (smb_debug < 1)
92613a2f6bSGordon Ross 			smb_debug = 1;
93*15359501SGordon Ross 		iod_alarm_time = 300;
94613a2f6bSGordon Ross 	}
95613a2f6bSGordon Ross 
96613a2f6bSGordon Ross 	/*
97613a2f6bSGordon Ross 	 * Find out if an IOD is already running.
98613a2f6bSGordon Ross 	 * If so, we lost a harmless startup race.
99613a2f6bSGordon Ross 	 * An IOD did start, so exit success.
100613a2f6bSGordon Ross 	 */
101613a2f6bSGordon Ross 	err = smb_iod_open_door(&door_fd);
102613a2f6bSGordon Ross 	if (err == 0) {
103613a2f6bSGordon Ross 		close(door_fd);
104613a2f6bSGordon Ross 		door_fd = -1;
105613a2f6bSGordon Ross 		DPRINT("main: already running\n");
106613a2f6bSGordon Ross 		exit(EXIT_OK);
107613a2f6bSGordon Ross 	}
108613a2f6bSGordon Ross 
109613a2f6bSGordon Ross 	/*
110613a2f6bSGordon Ross 	 * Create a file for the door.
111613a2f6bSGordon Ross 	 */
112613a2f6bSGordon Ross 	door_path = smb_iod_door_path();
113613a2f6bSGordon Ross 	unlink(door_path);
114613a2f6bSGordon Ross 	tmp_fd = open(door_path, O_RDWR|O_CREAT|O_EXCL, 0600);
115613a2f6bSGordon Ross 	if (tmp_fd < 0) {
116613a2f6bSGordon Ross 		perror(door_path);
117613a2f6bSGordon Ross 		exit(EXIT_FAIL);
118613a2f6bSGordon Ross 	}
119613a2f6bSGordon Ross 	close(tmp_fd);
120613a2f6bSGordon Ross 	tmp_fd = -1;
121613a2f6bSGordon Ross 
122613a2f6bSGordon Ross 
123613a2f6bSGordon Ross 	/*
124613a2f6bSGordon Ross 	 * Close FDs 0,1,2 so we don't have a TTY, and
125613a2f6bSGordon Ross 	 * re-open them on /dev/null so they won't be
126613a2f6bSGordon Ross 	 * used for device handles (etc.) later, and
127613a2f6bSGordon Ross 	 * we don't have to worry about printf calls
128613a2f6bSGordon Ross 	 * or whatever going to these FDs.
129613a2f6bSGordon Ross 	 */
130613a2f6bSGordon Ross 	for (i = 0; i < 3; i++) {
131613a2f6bSGordon Ross 		/* Exception: If smb_debug, keep stderr */
132613a2f6bSGordon Ross 		if (smb_debug && i == 2)
133613a2f6bSGordon Ross 			break;
134613a2f6bSGordon Ross 		close(i);
135613a2f6bSGordon Ross 		tmp_fd = open("/dev/null", O_RDWR);
136613a2f6bSGordon Ross 		if (tmp_fd < 0)
137613a2f6bSGordon Ross 			perror("/dev/null");
138613a2f6bSGordon Ross 		if (tmp_fd != i)
139613a2f6bSGordon Ross 			DPRINT("Open /dev/null - wrong fd?\n");
140613a2f6bSGordon Ross 	}
141613a2f6bSGordon Ross 
142613a2f6bSGordon Ross 	/*
143613a2f6bSGordon Ross 	 * Become session leader.
144613a2f6bSGordon Ross 	 */
145613a2f6bSGordon Ross 	setsid();
146613a2f6bSGordon Ross 
147613a2f6bSGordon Ross 	/*
148613a2f6bSGordon Ross 	 * Create door service threads with signals blocked.
149613a2f6bSGordon Ross 	 */
150613a2f6bSGordon Ross 	sigfillset(&tmpmask);
151613a2f6bSGordon Ross 	sigprocmask(SIG_BLOCK, &tmpmask, &oldmask);
152613a2f6bSGordon Ross 
153613a2f6bSGordon Ross 	/* Setup the door service. */
154613a2f6bSGordon Ross 	door_fd = door_create(iod_dispatch, NULL, door_attrs);
155613a2f6bSGordon Ross 	if (door_fd < 0) {
156613a2f6bSGordon Ross 		fprintf(stderr, "%s: door_create failed\n", argv[0]);
157613a2f6bSGordon Ross 		rc = EXIT_FAIL;
158613a2f6bSGordon Ross 		goto errout;
159613a2f6bSGordon Ross 	}
160613a2f6bSGordon Ross 	fdetach(door_path);
161613a2f6bSGordon Ross 	if (fattach(door_fd, door_path) < 0) {
162613a2f6bSGordon Ross 		fprintf(stderr, "%s: fattach failed\n", argv[0]);
163613a2f6bSGordon Ross 		rc = EXIT_FAIL;
164613a2f6bSGordon Ross 		goto errout;
165613a2f6bSGordon Ross 	}
166613a2f6bSGordon Ross 
167613a2f6bSGordon Ross 	/*
168613a2f6bSGordon Ross 	 * Post the initial alarm, and then just
169613a2f6bSGordon Ross 	 * wait for signals.
170613a2f6bSGordon Ross 	 */
171*15359501SGordon Ross 	alarm(iod_alarm_time);
172613a2f6bSGordon Ross again:
173613a2f6bSGordon Ross 	sig = sigwait(&tmpmask);
174613a2f6bSGordon Ross 	DPRINT("main: sig=%d\n", sig);
175613a2f6bSGordon Ross 
176613a2f6bSGordon Ross 	/*
177613a2f6bSGordon Ross 	 * If a door call races with the alarm, ignore the alarm.
178613a2f6bSGordon Ross 	 * It will be rescheduled when the threads go away.
179613a2f6bSGordon Ross 	 */
180613a2f6bSGordon Ross 	mutex_lock(&iod_mutex);
181613a2f6bSGordon Ross 	if (sig == SIGALRM && iod_thr_count > 0) {
182613a2f6bSGordon Ross 		mutex_unlock(&iod_mutex);
183613a2f6bSGordon Ross 		goto again;
184613a2f6bSGordon Ross 	}
185613a2f6bSGordon Ross 	iod_terminating = 1;
186613a2f6bSGordon Ross 	mutex_unlock(&iod_mutex);
187613a2f6bSGordon Ross 	rc = EXIT_OK;
188613a2f6bSGordon Ross 
189613a2f6bSGordon Ross errout:
190613a2f6bSGordon Ross 	fdetach(door_path);
191613a2f6bSGordon Ross 	door_revoke(door_fd);
192613a2f6bSGordon Ross 	door_fd = -1;
193613a2f6bSGordon Ross 	unlink(door_path);
194613a2f6bSGordon Ross 
195613a2f6bSGordon Ross 	return (rc);
196613a2f6bSGordon Ross }
197613a2f6bSGordon Ross 
198613a2f6bSGordon Ross /*ARGSUSED*/
199613a2f6bSGordon Ross void
200613a2f6bSGordon Ross iod_dispatch(void *cookie, char *argp, size_t argsz,
201613a2f6bSGordon Ross     door_desc_t *dp, uint_t n_desc)
202613a2f6bSGordon Ross {
203613a2f6bSGordon Ross 	smb_iod_ssn_t *ssn;
204613a2f6bSGordon Ross 	ucred_t *ucred;
205613a2f6bSGordon Ross 	uid_t cl_uid;
206613a2f6bSGordon Ross 	int rc;
207613a2f6bSGordon Ross 
208613a2f6bSGordon Ross 	/*
209613a2f6bSGordon Ross 	 * Verify that the calling process has the same UID.
210613a2f6bSGordon Ross 	 * Paranoia:  The door we created has mode 0600, so
211613a2f6bSGordon Ross 	 * this check is probably redundant.
212613a2f6bSGordon Ross 	 */
213613a2f6bSGordon Ross 	ucred = NULL;
214613a2f6bSGordon Ross 	if (door_ucred(&ucred) != 0) {
215613a2f6bSGordon Ross 		rc = EACCES;
216613a2f6bSGordon Ross 		goto out;
217613a2f6bSGordon Ross 	}
218613a2f6bSGordon Ross 	cl_uid = ucred_getruid(ucred);
219613a2f6bSGordon Ross 	ucred_free(ucred);
220613a2f6bSGordon Ross 	ucred = NULL;
221613a2f6bSGordon Ross 	if (cl_uid != getuid()) {
222613a2f6bSGordon Ross 		DPRINT("iod_dispatch: wrong UID\n");
223613a2f6bSGordon Ross 		rc = EACCES;
224613a2f6bSGordon Ross 		goto out;
225613a2f6bSGordon Ross 	}
226613a2f6bSGordon Ross 
227613a2f6bSGordon Ross 	/*
228613a2f6bSGordon Ross 	 * The library uses a NULL arg call to check if
229613a2f6bSGordon Ross 	 * the deamon is running.  Just return zero.
230613a2f6bSGordon Ross 	 */
231613a2f6bSGordon Ross 	if (argp == NULL) {
232613a2f6bSGordon Ross 		rc = 0;
233613a2f6bSGordon Ross 		goto out;
234613a2f6bSGordon Ross 	}
235613a2f6bSGordon Ross 
236613a2f6bSGordon Ross 	/*
237613a2f6bSGordon Ross 	 * Otherwise, the arg must be the (fixed size)
238613a2f6bSGordon Ross 	 * smb_iod_ssn_t
239613a2f6bSGordon Ross 	 */
240613a2f6bSGordon Ross 	if (argsz != sizeof (*ssn)) {
241613a2f6bSGordon Ross 		rc = EINVAL;
242613a2f6bSGordon Ross 		goto out;
243613a2f6bSGordon Ross 	}
244613a2f6bSGordon Ross 
245613a2f6bSGordon Ross 	mutex_lock(&iod_mutex);
246613a2f6bSGordon Ross 	if (iod_terminating) {
247613a2f6bSGordon Ross 		mutex_unlock(&iod_mutex);
248613a2f6bSGordon Ross 		DPRINT("iod_dispatch: terminating\n");
249613a2f6bSGordon Ross 		rc = EINTR;
250613a2f6bSGordon Ross 		goto out;
251613a2f6bSGordon Ross 	}
252613a2f6bSGordon Ross 	if (iod_thr_count++ == 0) {
253613a2f6bSGordon Ross 		alarm(0);
254613a2f6bSGordon Ross 		DPRINT("iod_dispatch: cancelled alarm\n");
255613a2f6bSGordon Ross 	}
256613a2f6bSGordon Ross 	mutex_unlock(&iod_mutex);
257613a2f6bSGordon Ross 
258613a2f6bSGordon Ross 	ssn = (void *) argp;
259613a2f6bSGordon Ross 	rc = iod_newvc(ssn);
260613a2f6bSGordon Ross 
261613a2f6bSGordon Ross 	mutex_lock(&iod_mutex);
262613a2f6bSGordon Ross 	if (--iod_thr_count == 0) {
263613a2f6bSGordon Ross 		DPRINT("iod_dispatch: schedule alarm\n");
264*15359501SGordon Ross 		alarm(iod_alarm_time);
265613a2f6bSGordon Ross 	}
266613a2f6bSGordon Ross 	mutex_unlock(&iod_mutex);
267613a2f6bSGordon Ross 
268613a2f6bSGordon Ross out:
269613a2f6bSGordon Ross 	door_return((void *)&rc, sizeof (rc), NULL, 0);
270613a2f6bSGordon Ross }
271613a2f6bSGordon Ross 
272613a2f6bSGordon Ross /*
273613a2f6bSGordon Ross  * Try making a connection with the server described by
274613a2f6bSGordon Ross  * the info in the smb_iod_ssn_t arg.  If successful,
275613a2f6bSGordon Ross  * start an IOD thread to service it, then return to
276613a2f6bSGordon Ross  * the client side of the door.
277613a2f6bSGordon Ross  */
278613a2f6bSGordon Ross int
279613a2f6bSGordon Ross iod_newvc(smb_iod_ssn_t *clnt_ssn)
280613a2f6bSGordon Ross {
281613a2f6bSGordon Ross 	smb_ctx_t *ctx;
282613a2f6bSGordon Ross 	thread_t tid;
283613a2f6bSGordon Ross 	int err;
284613a2f6bSGordon Ross 
285613a2f6bSGordon Ross 
286613a2f6bSGordon Ross 	/*
287613a2f6bSGordon Ross 	 * This needs to essentially "clone" the smb_ctx_t
288613a2f6bSGordon Ross 	 * from the client side of the door, or at least
289613a2f6bSGordon Ross 	 * as much of it as we need while creating a VC.
290613a2f6bSGordon Ross 	 */
291613a2f6bSGordon Ross 	err = smb_ctx_alloc(&ctx);
292613a2f6bSGordon Ross 	if (err)
293613a2f6bSGordon Ross 		return (err);
294613a2f6bSGordon Ross 	bcopy(clnt_ssn, &ctx->ct_iod_ssn, sizeof (ctx->ct_iod_ssn));
295613a2f6bSGordon Ross 
296613a2f6bSGordon Ross 	/*
297613a2f6bSGordon Ross 	 * Do the initial connection setup here, so we can
298613a2f6bSGordon Ross 	 * report the outcome to the door client.
299613a2f6bSGordon Ross 	 */
300613a2f6bSGordon Ross 	err = smb_iod_connect(ctx);
301613a2f6bSGordon Ross 	if (err != 0)
302613a2f6bSGordon Ross 		goto out;
303613a2f6bSGordon Ross 
304613a2f6bSGordon Ross 	/*
305613a2f6bSGordon Ross 	 * Create the driver session now, so we don't
306613a2f6bSGordon Ross 	 * race with the door client findvc call.
307613a2f6bSGordon Ross 	 */
308613a2f6bSGordon Ross 	if ((err = smb_ctx_gethandle(ctx)) != 0)
309613a2f6bSGordon Ross 		goto out;
310613a2f6bSGordon Ross 	if (ioctl(ctx->ct_dev_fd, SMBIOC_SSN_CREATE, &ctx->ct_ssn) < 0) {
311613a2f6bSGordon Ross 		err = errno;
312613a2f6bSGordon Ross 		goto out;
313613a2f6bSGordon Ross 	}
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");
356*15359501SGordon 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