xref: /titanic_51/usr/src/cmd/fs.d/smbclnt/smbiod-svc/smbiod-svc.c (revision a547be5daca7e465ca82df6d179f6b1f8e0cda72)
1*a547be5dSGordon Ross /*
2*a547be5dSGordon Ross  * CDDL HEADER START
3*a547be5dSGordon Ross  *
4*a547be5dSGordon Ross  * The contents of this file are subject to the terms of the
5*a547be5dSGordon Ross  * Common Development and Distribution License (the "License").
6*a547be5dSGordon Ross  * You may not use this file except in compliance with the License.
7*a547be5dSGordon Ross  *
8*a547be5dSGordon Ross  * You can obtain a copy of the license at usr/src/OPENSOLARIS.LICENSE
9*a547be5dSGordon Ross  * or http://www.opensolaris.org/os/licensing.
10*a547be5dSGordon Ross  * See the License for the specific language governing permissions
11*a547be5dSGordon Ross  * and limitations under the License.
12*a547be5dSGordon Ross  *
13*a547be5dSGordon Ross  * When distributing Covered Code, include this CDDL HEADER in each
14*a547be5dSGordon Ross  * file and include the License file at usr/src/OPENSOLARIS.LICENSE.
15*a547be5dSGordon Ross  * If applicable, add the following below this CDDL HEADER, with the
16*a547be5dSGordon Ross  * fields enclosed by brackets "[]" replaced with your own identifying
17*a547be5dSGordon Ross  * information: Portions Copyright [yyyy] [name of copyright owner]
18*a547be5dSGordon Ross  *
19*a547be5dSGordon Ross  * CDDL HEADER END
20*a547be5dSGordon Ross  */
21*a547be5dSGordon Ross 
22*a547be5dSGordon Ross /*
23*a547be5dSGordon Ross  * Copyright (c) 2010, Oracle and/or its affiliates. All rights reserved.
24*a547be5dSGordon Ross  */
25*a547be5dSGordon Ross 
26*a547be5dSGordon Ross /*
27*a547be5dSGordon Ross  * SMBFS I/O Daemon (SMF service)
28*a547be5dSGordon Ross  */
29*a547be5dSGordon Ross 
30*a547be5dSGordon Ross #include <sys/types.h>
31*a547be5dSGordon Ross #include <sys/stat.h>
32*a547be5dSGordon Ross #include <sys/note.h>
33*a547be5dSGordon Ross #include <sys/queue.h>
34*a547be5dSGordon Ross 
35*a547be5dSGordon Ross #include <errno.h>
36*a547be5dSGordon Ross #include <fcntl.h>
37*a547be5dSGordon Ross #include <signal.h>
38*a547be5dSGordon Ross #include <stdarg.h>
39*a547be5dSGordon Ross #include <stdio.h>
40*a547be5dSGordon Ross #include <string.h>
41*a547be5dSGordon Ross #include <strings.h>
42*a547be5dSGordon Ross #include <stdlib.h>
43*a547be5dSGordon Ross #include <synch.h>
44*a547be5dSGordon Ross #include <time.h>
45*a547be5dSGordon Ross #include <unistd.h>
46*a547be5dSGordon Ross #include <ucred.h>
47*a547be5dSGordon Ross #include <wait.h>
48*a547be5dSGordon Ross #include <priv_utils.h>
49*a547be5dSGordon Ross #include <err.h>
50*a547be5dSGordon Ross #include <door.h>
51*a547be5dSGordon Ross #include <libscf.h>
52*a547be5dSGordon Ross #include <locale.h>
53*a547be5dSGordon Ross #include <thread.h>
54*a547be5dSGordon Ross #include <assert.h>
55*a547be5dSGordon Ross 
56*a547be5dSGordon Ross #include <netsmb/smb_lib.h>
57*a547be5dSGordon Ross 
58*a547be5dSGordon Ross static boolean_t d_flag = B_FALSE;
59*a547be5dSGordon Ross 
60*a547be5dSGordon Ross /* Keep a list of child processes. */
61*a547be5dSGordon Ross typedef struct _child {
62*a547be5dSGordon Ross 	LIST_ENTRY(_child) list;
63*a547be5dSGordon Ross 	pid_t pid;
64*a547be5dSGordon Ross 	uid_t uid;
65*a547be5dSGordon Ross } child_t;
66*a547be5dSGordon Ross static LIST_HEAD(, _child) child_list = { 0 };
67*a547be5dSGordon Ross mutex_t	cl_mutex = DEFAULTMUTEX;
68*a547be5dSGordon Ross 
69*a547be5dSGordon Ross static const char smbiod_path[] = "/usr/lib/smbfs/smbiod";
70*a547be5dSGordon Ross static const char door_path[] = SMBIOD_SVC_DOOR;
71*a547be5dSGordon Ross 
72*a547be5dSGordon Ross void svc_dispatch(void *cookie, char *argp, size_t argsz,
73*a547be5dSGordon Ross     door_desc_t *dp, uint_t n_desc);
74*a547be5dSGordon Ross static int cmd_start(uid_t uid, gid_t gid);
75*a547be5dSGordon Ross static int new_child(uid_t uid, gid_t gid);
76*a547be5dSGordon Ross static void svc_sigchld(void);
77*a547be5dSGordon Ross static void child_gone(uid_t, pid_t, int);
78*a547be5dSGordon Ross static void svc_cleanup(void);
79*a547be5dSGordon Ross 
80*a547be5dSGordon Ross static child_t *
81*a547be5dSGordon Ross child_find_by_pid(pid_t pid)
82*a547be5dSGordon Ross {
83*a547be5dSGordon Ross 	child_t *cp;
84*a547be5dSGordon Ross 
85*a547be5dSGordon Ross 	assert(MUTEX_HELD(&cl_mutex));
86*a547be5dSGordon Ross 	LIST_FOREACH(cp, &child_list, list) {
87*a547be5dSGordon Ross 		if (cp->pid == pid)
88*a547be5dSGordon Ross 			return (cp);
89*a547be5dSGordon Ross 	}
90*a547be5dSGordon Ross 	return (NULL);
91*a547be5dSGordon Ross }
92*a547be5dSGordon Ross 
93*a547be5dSGordon Ross static child_t *
94*a547be5dSGordon Ross child_find_by_uid(uid_t uid)
95*a547be5dSGordon Ross {
96*a547be5dSGordon Ross 	child_t *cp;
97*a547be5dSGordon Ross 
98*a547be5dSGordon Ross 	assert(MUTEX_HELD(&cl_mutex));
99*a547be5dSGordon Ross 	LIST_FOREACH(cp, &child_list, list) {
100*a547be5dSGordon Ross 		if (cp->uid == uid)
101*a547be5dSGordon Ross 			return (cp);
102*a547be5dSGordon Ross 	}
103*a547be5dSGordon Ross 	return (NULL);
104*a547be5dSGordon Ross }
105*a547be5dSGordon Ross 
106*a547be5dSGordon Ross /*
107*a547be5dSGordon Ross  * Find out if the service is already running.
108*a547be5dSGordon Ross  * Return: true, false.
109*a547be5dSGordon Ross  */
110*a547be5dSGordon Ross static boolean_t
111*a547be5dSGordon Ross already_running(void)
112*a547be5dSGordon Ross {
113*a547be5dSGordon Ross 	door_info_t info;
114*a547be5dSGordon Ross 	int fd, rc;
115*a547be5dSGordon Ross 
116*a547be5dSGordon Ross 	if ((fd = open(door_path, O_RDONLY)) < 0)
117*a547be5dSGordon Ross 		return (B_FALSE);
118*a547be5dSGordon Ross 
119*a547be5dSGordon Ross 	rc = door_info(fd, &info);
120*a547be5dSGordon Ross 	close(fd);
121*a547be5dSGordon Ross 	if (rc < 0)
122*a547be5dSGordon Ross 		return (B_FALSE);
123*a547be5dSGordon Ross 
124*a547be5dSGordon Ross 	return (B_TRUE);
125*a547be5dSGordon Ross }
126*a547be5dSGordon Ross 
127*a547be5dSGordon Ross /*
128*a547be5dSGordon Ross  * This function will fork off a child process,
129*a547be5dSGordon Ross  * from which only the child will return.
130*a547be5dSGordon Ross  *
131*a547be5dSGordon Ross  * The parent exit status is taken as the SMF start method
132*a547be5dSGordon Ross  * success or failure, so the parent waits (via pipe read)
133*a547be5dSGordon Ross  * for the child to finish initialization before it exits.
134*a547be5dSGordon Ross  * Use SMF error codes only on exit.
135*a547be5dSGordon Ross  */
136*a547be5dSGordon Ross static int
137*a547be5dSGordon Ross daemonize_init(void)
138*a547be5dSGordon Ross {
139*a547be5dSGordon Ross 	int pid, st;
140*a547be5dSGordon Ross 	int pfds[2];
141*a547be5dSGordon Ross 
142*a547be5dSGordon Ross 	chdir("/");
143*a547be5dSGordon Ross 
144*a547be5dSGordon Ross 	if (pipe(pfds) < 0) {
145*a547be5dSGordon Ross 		perror("pipe");
146*a547be5dSGordon Ross 		exit(SMF_EXIT_ERR_FATAL);
147*a547be5dSGordon Ross 	}
148*a547be5dSGordon Ross 	if ((pid = fork1()) == -1) {
149*a547be5dSGordon Ross 		perror("fork");
150*a547be5dSGordon Ross 		exit(SMF_EXIT_ERR_FATAL);
151*a547be5dSGordon Ross 	}
152*a547be5dSGordon Ross 
153*a547be5dSGordon Ross 	/*
154*a547be5dSGordon Ross 	 * If we're the parent process, wait for either the child to send us
155*a547be5dSGordon Ross 	 * the appropriate exit status over the pipe or for the read to fail
156*a547be5dSGordon Ross 	 * (presumably with 0 for EOF if our child terminated abnormally).
157*a547be5dSGordon Ross 	 * If the read fails, exit with either the child's exit status if it
158*a547be5dSGordon Ross 	 * exited or with SMF_EXIT_ERR_FATAL if it died from a fatal signal.
159*a547be5dSGordon Ross 	 */
160*a547be5dSGordon Ross 	if (pid != 0) {
161*a547be5dSGordon Ross 		/* parent */
162*a547be5dSGordon Ross 		close(pfds[1]);
163*a547be5dSGordon Ross 		if (read(pfds[0], &st, sizeof (st)) == sizeof (st))
164*a547be5dSGordon Ross 			_exit(st);
165*a547be5dSGordon Ross 		if (waitpid(pid, &st, 0) == pid && WIFEXITED(st))
166*a547be5dSGordon Ross 			_exit(WEXITSTATUS(st));
167*a547be5dSGordon Ross 		_exit(SMF_EXIT_ERR_FATAL);
168*a547be5dSGordon Ross 	}
169*a547be5dSGordon Ross 
170*a547be5dSGordon Ross 	/* child */
171*a547be5dSGordon Ross 	close(pfds[0]);
172*a547be5dSGordon Ross 
173*a547be5dSGordon Ross 	return (pfds[1]);
174*a547be5dSGordon Ross }
175*a547be5dSGordon Ross 
176*a547be5dSGordon Ross static void
177*a547be5dSGordon Ross daemonize_fini(int pfd, int rc)
178*a547be5dSGordon Ross {
179*a547be5dSGordon Ross 	/* Tell parent we're ready. */
180*a547be5dSGordon Ross 	(void) write(pfd, &rc, sizeof (rc));
181*a547be5dSGordon Ross 	close(pfd);
182*a547be5dSGordon Ross }
183*a547be5dSGordon Ross 
184*a547be5dSGordon Ross int
185*a547be5dSGordon Ross main(int argc, char **argv)
186*a547be5dSGordon Ross {
187*a547be5dSGordon Ross 	sigset_t oldmask, tmpmask;
188*a547be5dSGordon Ross 	struct sigaction sa;
189*a547be5dSGordon Ross 	struct rlimit rl;
190*a547be5dSGordon Ross 	int door_fd = -1, tmp_fd = -1, pfd = -1;
191*a547be5dSGordon Ross 	int c, sig;
192*a547be5dSGordon Ross 	int rc = SMF_EXIT_ERR_FATAL;
193*a547be5dSGordon Ross 	boolean_t created = B_FALSE, attached = B_FALSE;
194*a547be5dSGordon Ross 
195*a547be5dSGordon Ross 	/* set locale and text domain for i18n */
196*a547be5dSGordon Ross 	(void) setlocale(LC_ALL, "");
197*a547be5dSGordon Ross 	(void) textdomain(TEXT_DOMAIN);
198*a547be5dSGordon Ross 
199*a547be5dSGordon Ross 	while ((c = getopt(argc, argv, "d")) != -1) {
200*a547be5dSGordon Ross 		switch (c) {
201*a547be5dSGordon Ross 		case 'd':
202*a547be5dSGordon Ross 			/* Do debug messages. */
203*a547be5dSGordon Ross 			d_flag = B_TRUE;
204*a547be5dSGordon Ross 			break;
205*a547be5dSGordon Ross 		default:
206*a547be5dSGordon Ross 			fprintf(stderr, "Usage: %s [-d]\n", argv[0]);
207*a547be5dSGordon Ross 			return (SMF_EXIT_ERR_CONFIG);
208*a547be5dSGordon Ross 		}
209*a547be5dSGordon Ross 	}
210*a547be5dSGordon Ross 
211*a547be5dSGordon Ross 	if (already_running()) {
212*a547be5dSGordon Ross 		fprintf(stderr, "%s: already running", argv[0]);
213*a547be5dSGordon Ross 		return (rc);
214*a547be5dSGordon Ross 	}
215*a547be5dSGordon Ross 
216*a547be5dSGordon Ross 	/*
217*a547be5dSGordon Ross 	 * Raise the fd limit to max
218*a547be5dSGordon Ross 	 * errors here are non-fatal
219*a547be5dSGordon Ross 	 */
220*a547be5dSGordon Ross 	if (getrlimit(RLIMIT_NOFILE, &rl) != 0) {
221*a547be5dSGordon Ross 		fprintf(stderr, "getrlimit failed, err %d\n", errno);
222*a547be5dSGordon Ross 	} else if (rl.rlim_cur < rl.rlim_max) {
223*a547be5dSGordon Ross 		rl.rlim_cur = rl.rlim_max;
224*a547be5dSGordon Ross 		if (setrlimit(RLIMIT_NOFILE, &rl) != 0)
225*a547be5dSGordon Ross 			fprintf(stderr, "setrlimit "
226*a547be5dSGordon Ross 			    "RLIMIT_NOFILE %d, err %d",
227*a547be5dSGordon Ross 			    (int)rl.rlim_cur, errno);
228*a547be5dSGordon Ross 	}
229*a547be5dSGordon Ross 
230*a547be5dSGordon Ross 	/*
231*a547be5dSGordon Ross 	 * Want all signals blocked, as we're doing
232*a547be5dSGordon Ross 	 * synchronous delivery via sigwait below.
233*a547be5dSGordon Ross 	 */
234*a547be5dSGordon Ross 	sigfillset(&tmpmask);
235*a547be5dSGordon Ross 	sigprocmask(SIG_BLOCK, &tmpmask, &oldmask);
236*a547be5dSGordon Ross 
237*a547be5dSGordon Ross 	/*
238*a547be5dSGordon Ross 	 * Do want SIGCHLD, and will waitpid().
239*a547be5dSGordon Ross 	 */
240*a547be5dSGordon Ross 	sa.sa_flags = SA_NOCLDSTOP;
241*a547be5dSGordon Ross 	sa.sa_handler = SIG_DFL;
242*a547be5dSGordon Ross 	sigemptyset(&sa.sa_mask);
243*a547be5dSGordon Ross 	sigaction(SIGCHLD, &sa, NULL);
244*a547be5dSGordon Ross 
245*a547be5dSGordon Ross 	/*
246*a547be5dSGordon Ross 	 * Daemonize, unless debugging.
247*a547be5dSGordon Ross 	 */
248*a547be5dSGordon Ross 	if (d_flag) {
249*a547be5dSGordon Ross 		/* debug: run in foregound (not a service) */
250*a547be5dSGordon Ross 		putenv("SMBFS_DEBUG=1");
251*a547be5dSGordon Ross 	} else {
252*a547be5dSGordon Ross 		/* Non-debug: start daemon in the background. */
253*a547be5dSGordon Ross 		pfd = daemonize_init();
254*a547be5dSGordon Ross 	}
255*a547be5dSGordon Ross 
256*a547be5dSGordon Ross 	/*
257*a547be5dSGordon Ross 	 * Create directory for all smbiod doors.
258*a547be5dSGordon Ross 	 */
259*a547be5dSGordon Ross 	if ((mkdir(SMBIOD_RUNDIR, 0755) < 0) && errno != EEXIST) {
260*a547be5dSGordon Ross 		perror(SMBIOD_RUNDIR);
261*a547be5dSGordon Ross 		goto out;
262*a547be5dSGordon Ross 	}
263*a547be5dSGordon Ross 
264*a547be5dSGordon Ross 	/*
265*a547be5dSGordon Ross 	 * Create a file for the main service door.
266*a547be5dSGordon Ross 	 */
267*a547be5dSGordon Ross 	unlink(door_path);
268*a547be5dSGordon Ross 	tmp_fd = open(door_path, O_RDWR|O_CREAT|O_EXCL, 0644);
269*a547be5dSGordon Ross 	if (tmp_fd < 0) {
270*a547be5dSGordon Ross 		perror(door_path);
271*a547be5dSGordon Ross 		goto out;
272*a547be5dSGordon Ross 	}
273*a547be5dSGordon Ross 	close(tmp_fd);
274*a547be5dSGordon Ross 	tmp_fd = -1;
275*a547be5dSGordon Ross 	created = B_TRUE;
276*a547be5dSGordon Ross 
277*a547be5dSGordon Ross 	/* Setup the door service. */
278*a547be5dSGordon Ross 	door_fd = door_create(svc_dispatch, NULL,
279*a547be5dSGordon Ross 	    DOOR_REFUSE_DESC | DOOR_NO_CANCEL);
280*a547be5dSGordon Ross 	if (door_fd == -1) {
281*a547be5dSGordon Ross 		perror("svc door_create");
282*a547be5dSGordon Ross 		goto out;
283*a547be5dSGordon Ross 	}
284*a547be5dSGordon Ross 	fdetach(door_path);
285*a547be5dSGordon Ross 	if (fattach(door_fd, door_path) < 0) {
286*a547be5dSGordon Ross 		fprintf(stderr, "%s: fattach failed, %s\n",
287*a547be5dSGordon Ross 		    door_path, strerror(errno));
288*a547be5dSGordon Ross 		goto out;
289*a547be5dSGordon Ross 	}
290*a547be5dSGordon Ross 	attached = B_TRUE;
291*a547be5dSGordon Ross 
292*a547be5dSGordon Ross 	/*
293*a547be5dSGordon Ross 	 * Initializations done.  Tell start method we're up.
294*a547be5dSGordon Ross 	 */
295*a547be5dSGordon Ross 	rc = SMF_EXIT_OK;
296*a547be5dSGordon Ross 	if (pfd != -1) {
297*a547be5dSGordon Ross 		daemonize_fini(pfd, rc);
298*a547be5dSGordon Ross 		pfd = -1;
299*a547be5dSGordon Ross 	}
300*a547be5dSGordon Ross 
301*a547be5dSGordon Ross 	/*
302*a547be5dSGordon Ross 	 * Main thread just waits for signals.
303*a547be5dSGordon Ross 	 */
304*a547be5dSGordon Ross again:
305*a547be5dSGordon Ross 	sig = sigwait(&tmpmask);
306*a547be5dSGordon Ross 	if (d_flag)
307*a547be5dSGordon Ross 		fprintf(stderr, "main: sig=%d\n", sig);
308*a547be5dSGordon Ross 	switch (sig) {
309*a547be5dSGordon Ross 	case SIGINT:
310*a547be5dSGordon Ross 	case SIGTERM:
311*a547be5dSGordon Ross 		/*
312*a547be5dSGordon Ross 		 * The whole process contract gets a SIGTERM
313*a547be5dSGordon Ross 		 * at once.  Give children a chance to exit
314*a547be5dSGordon Ross 		 * so we can do normal SIGCHLD cleanup.
315*a547be5dSGordon Ross 		 * Prevent new door_open calls.
316*a547be5dSGordon Ross 		 */
317*a547be5dSGordon Ross 		fdetach(door_path);
318*a547be5dSGordon Ross 		attached = B_FALSE;
319*a547be5dSGordon Ross 		alarm(2);
320*a547be5dSGordon Ross 		goto again;
321*a547be5dSGordon Ross 	case SIGALRM:
322*a547be5dSGordon Ross 		break;	/* normal termination */
323*a547be5dSGordon Ross 	case SIGCHLD:
324*a547be5dSGordon Ross 		svc_sigchld();
325*a547be5dSGordon Ross 		goto again;
326*a547be5dSGordon Ross 	case SIGCONT:
327*a547be5dSGordon Ross 		goto again;
328*a547be5dSGordon Ross 	default:
329*a547be5dSGordon Ross 		/* Unexpected signal. */
330*a547be5dSGordon Ross 		fprintf(stderr, "svc_main: unexpected sig=%d\n", sig);
331*a547be5dSGordon Ross 		break;
332*a547be5dSGordon Ross 	}
333*a547be5dSGordon Ross 
334*a547be5dSGordon Ross out:
335*a547be5dSGordon Ross 	if (attached)
336*a547be5dSGordon Ross 		fdetach(door_path);
337*a547be5dSGordon Ross 	if (door_fd != -1)
338*a547be5dSGordon Ross 		door_revoke(door_fd);
339*a547be5dSGordon Ross 	if (created)
340*a547be5dSGordon Ross 		unlink(door_path);
341*a547be5dSGordon Ross 
342*a547be5dSGordon Ross 	/* NB: door threads gone now. */
343*a547be5dSGordon Ross 	svc_cleanup();
344*a547be5dSGordon Ross 
345*a547be5dSGordon Ross 	/* If startup error, report to parent. */
346*a547be5dSGordon Ross 	if (pfd != -1)
347*a547be5dSGordon Ross 		daemonize_fini(pfd, rc);
348*a547be5dSGordon Ross 
349*a547be5dSGordon Ross 	return (rc);
350*a547be5dSGordon Ross }
351*a547be5dSGordon Ross 
352*a547be5dSGordon Ross /*ARGSUSED*/
353*a547be5dSGordon Ross void
354*a547be5dSGordon Ross svc_dispatch(void *cookie, char *argp, size_t argsz,
355*a547be5dSGordon Ross     door_desc_t *dp, uint_t n_desc)
356*a547be5dSGordon Ross {
357*a547be5dSGordon Ross 	ucred_t *ucred = NULL;
358*a547be5dSGordon Ross 	uid_t uid;
359*a547be5dSGordon Ross 	gid_t gid;
360*a547be5dSGordon Ross 	int32_t cmd, rc;
361*a547be5dSGordon Ross 
362*a547be5dSGordon Ross 	/*
363*a547be5dSGordon Ross 	 * Allow a NULL arg call to check if this
364*a547be5dSGordon Ross 	 * daemon is running.  Just return zero.
365*a547be5dSGordon Ross 	 */
366*a547be5dSGordon Ross 	if (argp == NULL) {
367*a547be5dSGordon Ross 		rc = 0;
368*a547be5dSGordon Ross 		goto out;
369*a547be5dSGordon Ross 	}
370*a547be5dSGordon Ross 
371*a547be5dSGordon Ross 	/*
372*a547be5dSGordon Ross 	 * Get the caller's credentials.
373*a547be5dSGordon Ross 	 * (from client side of door)
374*a547be5dSGordon Ross 	 */
375*a547be5dSGordon Ross 	if (door_ucred(&ucred) != 0) {
376*a547be5dSGordon Ross 		rc = EACCES;
377*a547be5dSGordon Ross 		goto out;
378*a547be5dSGordon Ross 	}
379*a547be5dSGordon Ross 	uid = ucred_getruid(ucred);
380*a547be5dSGordon Ross 	gid = ucred_getrgid(ucred);
381*a547be5dSGordon Ross 
382*a547be5dSGordon Ross 	/*
383*a547be5dSGordon Ross 	 * Arg is just an int command code.
384*a547be5dSGordon Ross 	 * Reply is also an int.
385*a547be5dSGordon Ross 	 */
386*a547be5dSGordon Ross 	if (argsz != sizeof (cmd)) {
387*a547be5dSGordon Ross 		rc = EINVAL;
388*a547be5dSGordon Ross 		goto out;
389*a547be5dSGordon Ross 	}
390*a547be5dSGordon Ross 	bcopy(argp, &cmd, sizeof (cmd));
391*a547be5dSGordon Ross 	switch (cmd) {
392*a547be5dSGordon Ross 	case SMBIOD_START:
393*a547be5dSGordon Ross 		rc = cmd_start(uid, gid);
394*a547be5dSGordon Ross 		break;
395*a547be5dSGordon Ross 	default:
396*a547be5dSGordon Ross 		rc = EINVAL;
397*a547be5dSGordon Ross 		goto out;
398*a547be5dSGordon Ross 	}
399*a547be5dSGordon Ross 
400*a547be5dSGordon Ross out:
401*a547be5dSGordon Ross 	if (ucred != NULL)
402*a547be5dSGordon Ross 		ucred_free(ucred);
403*a547be5dSGordon Ross 
404*a547be5dSGordon Ross 	door_return((void *)&rc, sizeof (rc), NULL, 0);
405*a547be5dSGordon Ross }
406*a547be5dSGordon Ross 
407*a547be5dSGordon Ross /*
408*a547be5dSGordon Ross  * Start a per-user smbiod, if not already running.
409*a547be5dSGordon Ross  */
410*a547be5dSGordon Ross int
411*a547be5dSGordon Ross cmd_start(uid_t uid, gid_t gid)
412*a547be5dSGordon Ross {
413*a547be5dSGordon Ross 	char door_file[64];
414*a547be5dSGordon Ross 	child_t *cp;
415*a547be5dSGordon Ross 	int pid, fd = -1;
416*a547be5dSGordon Ross 
417*a547be5dSGordon Ross 	mutex_lock(&cl_mutex);
418*a547be5dSGordon Ross 	cp = child_find_by_uid(uid);
419*a547be5dSGordon Ross 	if (cp != NULL) {
420*a547be5dSGordon Ross 		/* This UID already has an IOD. */
421*a547be5dSGordon Ross 		mutex_unlock(&cl_mutex);
422*a547be5dSGordon Ross 		if (d_flag) {
423*a547be5dSGordon Ross 			fprintf(stderr, "cmd_start: uid %d"
424*a547be5dSGordon Ross 			    " already has an iod\n", uid);
425*a547be5dSGordon Ross 		}
426*a547be5dSGordon Ross 		return (0);
427*a547be5dSGordon Ross 	}
428*a547be5dSGordon Ross 
429*a547be5dSGordon Ross 	/*
430*a547be5dSGordon Ross 	 * OK, create a new child.
431*a547be5dSGordon Ross 	 */
432*a547be5dSGordon Ross 	cp = malloc(sizeof (*cp));
433*a547be5dSGordon Ross 	if (cp == NULL) {
434*a547be5dSGordon Ross 		mutex_unlock(&cl_mutex);
435*a547be5dSGordon Ross 		return (ENOMEM);
436*a547be5dSGordon Ross 	}
437*a547be5dSGordon Ross 	cp->pid = 0; /* update below */
438*a547be5dSGordon Ross 	cp->uid = uid;
439*a547be5dSGordon Ross 	LIST_INSERT_HEAD(&child_list, cp, list);
440*a547be5dSGordon Ross 	mutex_unlock(&cl_mutex);
441*a547be5dSGordon Ross 
442*a547be5dSGordon Ross 	/*
443*a547be5dSGordon Ross 	 * The child will not have permission to create or
444*a547be5dSGordon Ross 	 * destroy files in SMBIOD_RUNDIR so do that here.
445*a547be5dSGordon Ross 	 */
446*a547be5dSGordon Ross 	snprintf(door_file, sizeof (door_file),
447*a547be5dSGordon Ross 	    SMBIOD_USR_DOOR, cp->uid);
448*a547be5dSGordon Ross 	unlink(door_file);
449*a547be5dSGordon Ross 	fd = open(door_file, O_RDWR|O_CREAT|O_EXCL, 0600);
450*a547be5dSGordon Ross 	if (fd < 0) {
451*a547be5dSGordon Ross 		perror(door_file);
452*a547be5dSGordon Ross 		goto errout;
453*a547be5dSGordon Ross 	}
454*a547be5dSGordon Ross 	if (fchown(fd, uid, gid) < 0) {
455*a547be5dSGordon Ross 		perror(door_file);
456*a547be5dSGordon Ross 		goto errout;
457*a547be5dSGordon Ross 	}
458*a547be5dSGordon Ross 	close(fd);
459*a547be5dSGordon Ross 	fd = -1;
460*a547be5dSGordon Ross 
461*a547be5dSGordon Ross 	if ((pid = fork1()) == -1) {
462*a547be5dSGordon Ross 		perror("fork");
463*a547be5dSGordon Ross 		goto errout;
464*a547be5dSGordon Ross 	}
465*a547be5dSGordon Ross 	if (pid == 0) {
466*a547be5dSGordon Ross 		(void) new_child(uid, gid);
467*a547be5dSGordon Ross 		_exit(1);
468*a547be5dSGordon Ross 	}
469*a547be5dSGordon Ross 	/* parent */
470*a547be5dSGordon Ross 	cp->pid = pid;
471*a547be5dSGordon Ross 
472*a547be5dSGordon Ross 	if (d_flag) {
473*a547be5dSGordon Ross 		fprintf(stderr, "cmd_start: uid %d new iod, pid %d\n",
474*a547be5dSGordon Ross 		    uid, pid);
475*a547be5dSGordon Ross 	}
476*a547be5dSGordon Ross 
477*a547be5dSGordon Ross 	return (0);
478*a547be5dSGordon Ross 
479*a547be5dSGordon Ross errout:
480*a547be5dSGordon Ross 	if (fd != -1)
481*a547be5dSGordon Ross 		close(fd);
482*a547be5dSGordon Ross 	mutex_lock(&cl_mutex);
483*a547be5dSGordon Ross 	LIST_REMOVE(cp, list);
484*a547be5dSGordon Ross 	mutex_unlock(&cl_mutex);
485*a547be5dSGordon Ross 	free(cp);
486*a547be5dSGordon Ross 	return (errno);
487*a547be5dSGordon Ross }
488*a547be5dSGordon Ross 
489*a547be5dSGordon Ross /*
490*a547be5dSGordon Ross  * Assume the passed credentials (from the door client),
491*a547be5dSGordon Ross  * drop any extra privileges, and exec the per-user iod.
492*a547be5dSGordon Ross  */
493*a547be5dSGordon Ross static int
494*a547be5dSGordon Ross new_child(uid_t uid, gid_t gid)
495*a547be5dSGordon Ross {
496*a547be5dSGordon Ross 	char *argv[2];
497*a547be5dSGordon Ross 	int flags, rc;
498*a547be5dSGordon Ross 
499*a547be5dSGordon Ross 	flags = PU_RESETGROUPS | PU_LIMITPRIVS | PU_INHERITPRIVS;
500*a547be5dSGordon Ross 	rc = __init_daemon_priv(flags, uid, gid, PRIV_NET_ACCESS, NULL);
501*a547be5dSGordon Ross 	if (rc != 0)
502*a547be5dSGordon Ross 		return (errno);
503*a547be5dSGordon Ross 
504*a547be5dSGordon Ross 	argv[0] = "smbiod";
505*a547be5dSGordon Ross 	argv[1] = NULL;
506*a547be5dSGordon Ross 	(void) execv(smbiod_path, argv);
507*a547be5dSGordon Ross 	return (errno);
508*a547be5dSGordon Ross }
509*a547be5dSGordon Ross 
510*a547be5dSGordon Ross static void
511*a547be5dSGordon Ross svc_sigchld(void)
512*a547be5dSGordon Ross {
513*a547be5dSGordon Ross 	child_t *cp;
514*a547be5dSGordon Ross 	pid_t pid;
515*a547be5dSGordon Ross 	int err, status, found = 0;
516*a547be5dSGordon Ross 
517*a547be5dSGordon Ross 	mutex_lock(&cl_mutex);
518*a547be5dSGordon Ross 
519*a547be5dSGordon Ross 	while ((pid = waitpid(-1, &status, WNOHANG)) > 0) {
520*a547be5dSGordon Ross 
521*a547be5dSGordon Ross 		found++;
522*a547be5dSGordon Ross 		if (d_flag)
523*a547be5dSGordon Ross 			fprintf(stderr, "svc_sigchld: pid %d\n", (int)pid);
524*a547be5dSGordon Ross 
525*a547be5dSGordon Ross 		cp = child_find_by_pid(pid);
526*a547be5dSGordon Ross 		if (cp == NULL) {
527*a547be5dSGordon Ross 			fprintf(stderr, "Unknown pid %d\n", (int)pid);
528*a547be5dSGordon Ross 			continue;
529*a547be5dSGordon Ross 		}
530*a547be5dSGordon Ross 		child_gone(cp->uid, cp->pid, status);
531*a547be5dSGordon Ross 		LIST_REMOVE(cp, list);
532*a547be5dSGordon Ross 		free(cp);
533*a547be5dSGordon Ross 	}
534*a547be5dSGordon Ross 	err = errno;
535*a547be5dSGordon Ross 
536*a547be5dSGordon Ross 	mutex_unlock(&cl_mutex);
537*a547be5dSGordon Ross 
538*a547be5dSGordon Ross 	/* ECHILD is the normal end of loop. */
539*a547be5dSGordon Ross 	if (pid < 0 && err != ECHILD)
540*a547be5dSGordon Ross 		fprintf(stderr, "svc_sigchld: waitpid err %d\n", err);
541*a547be5dSGordon Ross 	if (found == 0)
542*a547be5dSGordon Ross 		fprintf(stderr, "svc_sigchld: no children?\n");
543*a547be5dSGordon Ross }
544*a547be5dSGordon Ross 
545*a547be5dSGordon Ross static void
546*a547be5dSGordon Ross child_gone(uid_t uid, pid_t pid, int status)
547*a547be5dSGordon Ross {
548*a547be5dSGordon Ross 	char door_file[64];
549*a547be5dSGordon Ross 	int x;
550*a547be5dSGordon Ross 
551*a547be5dSGordon Ross 	if (d_flag)
552*a547be5dSGordon Ross 		fprintf(stderr, "child_gone: uid %d pid %d\n",
553*a547be5dSGordon Ross 		    uid, (int)pid);
554*a547be5dSGordon Ross 
555*a547be5dSGordon Ross 	snprintf(door_file, sizeof (door_file),
556*a547be5dSGordon Ross 	    SMBIOD_RUNDIR "/%d", uid);
557*a547be5dSGordon Ross 	unlink(door_file);
558*a547be5dSGordon Ross 
559*a547be5dSGordon Ross 	if (WIFEXITED(status)) {
560*a547be5dSGordon Ross 		x = WEXITSTATUS(status);
561*a547be5dSGordon Ross 		if (x != 0) {
562*a547be5dSGordon Ross 			fprintf(stderr,
563*a547be5dSGordon Ross 			    "uid %d, pid %d exit %d",
564*a547be5dSGordon Ross 			    uid, (int)pid, x);
565*a547be5dSGordon Ross 		}
566*a547be5dSGordon Ross 	}
567*a547be5dSGordon Ross 	if (WIFSIGNALED(status)) {
568*a547be5dSGordon Ross 		x = WTERMSIG(status);
569*a547be5dSGordon Ross 		fprintf(stderr,
570*a547be5dSGordon Ross 		    "uid %d, pid %d signal %d",
571*a547be5dSGordon Ross 		    uid, (int)pid, x);
572*a547be5dSGordon Ross 	}
573*a547be5dSGordon Ross }
574*a547be5dSGordon Ross 
575*a547be5dSGordon Ross /*
576*a547be5dSGordon Ross  * Final cleanup before exit.  Unlink child doors, etc.
577*a547be5dSGordon Ross  * Called while single threaded, so no locks needed here.
578*a547be5dSGordon Ross  * The list is normally empty by now due to svc_sigchld
579*a547be5dSGordon Ross  * calls during shutdown.  But in case there were any
580*a547be5dSGordon Ross  * straglers, do cleanup here.  Don't bother freeing any
581*a547be5dSGordon Ross  * list elements here, as we're exiting.
582*a547be5dSGordon Ross  */
583*a547be5dSGordon Ross static void
584*a547be5dSGordon Ross svc_cleanup(void)
585*a547be5dSGordon Ross {
586*a547be5dSGordon Ross 	child_t *cp;
587*a547be5dSGordon Ross 
588*a547be5dSGordon Ross 	LIST_FOREACH(cp, &child_list, list) {
589*a547be5dSGordon Ross 		child_gone(cp->uid, cp->pid, 0);
590*a547be5dSGordon Ross 	}
591*a547be5dSGordon Ross }
592