xref: /illumos-gate/usr/src/cmd/fs.d/smbclnt/smbiod/smbiod.c (revision fabf08ae7a95a47c3e249ee651d83d26f798bcfa)
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 /*
2315359501SGordon Ross  * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24*fabf08aeSGordon Ross  * Copyright 2012 Nexenta Systems, Inc.  All rights reserved.
25613a2f6bSGordon Ross  */
26613a2f6bSGordon Ross 
27613a2f6bSGordon Ross /*
28a547be5dSGordon Ross  * SMBFS I/O Daemon (Per-user IOD)
29613a2f6bSGordon Ross  */
30613a2f6bSGordon Ross 
31613a2f6bSGordon Ross #include <sys/types.h>
32613a2f6bSGordon Ross #include <sys/stat.h>
33613a2f6bSGordon Ross #include <sys/note.h>
34613a2f6bSGordon Ross 
35613a2f6bSGordon Ross #include <errno.h>
36613a2f6bSGordon Ross #include <fcntl.h>
37613a2f6bSGordon Ross #include <signal.h>
38613a2f6bSGordon Ross #include <stdarg.h>
39613a2f6bSGordon Ross #include <stdio.h>
40613a2f6bSGordon Ross #include <string.h>
41613a2f6bSGordon Ross #include <strings.h>
42613a2f6bSGordon Ross #include <stdlib.h>
43613a2f6bSGordon Ross #include <synch.h>
44613a2f6bSGordon Ross #include <time.h>
45613a2f6bSGordon Ross #include <unistd.h>
46613a2f6bSGordon Ross #include <ucred.h>
47613a2f6bSGordon Ross #include <err.h>
48613a2f6bSGordon Ross #include <door.h>
49a547be5dSGordon Ross #include <libscf.h>
50a547be5dSGordon Ross #include <locale.h>
51613a2f6bSGordon Ross #include <thread.h>
52613a2f6bSGordon Ross 
53613a2f6bSGordon Ross #include <netsmb/smb_lib.h>
54613a2f6bSGordon Ross 
55613a2f6bSGordon Ross #define	DPRINT(...)	do \
56613a2f6bSGordon Ross { \
57613a2f6bSGordon Ross 	if (smb_debug) \
58613a2f6bSGordon Ross 		fprintf(stderr, __VA_ARGS__); \
59613a2f6bSGordon Ross 	_NOTE(CONSTCOND) \
60613a2f6bSGordon Ross } while (0)
61613a2f6bSGordon Ross 
62613a2f6bSGordon Ross mutex_t	iod_mutex = DEFAULTMUTEX;
63613a2f6bSGordon Ross int iod_thr_count;	/* threads, excluding main */
64613a2f6bSGordon Ross int iod_terminating;
6515359501SGordon Ross int iod_alarm_time = 30; /* sec. */
66613a2f6bSGordon Ross 
67613a2f6bSGordon Ross void iod_dispatch(void *cookie, char *argp, size_t argsz,
68613a2f6bSGordon Ross     door_desc_t *dp, uint_t n_desc);
69613a2f6bSGordon Ross int iod_newvc(smb_iod_ssn_t *clnt_ssn);
70613a2f6bSGordon Ross void * iod_work(void *arg);
71613a2f6bSGordon Ross 
72613a2f6bSGordon Ross int
main(int argc,char ** argv)73613a2f6bSGordon Ross main(int argc, char **argv)
74613a2f6bSGordon Ross {
75613a2f6bSGordon Ross 	sigset_t oldmask, tmpmask;
76613a2f6bSGordon Ross 	char *env, *door_path = NULL;
77a547be5dSGordon Ross 	int door_fd = -1;
78a547be5dSGordon Ross 	int err, sig;
79a547be5dSGordon Ross 	int rc = SMF_EXIT_ERR_FATAL;
80a547be5dSGordon Ross 	boolean_t attached = B_FALSE;
81a547be5dSGordon Ross 
82a547be5dSGordon Ross 	/* set locale and text domain for i18n */
83a547be5dSGordon Ross 	(void) setlocale(LC_ALL, "");
84a547be5dSGordon Ross 	(void) textdomain(TEXT_DOMAIN);
85613a2f6bSGordon Ross 
86613a2f6bSGordon Ross 	/* Debugging support. */
87613a2f6bSGordon Ross 	if ((env = getenv("SMBFS_DEBUG")) != NULL) {
88613a2f6bSGordon Ross 		smb_debug = atoi(env);
89613a2f6bSGordon Ross 		if (smb_debug < 1)
90613a2f6bSGordon Ross 			smb_debug = 1;
9115359501SGordon Ross 		iod_alarm_time = 300;
92613a2f6bSGordon Ross 	}
93613a2f6bSGordon Ross 
94613a2f6bSGordon Ross 	/*
95a547be5dSGordon Ross 	 * If a user runs this command (i.e. by accident)
96a547be5dSGordon Ross 	 * don't interfere with any already running IOD.
97613a2f6bSGordon Ross 	 */
98613a2f6bSGordon Ross 	err = smb_iod_open_door(&door_fd);
99613a2f6bSGordon Ross 	if (err == 0) {
100613a2f6bSGordon Ross 		close(door_fd);
101613a2f6bSGordon Ross 		door_fd = -1;
102a547be5dSGordon Ross 		DPRINT("%s: already running\n", argv[0]);
103a547be5dSGordon Ross 		exit(SMF_EXIT_OK);
104613a2f6bSGordon Ross 	}
105613a2f6bSGordon Ross 
106613a2f6bSGordon Ross 	/*
107a547be5dSGordon Ross 	 * Want all signals blocked, as we're doing
108a547be5dSGordon Ross 	 * synchronous delivery via sigwait below.
109613a2f6bSGordon Ross 	 */
110613a2f6bSGordon Ross 	sigfillset(&tmpmask);
111613a2f6bSGordon Ross 	sigprocmask(SIG_BLOCK, &tmpmask, &oldmask);
112613a2f6bSGordon Ross 
113613a2f6bSGordon Ross 	/* Setup the door service. */
114a547be5dSGordon Ross 	door_path = smb_iod_door_path();
115a547be5dSGordon Ross 	door_fd = door_create(iod_dispatch, NULL,
116a547be5dSGordon Ross 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
117a547be5dSGordon Ross 	if (door_fd == -1) {
118a547be5dSGordon Ross 		perror("iod door_create");
119a547be5dSGordon Ross 		goto out;
120613a2f6bSGordon Ross 	}
121613a2f6bSGordon Ross 	fdetach(door_path);
122613a2f6bSGordon Ross 	if (fattach(door_fd, door_path) < 0) {
123a547be5dSGordon Ross 		fprintf(stderr, "%s: fattach failed, %s\n",
124a547be5dSGordon Ross 		    door_path, strerror(errno));
125a547be5dSGordon Ross 		goto out;
126613a2f6bSGordon Ross 	}
127a547be5dSGordon Ross 	attached = B_TRUE;
128a547be5dSGordon Ross 
129a547be5dSGordon Ross 	/* Initializations done. */
130a547be5dSGordon Ross 	rc = SMF_EXIT_OK;
131613a2f6bSGordon Ross 
132613a2f6bSGordon Ross 	/*
133613a2f6bSGordon Ross 	 * Post the initial alarm, and then just
134613a2f6bSGordon Ross 	 * wait for signals.
135613a2f6bSGordon Ross 	 */
13615359501SGordon Ross 	alarm(iod_alarm_time);
137613a2f6bSGordon Ross again:
138613a2f6bSGordon Ross 	sig = sigwait(&tmpmask);
139613a2f6bSGordon Ross 	DPRINT("main: sig=%d\n", sig);
140a547be5dSGordon Ross 	switch (sig) {
141a547be5dSGordon Ross 	case SIGCONT:
142a547be5dSGordon Ross 		goto again;
143613a2f6bSGordon Ross 
144a547be5dSGordon Ross 	case SIGALRM:
145a547be5dSGordon Ross 		/* No threads active for a while. */
146613a2f6bSGordon Ross 		mutex_lock(&iod_mutex);
147a547be5dSGordon Ross 		if (iod_thr_count > 0) {
148a547be5dSGordon Ross 			/*
149a547be5dSGordon Ross 			 * Door call thread creation raced with
150a547be5dSGordon Ross 			 * the alarm.  Ignore this alaram.
151a547be5dSGordon Ross 			 */
152613a2f6bSGordon Ross 			mutex_unlock(&iod_mutex);
153613a2f6bSGordon Ross 			goto again;
154613a2f6bSGordon Ross 		}
155a547be5dSGordon Ross 		/* Prevent a race with iod_thr_count */
156613a2f6bSGordon Ross 		iod_terminating = 1;
157613a2f6bSGordon Ross 		mutex_unlock(&iod_mutex);
158a547be5dSGordon Ross 		break;
159613a2f6bSGordon Ross 
160a547be5dSGordon Ross 	case SIGINT:
161a547be5dSGordon Ross 	case SIGTERM:
162a547be5dSGordon Ross 		break;	/* normal termination */
163a547be5dSGordon Ross 
164a547be5dSGordon Ross 	default:
165a547be5dSGordon Ross 		/* Unexpected signal. */
166a547be5dSGordon Ross 		fprintf(stderr, "iod_main: unexpected sig=%d\n", sig);
167a547be5dSGordon Ross 		break;
168a547be5dSGordon Ross 	}
169a547be5dSGordon Ross 
170a547be5dSGordon Ross out:
171a547be5dSGordon Ross 	iod_terminating = 1;
172a547be5dSGordon Ross 	if (attached)
173613a2f6bSGordon Ross 		fdetach(door_path);
174a547be5dSGordon Ross 	if (door_fd != -1)
175613a2f6bSGordon Ross 		door_revoke(door_fd);
176a547be5dSGordon Ross 
177a547be5dSGordon Ross 	/*
178a547be5dSGordon Ross 	 * We need a reference in -lumem to satisfy check_rtime,
179a547be5dSGordon Ross 	 * else we get build hoise.  This is sufficient.
180a547be5dSGordon Ross 	 */
181a547be5dSGordon Ross 	free(NULL);
182613a2f6bSGordon Ross 
183613a2f6bSGordon Ross 	return (rc);
184613a2f6bSGordon Ross }
185613a2f6bSGordon Ross 
186613a2f6bSGordon Ross /*ARGSUSED*/
187613a2f6bSGordon Ross void
iod_dispatch(void * cookie,char * argp,size_t argsz,door_desc_t * dp,uint_t n_desc)188613a2f6bSGordon Ross iod_dispatch(void *cookie, char *argp, size_t argsz,
189613a2f6bSGordon Ross     door_desc_t *dp, uint_t n_desc)
190613a2f6bSGordon Ross {
191613a2f6bSGordon Ross 	smb_iod_ssn_t *ssn;
192613a2f6bSGordon Ross 	ucred_t *ucred;
193613a2f6bSGordon Ross 	uid_t cl_uid;
194613a2f6bSGordon Ross 	int rc;
195613a2f6bSGordon Ross 
196613a2f6bSGordon Ross 	/*
197613a2f6bSGordon Ross 	 * Verify that the calling process has the same UID.
198613a2f6bSGordon Ross 	 * Paranoia:  The door we created has mode 0600, so
199613a2f6bSGordon Ross 	 * this check is probably redundant.
200613a2f6bSGordon Ross 	 */
201613a2f6bSGordon Ross 	ucred = NULL;
202613a2f6bSGordon Ross 	if (door_ucred(&ucred) != 0) {
203613a2f6bSGordon Ross 		rc = EACCES;
204613a2f6bSGordon Ross 		goto out;
205613a2f6bSGordon Ross 	}
206613a2f6bSGordon Ross 	cl_uid = ucred_getruid(ucred);
207613a2f6bSGordon Ross 	ucred_free(ucred);
208613a2f6bSGordon Ross 	ucred = NULL;
209613a2f6bSGordon Ross 	if (cl_uid != getuid()) {
210613a2f6bSGordon Ross 		DPRINT("iod_dispatch: wrong UID\n");
211613a2f6bSGordon Ross 		rc = EACCES;
212613a2f6bSGordon Ross 		goto out;
213613a2f6bSGordon Ross 	}
214613a2f6bSGordon Ross 
215613a2f6bSGordon Ross 	/*
216613a2f6bSGordon Ross 	 * The library uses a NULL arg call to check if
217a547be5dSGordon Ross 	 * the daemon is running.  Just return zero.
218613a2f6bSGordon Ross 	 */
219613a2f6bSGordon Ross 	if (argp == NULL) {
220613a2f6bSGordon Ross 		rc = 0;
221613a2f6bSGordon Ross 		goto out;
222613a2f6bSGordon Ross 	}
223613a2f6bSGordon Ross 
224613a2f6bSGordon Ross 	/*
225613a2f6bSGordon Ross 	 * Otherwise, the arg must be the (fixed size)
226613a2f6bSGordon Ross 	 * smb_iod_ssn_t
227613a2f6bSGordon Ross 	 */
228613a2f6bSGordon Ross 	if (argsz != sizeof (*ssn)) {
229613a2f6bSGordon Ross 		rc = EINVAL;
230613a2f6bSGordon Ross 		goto out;
231613a2f6bSGordon Ross 	}
232613a2f6bSGordon Ross 
233613a2f6bSGordon Ross 	mutex_lock(&iod_mutex);
234613a2f6bSGordon Ross 	if (iod_terminating) {
235613a2f6bSGordon Ross 		mutex_unlock(&iod_mutex);
236613a2f6bSGordon Ross 		DPRINT("iod_dispatch: terminating\n");
237613a2f6bSGordon Ross 		rc = EINTR;
238613a2f6bSGordon Ross 		goto out;
239613a2f6bSGordon Ross 	}
240613a2f6bSGordon Ross 	if (iod_thr_count++ == 0) {
241613a2f6bSGordon Ross 		alarm(0);
242613a2f6bSGordon Ross 		DPRINT("iod_dispatch: cancelled alarm\n");
243613a2f6bSGordon Ross 	}
244613a2f6bSGordon Ross 	mutex_unlock(&iod_mutex);
245613a2f6bSGordon Ross 
246613a2f6bSGordon Ross 	ssn = (void *) argp;
247613a2f6bSGordon Ross 	rc = iod_newvc(ssn);
248613a2f6bSGordon Ross 
249613a2f6bSGordon Ross 	mutex_lock(&iod_mutex);
250613a2f6bSGordon Ross 	if (--iod_thr_count == 0) {
251613a2f6bSGordon Ross 		DPRINT("iod_dispatch: schedule alarm\n");
25215359501SGordon Ross 		alarm(iod_alarm_time);
253613a2f6bSGordon Ross 	}
254613a2f6bSGordon Ross 	mutex_unlock(&iod_mutex);
255613a2f6bSGordon Ross 
256613a2f6bSGordon Ross out:
257613a2f6bSGordon Ross 	door_return((void *)&rc, sizeof (rc), NULL, 0);
258613a2f6bSGordon Ross }
259613a2f6bSGordon Ross 
260613a2f6bSGordon Ross /*
261613a2f6bSGordon Ross  * Try making a connection with the server described by
262613a2f6bSGordon Ross  * the info in the smb_iod_ssn_t arg.  If successful,
263613a2f6bSGordon Ross  * start an IOD thread to service it, then return to
264613a2f6bSGordon Ross  * the client side of the door.
265613a2f6bSGordon Ross  */
266613a2f6bSGordon Ross int
iod_newvc(smb_iod_ssn_t * clnt_ssn)267613a2f6bSGordon Ross iod_newvc(smb_iod_ssn_t *clnt_ssn)
268613a2f6bSGordon Ross {
269613a2f6bSGordon Ross 	smb_ctx_t *ctx;
270613a2f6bSGordon Ross 	thread_t tid;
271613a2f6bSGordon Ross 	int err;
272613a2f6bSGordon Ross 
273613a2f6bSGordon Ross 
274613a2f6bSGordon Ross 	/*
275613a2f6bSGordon Ross 	 * This needs to essentially "clone" the smb_ctx_t
276613a2f6bSGordon Ross 	 * from the client side of the door, or at least
277613a2f6bSGordon Ross 	 * as much of it as we need while creating a VC.
278613a2f6bSGordon Ross 	 */
279613a2f6bSGordon Ross 	err = smb_ctx_alloc(&ctx);
280613a2f6bSGordon Ross 	if (err)
281613a2f6bSGordon Ross 		return (err);
282613a2f6bSGordon Ross 	bcopy(clnt_ssn, &ctx->ct_iod_ssn, sizeof (ctx->ct_iod_ssn));
283613a2f6bSGordon Ross 
284613a2f6bSGordon Ross 	/*
285*fabf08aeSGordon Ross 	 * Create the driver session first, so that any subsequent
286*fabf08aeSGordon Ross 	 * requests for the same session will find this one and
287*fabf08aeSGordon Ross 	 * wait, the same as when a reconnect is triggered.
288*fabf08aeSGordon Ross 	 *
289*fabf08aeSGordon Ross 	 * There is still an inherent race here, where two callers
290*fabf08aeSGordon Ross 	 * both find no VC in the driver, and both come here trying
291*fabf08aeSGordon Ross 	 * to create the VC.  In this case, we want the first one
292*fabf08aeSGordon Ross 	 * to actually do the VC setup, and the second to proceed
293*fabf08aeSGordon Ross 	 * as if the VC had been found in the driver.  The second
294*fabf08aeSGordon Ross 	 * caller gets an EEXIST error from the ioctl in this case,
295*fabf08aeSGordon Ross 	 * which we therefore ignore here so that the caller will
296*fabf08aeSGordon Ross 	 * go ahead and look again in the driver for the new VC.
297*fabf08aeSGordon Ross 	 */
298*fabf08aeSGordon Ross 	if ((err = smb_ctx_gethandle(ctx)) != 0)
299*fabf08aeSGordon Ross 		goto out;
300*fabf08aeSGordon Ross 	if (ioctl(ctx->ct_dev_fd, SMBIOC_SSN_CREATE, &ctx->ct_ssn) < 0) {
301*fabf08aeSGordon Ross 		err = errno;
302*fabf08aeSGordon Ross 		if (err == EEXIST)
303*fabf08aeSGordon Ross 			err = 0; /* see above */
304*fabf08aeSGordon Ross 		goto out;
305*fabf08aeSGordon Ross 	}
306*fabf08aeSGordon Ross 
307*fabf08aeSGordon Ross 	/*
308613a2f6bSGordon Ross 	 * Do the initial connection setup here, so we can
309613a2f6bSGordon Ross 	 * report the outcome to the door client.
310613a2f6bSGordon Ross 	 */
311613a2f6bSGordon Ross 	err = smb_iod_connect(ctx);
312613a2f6bSGordon Ross 	if (err != 0)
313613a2f6bSGordon Ross 		goto out;
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 *
iod_work(void * arg)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");
35615359501SGordon 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