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