xref: /illumos-gate/usr/src/cmd/fs.d/reparsed/reparsed.c (revision 46b592853d0f4f11781b6b0a7533f267c6aee132)
1 /*
2  * CDDL HEADER START
3  *
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.
7  *
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.
12  *
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]
18  *
19  * CDDL HEADER END
20  */
21 /*
22  * Copyright 2009 Sun Microsystems, Inc.  All rights reserved.
23  * Use is subject to license terms.
24  */
25 
26 /*
27  * Reparsed daemon
28  */
29 
30 #include <stdio.h>
31 #include <stdio_ext.h>
32 #include <stdlib.h>
33 #include <unistd.h>
34 #include <signal.h>
35 #include <sys/types.h>
36 #include <sys/stat.h>
37 #include <fcntl.h>
38 #include <memory.h>
39 #include <alloca.h>
40 #include <ucontext.h>
41 #include <errno.h>
42 #include <syslog.h>
43 #include <string.h>
44 #include <strings.h>
45 #include <door.h>
46 #include <wait.h>
47 #include <libintl.h>
48 #include <locale.h>
49 #include <sys/param.h>
50 #include <sys/systeminfo.h>
51 #include <sys/thread.h>
52 #include <rpc/xdr.h>
53 #include <priv.h>
54 #include <sys/fs_reparse.h>
55 #include <priv_utils.h>
56 #include <rpcsvc/daemon_utils.h>
57 
58 #define	REPARSED_CMD_OPTS	"v"
59 #define	DOOR_RESULT_BUFSZ	(MAXPATHLEN + sizeof (reparsed_door_res_t))
60 #define	SAFETY_BUFFER		8*1024
61 
62 static char *MyName;
63 static int verbose = 0;
64 
65 static int start_reparsed_svcs();
66 static void daemonize(void);
67 static void reparsed_door_call_error(int error, int buflen);
68 static void reparsed_doorfunc(void *cookie, char *argp, size_t arg_size,
69 			door_desc_t *dp, uint_t n_desc);
70 
71 static void
72 usage()
73 {
74 	syslog(LOG_ERR, "Usage: %s", MyName);
75 	syslog(LOG_ERR, "\t[-v]\t\tverbose error messages)");
76 	exit(1);
77 }
78 
79 static void
80 warn_hup(int i)
81 {
82 	syslog(LOG_ERR, "SIGHUP received: ignored");
83 	(void) signal(SIGHUP, warn_hup);
84 }
85 
86 /*
87  * Processing for daemonization
88  */
89 static void
90 daemonize(void)
91 {
92 	switch (fork()) {
93 	case -1:
94 		syslog(LOG_ERR, "reparsed: can't fork - errno %d", errno);
95 		exit(2);
96 		/* NOTREACHED */
97 	case 0:		/* child */
98 		break;
99 
100 	default:	/* parent */
101 		_exit(0);
102 	}
103 	(void) chdir("/");
104 
105 	/*
106 	 * Close stdin, stdout, and stderr.
107 	 * Open again to redirect input+output
108 	 */
109 	(void) close(0);
110 	(void) close(1);
111 	(void) close(2);
112 	(void) open("/dev/null", O_RDONLY);
113 	(void) open("/dev/null", O_WRONLY);
114 	(void) dup(1);
115 	(void) setsid();
116 }
117 
118 int
119 main(int argc, char *argv[])
120 {
121 	pid_t pid;
122 	int c, error;
123 	struct rlimit rlset;
124 	char *defval;
125 
126 	/*
127 	 * There is no check for non-global zone and Trusted Extensions.
128 	 * Reparsed works in both of these environments as long as the
129 	 * services that use reparsed are supported.
130 	 */
131 
132 	MyName = argv[0];
133 	if (geteuid() != 0) {
134 		syslog(LOG_ERR, "%s must be run as root", MyName);
135 		exit(1);
136 	}
137 
138 	while ((c = getopt(argc, argv, REPARSED_CMD_OPTS)) != EOF) {
139 		switch (c) {
140 		case 'v':
141 			verbose++;
142 			break;
143 		default:
144 			usage();
145 		}
146 	}
147 
148 	daemonize();
149 	openlog(MyName, LOG_PID | LOG_NDELAY, LOG_DAEMON);
150 
151 	(void) _create_daemon_lock(REPARSED, DAEMON_UID, DAEMON_GID);
152 	(void) enable_extended_FILE_stdio(-1, -1);
153 	switch (_enter_daemon_lock(REPARSED)) {
154 	case 0:
155 		break;
156 	case -1:
157 		syslog(LOG_ERR, "Error locking for %s", REPARSED);
158 		exit(2);
159 	default:
160 		/* daemon was already running */
161 		exit(0);
162 	}
163 
164 	(void) signal(SIGHUP, warn_hup);
165 
166 	/*
167 	 * Make the process a privilege aware daemon.
168 	 * Only "basic" privileges are required.
169 	 *
170 	 */
171 	if (__init_daemon_priv(PU_RESETGROUPS|PU_CLEARLIMITSET, 0, 0,
172 	    (char *)NULL) == -1) {
173 		syslog(LOG_ERR, "should be run with sufficient privileges");
174 		exit(3);
175 	}
176 
177 	/*
178 	 * Clear basic privileges not required by reparsed.
179 	 */
180 	__fini_daemon_priv(PRIV_PROC_FORK, PRIV_PROC_EXEC, PRIV_PROC_SESSION,
181 	    PRIV_FILE_LINK_ANY, PRIV_PROC_INFO, (char *)NULL);
182 
183 	return (start_reparsed_svcs());
184 }
185 
186 static void
187 reparsed_door_call_error(int error, int buflen)
188 {
189 	reparsed_door_res_t rpd_res;
190 
191 	memset(&rpd_res, 0, sizeof (reparsed_door_res_t));
192 	rpd_res.res_status = error;
193 	rpd_res.res_len = buflen;
194 	door_return((char *)&rpd_res, sizeof (reparsed_door_res_t), NULL, 0);
195 
196 	(void) door_return(NULL, 0, NULL, 0);
197 	/* NOTREACHED */
198 }
199 
200 /*
201  *  reparsed_doorfunc
202  *
203  *  argp:  "service_type:service_data" string
204  *  dp & n_desc: not used.
205  */
206 static void
207 reparsed_doorfunc(void *cookie, char *argp, size_t arg_size,
208     door_desc_t *dp, uint_t n_desc)
209 {
210 	int err;
211 	size_t bufsz;
212 	char *svc_type, *svc_data;
213 	char *cp, *buf, *sbuf, res_buf[DOOR_RESULT_BUFSZ];
214 	reparsed_door_res_t *resp;
215 
216 	if ((argp == NULL) || (arg_size == 0)) {
217 		reparsed_door_call_error(EINVAL, 0);
218 		/* NOTREACHED */
219 	}
220 
221 	if (verbose)
222 		syslog(LOG_NOTICE, "reparsed_door: [%s, %d]", argp, arg_size);
223 
224 	if ((svc_type = strdup(argp)) == NULL) {
225 		reparsed_door_call_error(ENOMEM, 0);
226 		/* NOTREACHED */
227 	}
228 
229 	/*
230 	 * Door argument string comes in "service_type:service_data" format.
231 	 * Need to break it into separate "service_type" and "service_data"
232 	 * string before passing them to reparse_deref() to process them.
233 	 */
234 	if ((cp = strchr(svc_type, ':')) == NULL) {
235 		free(svc_type);
236 		reparsed_door_call_error(EINVAL, 0);
237 		/* NOTREACHED */
238 	}
239 	*cp++ = '\0';
240 	svc_data = cp;
241 
242 	/*
243 	 * Setup buffer for reparse_deref(). 'bufsz' is the actual
244 	 * buffer size to hold the result returned by reparse_deref().
245 	 */
246 	resp = (reparsed_door_res_t *)res_buf;
247 	buf = resp->res_data;
248 	bufsz = sizeof (res_buf) - sizeof (reparsed_door_res_t);
249 
250 	/*
251 	 * reparse_deref() calls the service type plugin library to process
252 	 * the service data. The plugin library function should understand
253 	 * the context of the service data and should be the one to XDR the
254 	 * results before returning it to the caller.
255 	 */
256 	err = reparse_deref(svc_type, svc_data, buf, &bufsz);
257 
258 	if (verbose)
259 		syslog(LOG_NOTICE,
260 		    "reparsed_deref(svc_type: %s, data: %s, size: %d) -> %d",
261 		    svc_type, svc_data, bufsz, err);
262 
263 	switch (err) {
264 	case 0:
265 		break;
266 
267 	case EOVERFLOW:
268 		/*
269 		 * bufsz was returned with size needed by reparse_deref().
270 		 *
271 		 * We cannot use malloc() here because door_return() never
272 		 * returns, and memory allocated by malloc() would get leaked.
273 		 */
274 		sbuf = alloca(bufsz + sizeof (reparsed_door_res_t));
275 		if (sbuf == NULL || stack_inbounds(buf) == 0 ||
276 		    stack_inbounds(buf + sizeof (reparsed_door_res_t) +
277 		    SAFETY_BUFFER - 1) == 0) {
278 			free(svc_type);
279 			reparsed_door_call_error(ENOMEM, 0);
280 			/* NOTREACHED */
281 		}
282 
283 		resp = (reparsed_door_res_t *)sbuf;
284 		if ((err = reparse_deref(svc_type, svc_data, resp->res_data,
285 		    &bufsz)) == 0)
286 			break;
287 
288 		/* fall through */
289 
290 	default:
291 		free(svc_type);
292 		reparsed_door_call_error(err, 0);
293 		/* NOTREACHED */
294 	}
295 
296 	free(svc_type);
297 
298 	if (verbose)
299 		syslog(LOG_NOTICE, "reparsed_door_return <buf=%s> size=%d",
300 		    buf, bufsz);
301 
302 	resp->res_status = 0;
303 	resp->res_len = bufsz;
304 	(void) door_return((char *)resp, bufsz + sizeof (reparsed_door_res_t),
305 	    NULL, 0);
306 
307 	(void) door_return(NULL, 0, NULL, 0);
308 	/* NOTREACHED */
309 }
310 
311 static int
312 start_reparsed_svcs()
313 {
314 	int doorfd;
315 	int dfd;
316 
317 	if ((doorfd = door_create(reparsed_doorfunc, NULL,
318 	    DOOR_REFUSE_DESC|DOOR_NO_CANCEL)) == -1) {
319 		syslog(LOG_ERR, "Unable to create door");
320 		return (1);
321 	}
322 
323 	/*
324 	 * Create a file system path for the door
325 	 */
326 	if ((dfd = open(REPARSED_DOOR, O_RDWR|O_CREAT|O_TRUNC,
327 	    S_IRUSR|S_IWUSR|S_IRGRP|S_IROTH)) == -1) {
328 		syslog(LOG_ERR, "unable to open %s", REPARSED_DOOR);
329 		(void) close(doorfd);
330 		return (1);
331 	}
332 
333 	/*
334 	 * Clean up any stale associations
335 	 */
336 	(void) fdetach(REPARSED_DOOR);
337 
338 	/*
339 	 * Register in the kernel namespace for door_ki_open().
340 	 */
341 	if (fattach(doorfd, REPARSED_DOOR) == -1) {
342 		syslog(LOG_ERR, "Unable to fattach door %s", REPARSED_DOOR);
343 		(void) close(doorfd);
344 		(void) close(dfd);
345 		return (1);
346 	}
347 	(void) close(dfd);
348 
349 	/*
350 	 * Wait for incoming calls
351 	 */
352 	/*CONSTCOND*/
353 	while (1)
354 		(void) pause();
355 
356 	syslog(LOG_ERR, "Door server exited");
357 	return (10);
358 }
359