1 /*
2  * Copyright (c) 1992, 1993, 1994
3  *	The Regents of the University of California.  All rights reserved.
4  *
5  * This code is derived from software donated to Berkeley by
6  * Jan-Simon Pendry.
7  *
8  * %sccs.include.redist.c%
9  */
10 
11 #ifndef lint
12 char copyright[] =
13 "@(#) Copyright (c) 1992, 1993, 1994\n\
14 	The Regents of the University of California.  All rights reserved.\n";
15 #endif /* not lint */
16 
17 #ifndef lint
18 static char sccsid[] = "@(#)mount_portal.c	8.6 (Berkeley) 04/26/95";
19 #endif /* not lint */
20 
21 #include <sys/param.h>
22 #include <sys/wait.h>
23 #include <sys/socket.h>
24 #include <sys/un.h>
25 #include <sys/syslog.h>
26 #include <sys/mount.h>
27 
28 #include <err.h>
29 #include <errno.h>
30 #include <signal.h>
31 #include <stdio.h>
32 #include <stdlib.h>
33 #include <string.h>
34 #include <unistd.h>
35 
36 #include "mntopts.h"
37 #include "pathnames.h"
38 #include "portald.h"
39 
40 struct mntopt mopts[] = {
41 	MOPT_STDOPTS,
42 	{ NULL }
43 };
44 
45 static void usage __P((void));
46 
47 static sig_atomic_t readcf;	/* Set when SIGHUP received */
48 
49 static void sigchld(sig)
50 int sig;
51 {
52 	pid_t pid;
53 
54 	while ((pid = waitpid((pid_t) -1, (int *) 0, WNOHANG)) > 0)
55 		;
56 	if (pid < 0 && errno != ECHILD)
57 		syslog(LOG_WARNING, "waitpid: %s", strerror(errno));
58 }
59 
60 int
61 main(argc, argv)
62 	int argc;
63 	char *argv[];
64 {
65 	struct portal_args args;
66 	struct sockaddr_un un;
67 	char *conf;
68 	char *mountpt;
69 	int mntflags = 0;
70 	char tag[32];
71 
72 	qelem q;
73 	int rc;
74 	int so;
75 	int error = 0;
76 
77 	/*
78 	 * Crack command line args
79 	 */
80 	int ch;
81 
82 	while ((ch = getopt(argc, argv, "o:")) != EOF) {
83 		switch (ch) {
84 		case 'o':
85 			getmntopts(optarg, mopts, &mntflags, 0);
86 			break;
87 		default:
88 			error = 1;
89 			break;
90 		}
91 	}
92 
93 	if (optind != (argc - 2))
94 		error = 1;
95 
96 	if (error)
97 		usage();
98 
99 	/*
100 	 * Get config file and mount point
101 	 */
102 	conf = argv[optind];
103 	mountpt = argv[optind+1];
104 
105 	/*
106 	 * Construct the listening socket
107 	 */
108 	un.sun_family = AF_UNIX;
109 	if (sizeof(_PATH_TMPPORTAL) >= sizeof(un.sun_path)) {
110 		fprintf(stderr, "mount_portal: portal socket name too long\n");
111 		exit(1);
112 	}
113 	strcpy(un.sun_path, _PATH_TMPPORTAL);
114 	mktemp(un.sun_path);
115 	un.sun_len = strlen(un.sun_path);
116 
117 	so = socket(AF_UNIX, SOCK_STREAM, 0);
118 	if (so < 0) {
119 		fprintf(stderr, "mount_portal: socket: %s\n", strerror(errno));
120 		exit(1);
121 	}
122 	(void) unlink(un.sun_path);
123 	if (bind(so, (struct sockaddr *) &un, sizeof(un)) < 0)
124 		err(1, NULL);
125 	(void) unlink(un.sun_path);
126 
127 	(void) listen(so, 5);
128 
129 	args.pa_socket = so;
130 	sprintf(tag, "portal:%d", getpid());
131 	args.pa_config = tag;
132 
133 	rc = mount("portal", mountpt, mntflags, &args);
134 	if (rc < 0)
135 		err(1, NULL);
136 
137 	/*
138 	 * Everything is ready to go - now is a good time to fork
139 	 */
140 	daemon(0, 0);
141 
142 	/*
143 	 * Start logging (and change name)
144 	 */
145 	openlog("portald", LOG_CONS|LOG_PID, LOG_DAEMON);
146 
147 	q.q_forw = q.q_back = &q;
148 	readcf = 1;
149 
150 	signal(SIGCHLD, sigchld);
151 
152 	/*
153 	 * Just loop waiting for new connections and activating them
154 	 */
155 	for (;;) {
156 		struct sockaddr_un un2;
157 		int len2 = sizeof(un2);
158 		int so2;
159 		pid_t pid;
160 		fd_set fdset;
161 		int rc;
162 
163 		/*
164 		 * Check whether we need to re-read the configuration file
165 		 */
166 		if (readcf) {
167 			readcf = 0;
168 			conf_read(&q, conf);
169 			continue;
170 		}
171 
172 		/*
173 		 * Accept a new connection
174 		 * Will get EINTR if a signal has arrived, so just
175 		 * ignore that error code
176 		 */
177 		FD_SET(so, &fdset);
178 		rc = select(so+1, &fdset, (void *) 0, (void *) 0, (void *) 0);
179 		if (rc < 0) {
180 			if (errno == EINTR)
181 				continue;
182 			syslog(LOG_ERR, "select: %s", strerror(errno));
183 			exit(1);
184 		}
185 		if (rc == 0)
186 			break;
187 		so2 = accept(so, (struct sockaddr *) &un2, &len2);
188 		if (so2 < 0) {
189 			/*
190 			 * The unmount function does a shutdown on the socket
191 			 * which will generated ECONNABORTED on the accept.
192 			 */
193 			if (errno == ECONNABORTED)
194 				break;
195 			if (errno != EINTR) {
196 				syslog(LOG_ERR, "accept: %s", strerror(errno));
197 				exit(1);
198 			}
199 			continue;
200 		}
201 
202 		/*
203 		 * Now fork a new child to deal with the connection
204 		 */
205 	eagain:;
206 		switch (pid = fork()) {
207 		case -1:
208 			if (errno == EAGAIN) {
209 				sleep(1);
210 				goto eagain;
211 			}
212 			syslog(LOG_ERR, "fork: %s", strerror(errno));
213 			break;
214 		case 0:
215 			(void) close(so);
216 			activate(&q, so2);
217 			exit(0);
218 		default:
219 			(void) close(so2);
220 			break;
221 		}
222 	}
223 	syslog(LOG_INFO, "%s unmounted", mountpt);
224 	exit(0);
225 }
226 
227 static void
228 usage()
229 {
230 	(void)fprintf(stderr,
231 		"usage: mount_portal [-o options] config mount-point\n");
232 	exit(1);
233 }
234