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
main(int argc,char ** argv)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
iod_dispatch(void * cookie,char * argp,size_t argsz,door_desc_t * dp,uint_t n_desc)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
iod_newvc(smb_iod_ssn_t * clnt_ssn)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 *
iod_work(void * arg)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