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]
23 * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
27 * SMBFS I/O Daemon (SMF service)
30 #include <sys/types.h>
33 #include <sys/queue.h>
48 #include <priv_utils.h>
56 #include <netsmb/smb_lib.h>
58 static boolean_t d_flag
= B_FALSE
;
60 /* Keep a list of child processes. */
61 typedef struct _child
{
62 LIST_ENTRY(_child
) list
;
66 static LIST_HEAD(, _child
) child_list
= { 0 };
67 mutex_t cl_mutex
= DEFAULTMUTEX
;
69 static const char smbiod_path
[] = "/usr/lib/smbfs/smbiod";
70 static const char door_path
[] = SMBIOD_SVC_DOOR
;
72 void svc_dispatch(void *cookie
, char *argp
, size_t argsz
,
73 door_desc_t
*dp
, uint_t n_desc
);
74 static int cmd_start(uid_t uid
, gid_t gid
);
75 static int new_child(uid_t uid
, gid_t gid
);
76 static void svc_sigchld(void);
77 static void child_gone(uid_t
, pid_t
, int);
78 static void svc_cleanup(void);
81 child_find_by_pid(pid_t pid
)
85 assert(MUTEX_HELD(&cl_mutex
));
86 LIST_FOREACH(cp
, &child_list
, list
) {
94 child_find_by_uid(uid_t uid
)
98 assert(MUTEX_HELD(&cl_mutex
));
99 LIST_FOREACH(cp
, &child_list
, list
) {
107 * Find out if the service is already running.
108 * Return: true, false.
111 already_running(void)
116 if ((fd
= open(door_path
, O_RDONLY
)) < 0)
119 rc
= door_info(fd
, &info
);
128 * This function will fork off a child process,
129 * from which only the child will return.
131 * The parent exit status is taken as the SMF start method
132 * success or failure, so the parent waits (via pipe read)
133 * for the child to finish initialization before it exits.
134 * Use SMF error codes only on exit.
144 if (pipe(pfds
) < 0) {
146 exit(SMF_EXIT_ERR_FATAL
);
148 if ((pid
= fork1()) == -1) {
150 exit(SMF_EXIT_ERR_FATAL
);
154 * If we're the parent process, wait for either the child to send us
155 * the appropriate exit status over the pipe or for the read to fail
156 * (presumably with 0 for EOF if our child terminated abnormally).
157 * If the read fails, exit with either the child's exit status if it
158 * exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal.
163 if (read(pfds
[0], &st
, sizeof (st
)) == sizeof (st
))
165 if (waitpid(pid
, &st
, 0) == pid
&& WIFEXITED(st
))
166 _exit(WEXITSTATUS(st
));
167 _exit(SMF_EXIT_ERR_FATAL
);
177 daemonize_fini(int pfd
, int rc
)
179 /* Tell parent we're ready. */
180 (void) write(pfd
, &rc
, sizeof (rc
));
185 main(int argc
, char **argv
)
187 sigset_t oldmask
, tmpmask
;
190 int door_fd
= -1, tmp_fd
= -1, pfd
= -1;
192 int rc
= SMF_EXIT_ERR_FATAL
;
193 boolean_t created
= B_FALSE
, attached
= B_FALSE
;
195 /* set locale and text domain for i18n */
196 (void) setlocale(LC_ALL
, "");
197 (void) textdomain(TEXT_DOMAIN
);
199 while ((c
= getopt(argc
, argv
, "d")) != -1) {
202 /* Do debug messages. */
206 fprintf(stderr
, "Usage: %s [-d]\n", argv
[0]);
207 return (SMF_EXIT_ERR_CONFIG
);
211 if (already_running()) {
212 fprintf(stderr
, "%s: already running", argv
[0]);
217 * Raise the fd limit to max
218 * errors here are non-fatal
220 if (getrlimit(RLIMIT_NOFILE
, &rl
) != 0) {
221 fprintf(stderr
, "getrlimit failed, err %d\n", errno
);
222 } else if (rl
.rlim_cur
< rl
.rlim_max
) {
223 rl
.rlim_cur
= rl
.rlim_max
;
224 if (setrlimit(RLIMIT_NOFILE
, &rl
) != 0)
225 fprintf(stderr
, "setrlimit "
226 "RLIMIT_NOFILE %d, err %d",
227 (int)rl
.rlim_cur
, errno
);
231 * Want all signals blocked, as we're doing
232 * synchronous delivery via sigwait below.
234 sigfillset(&tmpmask
);
235 sigprocmask(SIG_BLOCK
, &tmpmask
, &oldmask
);
238 * Do want SIGCHLD, and will waitpid().
240 sa
.sa_flags
= SA_NOCLDSTOP
;
241 sa
.sa_handler
= SIG_DFL
;
242 sigemptyset(&sa
.sa_mask
);
243 sigaction(SIGCHLD
, &sa
, NULL
);
246 * Daemonize, unless debugging.
249 /* debug: run in foregound (not a service) */
250 putenv("SMBFS_DEBUG=1");
252 /* Non-debug: start daemon in the background. */
253 pfd
= daemonize_init();
257 * Create directory for all smbiod doors.
259 if ((mkdir(SMBIOD_RUNDIR
, 0755) < 0) && errno
!= EEXIST
) {
260 perror(SMBIOD_RUNDIR
);
265 * Create a file for the main service door.
268 tmp_fd
= open(door_path
, O_RDWR
|O_CREAT
|O_EXCL
, 0644);
277 /* Setup the door service. */
278 door_fd
= door_create(svc_dispatch
, NULL
,
279 DOOR_REFUSE_DESC
| DOOR_NO_CANCEL
);
281 perror("svc door_create");
285 if (fattach(door_fd
, door_path
) < 0) {
286 fprintf(stderr
, "%s: fattach failed, %s\n",
287 door_path
, strerror(errno
));
293 * Initializations done. Tell start method we're up.
297 daemonize_fini(pfd
, rc
);
302 * Main thread just waits for signals.
305 sig
= sigwait(&tmpmask
);
307 fprintf(stderr
, "main: sig=%d\n", sig
);
312 * The whole process contract gets a SIGTERM
313 * at once. Give children a chance to exit
314 * so we can do normal SIGCHLD cleanup.
315 * Prevent new door_open calls.
322 break; /* normal termination */
329 /* Unexpected signal. */
330 fprintf(stderr
, "svc_main: unexpected sig=%d\n", sig
);
338 door_revoke(door_fd
);
342 /* NB: door threads gone now. */
345 /* If startup error, report to parent. */
347 daemonize_fini(pfd
, rc
);
354 svc_dispatch(void *cookie
, char *argp
, size_t argsz
,
355 door_desc_t
*dp
, uint_t n_desc
)
357 ucred_t
*ucred
= NULL
;
363 * Allow a NULL arg call to check if this
364 * daemon is running. Just return zero.
372 * Get the caller's credentials.
373 * (from client side of door)
375 if (door_ucred(&ucred
) != 0) {
379 uid
= ucred_getruid(ucred
);
380 gid
= ucred_getrgid(ucred
);
383 * Arg is just an int command code.
384 * Reply is also an int.
386 if (argsz
!= sizeof (cmd
)) {
390 bcopy(argp
, &cmd
, sizeof (cmd
));
393 rc
= cmd_start(uid
, gid
);
404 door_return((void *)&rc
, sizeof (rc
), NULL
, 0);
408 * Start a per-user smbiod, if not already running.
411 cmd_start(uid_t uid
, gid_t gid
)
417 mutex_lock(&cl_mutex
);
418 cp
= child_find_by_uid(uid
);
420 /* This UID already has an IOD. */
421 mutex_unlock(&cl_mutex
);
423 fprintf(stderr
, "cmd_start: uid %d"
424 " already has an iod\n", uid
);
430 * OK, create a new child.
432 cp
= malloc(sizeof (*cp
));
434 mutex_unlock(&cl_mutex
);
437 cp
->pid
= 0; /* update below */
439 LIST_INSERT_HEAD(&child_list
, cp
, list
);
440 mutex_unlock(&cl_mutex
);
443 * The child will not have permission to create or
444 * destroy files in SMBIOD_RUNDIR so do that here.
446 snprintf(door_file
, sizeof (door_file
),
447 SMBIOD_USR_DOOR
, cp
->uid
);
449 fd
= open(door_file
, O_RDWR
|O_CREAT
|O_EXCL
, 0600);
454 if (fchown(fd
, uid
, gid
) < 0) {
461 if ((pid
= fork1()) == -1) {
466 (void) new_child(uid
, gid
);
473 fprintf(stderr
, "cmd_start: uid %d new iod, pid %d\n",
482 mutex_lock(&cl_mutex
);
483 LIST_REMOVE(cp
, list
);
484 mutex_unlock(&cl_mutex
);
490 * Assume the passed credentials (from the door client),
491 * drop any extra privileges, and exec the per-user iod.
494 new_child(uid_t uid
, gid_t gid
)
499 flags
= PU_RESETGROUPS
| PU_LIMITPRIVS
| PU_INHERITPRIVS
;
500 rc
= __init_daemon_priv(flags
, uid
, gid
, PRIV_NET_ACCESS
, NULL
);
506 (void) execv(smbiod_path
, argv
);
515 int err
, status
, found
= 0;
517 mutex_lock(&cl_mutex
);
519 while ((pid
= waitpid(-1, &status
, WNOHANG
)) > 0) {
523 fprintf(stderr
, "svc_sigchld: pid %d\n", (int)pid
);
525 cp
= child_find_by_pid(pid
);
527 fprintf(stderr
, "Unknown pid %d\n", (int)pid
);
530 child_gone(cp
->uid
, cp
->pid
, status
);
531 LIST_REMOVE(cp
, list
);
536 mutex_unlock(&cl_mutex
);
538 /* ECHILD is the normal end of loop. */
539 if (pid
< 0 && err
!= ECHILD
)
540 fprintf(stderr
, "svc_sigchld: waitpid err %d\n", err
);
542 fprintf(stderr
, "svc_sigchld: no children?\n");
546 child_gone(uid_t uid
, pid_t pid
, int status
)
552 fprintf(stderr
, "child_gone: uid %d pid %d\n",
555 snprintf(door_file
, sizeof (door_file
),
556 SMBIOD_RUNDIR
"/%d", uid
);
559 if (WIFEXITED(status
)) {
560 x
= WEXITSTATUS(status
);
563 "uid %d, pid %d exit %d",
567 if (WIFSIGNALED(status
)) {
568 x
= WTERMSIG(status
);
570 "uid %d, pid %d signal %d",
576 * Final cleanup before exit. Unlink child doors, etc.
577 * Called while single threaded, so no locks needed here.
578 * The list is normally empty by now due to svc_sigchld
579 * calls during shutdown. But in case there were any
580 * straglers, do cleanup here. Don't bother freeing any
581 * list elements here, as we're exiting.
588 LIST_FOREACH(cp
, &child_list
, list
) {
589 child_gone(cp
->uid
, cp
->pid
, 0);