No empty .Rs/.Re
[netbsd-mini2440.git] / external / bsd / am-utils / dist / amd / info_exec.c
blob3838f0eacd29afb082926062baf2fbda7d323f6d
1 /* $NetBSD$ */
3 /*
4 * Copyright (c) 1997-2009 Erez Zadok
5 * Copyright (c) 1990 Jan-Simon Pendry
6 * Copyright (c) 1990 Imperial College of Science, Technology & Medicine
7 * Copyright (c) 1990 The Regents of the University of California.
8 * All rights reserved.
10 * This code is derived from software contributed to Berkeley by
11 * Jan-Simon Pendry at Imperial College, London.
13 * Redistribution and use in source and binary forms, with or without
14 * modification, are permitted provided that the following conditions
15 * are met:
16 * 1. Redistributions of source code must retain the above copyright
17 * notice, this list of conditions and the following disclaimer.
18 * 2. Redistributions in binary form must reproduce the above copyright
19 * notice, this list of conditions and the following disclaimer in the
20 * documentation and/or other materials provided with the distribution.
21 * 3. All advertising materials mentioning features or use of this software
22 * must display the following acknowledgment:
23 * This product includes software developed by the University of
24 * California, Berkeley and its contributors.
25 * 4. Neither the name of the University nor the names of its contributors
26 * may be used to endorse or promote products derived from this software
27 * without specific prior written permission.
29 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
30 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
31 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
32 * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
33 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
34 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
35 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
36 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
37 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
38 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
39 * SUCH DAMAGE.
42 * File: am-utils/amd/info_exec.c
47 * Get info from executable map
49 * Original from Erik Kline, 2004.
52 #ifdef HAVE_CONFIG_H
53 # include <config.h>
54 #endif /* HAVE_CONFIG_H */
55 #include <am_defs.h>
56 #include <amd.h>
57 #include <sun_map.h>
60 /* forward declarations */
61 int exec_init(mnt_map *m, char *map, time_t *tp);
62 int exec_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp);
66 * a timed fgets()
68 static char *
69 fgets_timed(char *s, int size, int rdfd, int secs)
71 fd_set fds;
72 struct timeval timeo;
73 time_t start, now;
74 int rval=0, i=0;
76 if (!s || size < 0 || rdfd < 0)
77 return 0;
79 s[0] = '\0';
80 if (size == 0)
81 return s;
83 start = clocktime(NULL);
84 while (s[i] != '\n' && i < size-1) {
85 s[i+1] = '\0'; /* places the requisite trailing '\0' */
87 /* ready for reading */
88 rval = read(rdfd, (void *)(s+i), 1);
89 if (rval == 1) {
90 if (s[i] == 0) {
91 rval = 0;
92 break;
94 i++;
95 continue;
96 } else if (rval == 0) {
97 break;
98 } else if (rval < 0 && errno != EAGAIN && errno != EINTR) {
99 plog(XLOG_WARNING, "fgets_timed read error: %m");
100 break;
103 timeo.tv_usec = 0;
104 now = clocktime(NULL) - start;
105 if (secs <= 0)
106 timeo.tv_sec = 0;
107 else if (now < secs)
108 timeo.tv_sec = secs - now;
109 else {
110 /* timed out (now>=secs) */
111 plog(XLOG_WARNING, "executable map read timed out (> %d secs)", secs);
112 rval = -1;
113 break;
116 FD_ZERO(&fds);
117 FD_SET(rdfd, &fds);
119 rval = select(rdfd+1, &fds, NULL, NULL, &timeo);
120 if (rval < 0) {
121 /* error selecting */
122 plog(XLOG_WARNING, "fgets_timed select error: %m");
123 if (errno == EINTR)
124 continue;
125 rval = -1;
126 break;
127 } else if (rval == 0) {
128 /* timed out */
129 plog(XLOG_WARNING, "executable map read timed out (> %d secs)", secs);
130 rval = -1;
131 break;
135 if (rval > 0)
136 return s;
138 close(rdfd);
139 return (rval == 0 ? s : 0);
143 static int
144 read_line(char *buf, int size, int fd)
146 int done = 0;
148 while (fgets_timed(buf, size, fd, gopt.exec_map_timeout)) {
149 int len = strlen(buf);
150 done += len;
151 if (len > 1 && buf[len - 2] == '\\' &&
152 buf[len - 1] == '\n') {
153 buf += len - 2;
154 size -= len - 2;
155 *buf = '\n';
156 buf[1] = '\0';
157 } else {
158 return done;
162 return done;
167 * Try to locate a value in a query answer
169 static int
170 exec_parse_qanswer(mnt_map *m, int fd, char *map, char *key, char **pval, time_t *tp)
172 char qanswer[INFO_MAX_LINE_LEN], *dc = NULL;
173 int chuck = 0;
174 int line_no = 0;
176 while (read_line(qanswer, sizeof(qanswer), fd)) {
177 char *cp;
178 char *hash;
179 int len = strlen(qanswer);
180 line_no++;
183 * Make sure we got the whole line
185 if (qanswer[len - 1] != '\n') {
186 plog(XLOG_WARNING, "line %d in \"%s\" is too long", line_no, map);
187 chuck = 1;
188 } else {
189 qanswer[len - 1] = '\0';
193 * Strip comments
195 hash = strchr(qanswer, '#');
196 if (hash)
197 *hash = '\0';
200 * Find beginning of value (query answer)
202 for (cp = qanswer; *cp && !isascii((unsigned char)*cp) && !isspace((unsigned char)*cp); cp++)
205 /* Ignore blank lines */
206 if (!*cp)
207 goto again;
210 * Return a copy of the data
212 if (m->cfm && (m->cfm->cfm_flags & CFM_SUN_MAP_SYNTAX))
213 dc = sun_entry2amd(key, cp);
214 else
215 dc = strdup(cp);
216 *pval = dc;
217 dlog("%s returns %s", key, dc);
219 close(fd);
220 return 0;
222 again:
224 * If the last read didn't get a whole line then
225 * throw away the remainder before continuing...
227 if (chuck) {
228 while (fgets_timed(qanswer, sizeof(qanswer), fd, gopt.exec_map_timeout) &&
229 !strchr(qanswer, '\n')) ;
230 chuck = 0;
234 return ENOENT;
238 static int
239 set_nonblock(int fd)
241 int val;
243 if (fd < 0)
244 return 0;
246 if ((val = fcntl(fd, F_GETFL, 0)) < 0) {
247 plog(XLOG_WARNING, "set_nonblock fcntl F_GETFL error: %m");
248 return 0;
251 val |= O_NONBLOCK;
252 if (fcntl(fd, F_SETFL, val) < 0) {
253 plog(XLOG_WARNING, "set_nonblock fcntl F_SETFL error: %m");
254 return 0;
257 return 1;
261 static int
262 exec_map_open(char *emap, char *key)
264 pid_t p1, p2;
265 int pdes[2], nullfd, i;
266 char *argv[3];
268 if (!emap)
269 return 0;
271 argv[0] = emap;
272 argv[1] = key;
273 argv[2] = NULL;
275 if ((nullfd = open("/dev/null", O_WRONLY|O_NOCTTY)) < 0)
276 return -1;
278 if (pipe(pdes) < 0) {
279 close(nullfd);
280 return -1;
283 switch ((p1 = vfork())) {
284 case -1:
285 /* parent: fork error */
286 close(nullfd);
287 close(pdes[0]);
288 close(pdes[1]);
289 return -1;
290 case 0:
291 /* child #1 */
292 p2 = vfork();
293 switch (p2) {
294 case -1:
295 /* child #1: fork error */
296 exit(errno);
297 case 0:
298 /* child #2: init will reap our status */
299 if (pdes[1] != STDOUT_FILENO) {
300 dup2(pdes[1], STDOUT_FILENO);
301 close(pdes[1]);
304 if (nullfd != STDERR_FILENO) {
305 dup2(nullfd, STDERR_FILENO);
306 close(nullfd);
309 for (i=0; i<FD_SETSIZE; i++)
310 if (i != STDOUT_FILENO && i != STDERR_FILENO)
311 close(i);
313 /* make the write descriptor non-blocking */
314 if (!set_nonblock(STDOUT_FILENO)) {
315 close(STDOUT_FILENO);
316 exit(-1);
319 execve(emap, argv, NULL);
320 exit(errno); /* in case execve failed */
323 /* child #1 */
324 exit(0);
327 /* parent */
328 close(nullfd);
329 close(pdes[1]);
331 /* anti-zombie insurance */
332 while (waitpid(p1, 0, 0) < 0)
333 if (errno != EINTR)
334 exit(errno);
336 /* make the read descriptor non-blocking */
337 if (!set_nonblock(pdes[0])) {
338 close(pdes[0]);
339 return -1;
342 return pdes[0];
347 * Check for various permissions on executable map without trying to
348 * fork a new executable-map process.
350 * return: >0 (errno) if failed
351 * 0 if ok
353 static int
354 exec_check_perm(char *map)
356 struct stat sb;
358 /* sanity and permission checks */
359 if (!map) {
360 dlog("exec_check_permission got a NULL map");
361 return EINVAL;
363 if (stat(map, &sb)) {
364 plog(XLOG_ERROR, "map \"%s\" stat failure: %m", map);
365 return errno;
367 if (!S_ISREG(sb.st_mode)) {
368 plog(XLOG_ERROR, "map \"%s\" should be regular file", map);
369 return EINVAL;
371 if (sb.st_uid != 0) {
372 plog(XLOG_ERROR, "map \"%s\" owned by uid %u (must be 0)", map, (u_int) sb.st_uid);
373 return EACCES;
375 if (!(sb.st_mode & S_IXUSR)) {
376 plog(XLOG_ERROR, "map \"%s\" should be executable", map);
377 return EACCES;
379 if (sb.st_mode & (S_ISUID|S_ISGID)) {
380 plog(XLOG_ERROR, "map \"%s\" should not be setuid/setgid", map);
381 return EACCES;
383 if (sb.st_mode & S_IWOTH) {
384 plog(XLOG_ERROR, "map \"%s\" should not be world writeable", map);
385 return EACCES;
388 return 0; /* all is well */
393 exec_init(mnt_map *m, char *map, time_t *tp)
396 * Basically just test that the executable map can be found
397 * and has proper permissions.
399 return exec_check_perm(map);
404 exec_search(mnt_map *m, char *map, char *key, char **pval, time_t *tp)
406 int mapfd, ret;
408 if ((ret = exec_check_perm(map)) != 0) {
409 return ret;
412 if (!key)
413 return 0;
415 if (logfp)
416 fflush(logfp);
417 dlog("exec_search \"%s\", key: \"%s\"", map, key);
418 mapfd = exec_map_open(map, key);
420 if (mapfd >= 0) {
421 if (tp)
422 *tp = clocktime(NULL);
424 return exec_parse_qanswer(m, mapfd, map, key, pval, tp);
427 return errno;