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