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