3514 Spurious errors setting up new SMB client sessions
[illumos-gate.git] / usr / src / cmd / fs.d / smbclnt / smbiod / smbiod.c
blob32fda8d5fb314068b128ac5c86a977b7c87f5b18
1 /*
2 * CDDL HEADER START
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.
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.
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]
19 * CDDL HEADER END
23 * Copyright (c) 2009, 2010, Oracle and/or its affiliates. All rights reserved.
24 * Copyright 2012 Nexenta Systems, Inc. All rights reserved.
28 * SMBFS I/O Daemon (Per-user IOD)
31 #include <sys/types.h>
32 #include <sys/stat.h>
33 #include <sys/note.h>
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>
53 #include <netsmb/smb_lib.h>
55 #define DPRINT(...) do \
56 { \
57 if (smb_debug) \
58 fprintf(stderr, __VA_ARGS__); \
59 _NOTE(CONSTCOND) \
60 } while (0)
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. */
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);
72 int
73 main(int argc, char **argv)
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;
82 /* set locale and text domain for i18n */
83 (void) setlocale(LC_ALL, "");
84 (void) textdomain(TEXT_DOMAIN);
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;
95 * If a user runs this command (i.e. by accident)
96 * don't interfere with any already running IOD.
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);
107 * Want all signals blocked, as we're doing
108 * synchronous delivery via sigwait below.
110 sigfillset(&tmpmask);
111 sigprocmask(SIG_BLOCK, &tmpmask, &oldmask);
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;
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;
127 attached = B_TRUE;
129 /* Initializations done. */
130 rc = SMF_EXIT_OK;
133 * Post the initial alarm, and then just
134 * wait for signals.
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;
144 case SIGALRM:
145 /* No threads active for a while. */
146 mutex_lock(&iod_mutex);
147 if (iod_thr_count > 0) {
149 * Door call thread creation raced with
150 * the alarm. Ignore this alaram.
152 mutex_unlock(&iod_mutex);
153 goto again;
155 /* Prevent a race with iod_thr_count */
156 iod_terminating = 1;
157 mutex_unlock(&iod_mutex);
158 break;
160 case SIGINT:
161 case SIGTERM:
162 break; /* normal termination */
164 default:
165 /* Unexpected signal. */
166 fprintf(stderr, "iod_main: unexpected sig=%d\n", sig);
167 break;
170 out:
171 iod_terminating = 1;
172 if (attached)
173 fdetach(door_path);
174 if (door_fd != -1)
175 door_revoke(door_fd);
178 * We need a reference in -lumem to satisfy check_rtime,
179 * else we get build hoise. This is sufficient.
181 free(NULL);
183 return (rc);
186 /*ARGSUSED*/
187 void
188 iod_dispatch(void *cookie, char *argp, size_t argsz,
189 door_desc_t *dp, uint_t n_desc)
191 smb_iod_ssn_t *ssn;
192 ucred_t *ucred;
193 uid_t cl_uid;
194 int rc;
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.
201 ucred = NULL;
202 if (door_ucred(&ucred) != 0) {
203 rc = EACCES;
204 goto out;
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;
216 * The library uses a NULL arg call to check if
217 * the daemon is running. Just return zero.
219 if (argp == NULL) {
220 rc = 0;
221 goto out;
225 * Otherwise, the arg must be the (fixed size)
226 * smb_iod_ssn_t
228 if (argsz != sizeof (*ssn)) {
229 rc = EINVAL;
230 goto out;
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;
240 if (iod_thr_count++ == 0) {
241 alarm(0);
242 DPRINT("iod_dispatch: cancelled alarm\n");
244 mutex_unlock(&iod_mutex);
246 ssn = (void *) argp;
247 rc = iod_newvc(ssn);
249 mutex_lock(&iod_mutex);
250 if (--iod_thr_count == 0) {
251 DPRINT("iod_dispatch: schedule alarm\n");
252 alarm(iod_alarm_time);
254 mutex_unlock(&iod_mutex);
256 out:
257 door_return((void *)&rc, sizeof (rc), NULL, 0);
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.
267 iod_newvc(smb_iod_ssn_t *clnt_ssn)
269 smb_ctx_t *ctx;
270 thread_t tid;
271 int err;
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.
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));
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.
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.
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;
308 * Do the initial connection setup here, so we can
309 * report the outcome to the door client.
311 err = smb_iod_connect(ctx);
312 if (err != 0)
313 goto out;
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) {
319 * Given to the new thread.
320 * free at end of iod_work
322 ctx = NULL;
325 out:
326 if (ctx)
327 smb_ctx_free(ctx);
329 return (err);
333 * Be the reader thread for some VC.
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.
339 void *
340 iod_work(void *arg)
342 smb_ctx_t *ctx = arg;
344 mutex_lock(&iod_mutex);
345 if (iod_thr_count++ == 0) {
346 alarm(0);
347 DPRINT("iod_work: cancelled alarm\n");
349 mutex_unlock(&iod_mutex);
351 (void) smb_iod_work(ctx);
353 mutex_lock(&iod_mutex);
354 if (--iod_thr_count == 0) {
355 DPRINT("iod_work: schedule alarm\n");
356 alarm(iod_alarm_time);
358 mutex_unlock(&iod_mutex);
360 smb_ctx_free(ctx);
361 return (NULL);