xref: /openbsd/usr.bin/cap_mkdb/cap_mkdb.c (revision 3d8817e4)
1 /*	$OpenBSD: cap_mkdb.c,v 1.17 2009/11/01 23:16:39 nicm Exp $	*/
2 /*	$NetBSD: cap_mkdb.c,v 1.5 1995/09/02 05:47:12 jtc Exp $	*/
3 
4 /*-
5  * Copyright (c) 1992, 1993
6  *	The Regents of the University of California.  All rights reserved.
7  *
8  * Redistribution and use in source and binary forms, with or without
9  * modification, are permitted provided that the following conditions
10  * are met:
11  * 1. Redistributions of source code must retain the above copyright
12  *    notice, this list of conditions and the following disclaimer.
13  * 2. Redistributions in binary form must reproduce the above copyright
14  *    notice, this list of conditions and the following disclaimer in the
15  *    documentation and/or other materials provided with the distribution.
16  * 3. Neither the name of the University nor the names of its contributors
17  *    may be used to endorse or promote products derived from this software
18  *    without specific prior written permission.
19  *
20  * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
21  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
22  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
23  * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
24  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
25  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
26  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
27  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
28  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
29  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
30  * SUCH DAMAGE.
31  */
32 
33 #include <sys/param.h>
34 #include <sys/stat.h>
35 
36 #include <db.h>
37 #include <err.h>
38 #include <errno.h>
39 #include <fcntl.h>
40 #include <limits.h>
41 #include <stdio.h>
42 #include <stdlib.h>
43 #include <string.h>
44 #include <ctype.h>
45 #include <unistd.h>
46 
47 void	 db_build(char **);
48 void	 dounlink(void);
49 void	 usage(void);
50 int	 igetnext(char **, char **);
51 int	 main(int, char *[]);
52 
53 DB *capdbp;
54 int info, verbose;
55 char *capname, buf[8 * 1024];
56 
57 HASHINFO openinfo = {
58 	4096,		/* bsize */
59 	16,		/* ffactor */
60 	256,		/* nelem */
61 	2048 * 1024,	/* cachesize */
62 	NULL,		/* hash() */
63 	0		/* lorder */
64 };
65 
66 /*
67  * cap_mkdb creates a capability hash database for quick retrieval of capability
68  * records.  The database contains 2 types of entries: records and references
69  * marked by the first byte in the data.  A record entry contains the actual
70  * capability record whereas a reference contains the name (key) under which
71  * the correct record is stored.
72  */
73 int
74 main(int argc, char *argv[])
75 {
76 	int c;
77 
78 	capname = NULL;
79 	while ((c = getopt(argc, argv, "f:iv")) != -1) {
80 		switch(c) {
81 		case 'f':
82 			capname = optarg;
83 			break;
84 		case 'v':
85 			verbose = 1;
86 			break;
87 		case 'i':
88 			info = 1;
89 			break;
90 		case '?':
91 		default:
92 			usage();
93 		}
94 	}
95 	argc -= optind;
96 	argv += optind;
97 
98 	if (*argv == NULL)
99 		usage();
100 
101 	/*
102 	 * The database file is the first argument if no name is specified.
103 	 * Make arrangements to unlink it if we exit badly.
104 	 */
105 	(void)snprintf(buf, sizeof(buf), "%s.db", capname ? capname : *argv);
106 	if ((capname = strdup(buf)) == NULL)
107 		err(1, NULL);
108 	if ((capdbp = dbopen(capname, O_CREAT | O_TRUNC | O_RDWR,
109 	    DEFFILEMODE, DB_HASH, &openinfo)) == NULL)
110 		err(1, "%s", buf);
111 
112 	if (atexit(dounlink))
113 		err(1, "atexit");
114 
115 	db_build(argv);
116 
117 	if (capdbp->close(capdbp) < 0)
118 		err(1, "%s", capname);
119 	capname = NULL;
120 	exit(0);
121 }
122 
123 void
124 dounlink(void)
125 {
126 	if (capname != NULL)
127 		(void)unlink(capname);
128 }
129 
130 /*
131  * Any changes to these definitions should be made also in the getcap(3)
132  * library routines.
133  */
134 #define RECOK	(char)0
135 #define TCERR	(char)1
136 #define SHADOW	(char)2
137 
138 /*
139  * db_build() builds the name and capability databases according to the
140  * details above.
141  */
142 void
143 db_build(char **ifiles)
144 {
145 	DBT key, data;
146 	recno_t reccnt;
147 	size_t len, bplen;
148 	int st;
149 	char *bp, *p, *t, *out, ch;
150 
151 	cgetusedb(0);		/* disable reading of .db files in getcap(3) */
152 
153 	data.data = NULL;
154 	key.data = NULL;
155 	for (reccnt = 0, bplen = 0;
156 	     (st = (info ? igetnext(&bp, ifiles) : cgetnext(&bp, ifiles))) > 0;) {
157 
158 		/*
159 		 * Allocate enough memory to store four times the size of the
160 		 * record (so an existing ':' can be expanded to '\072' for
161 		 * terminfo) plus a terminating NULL and one extra byte.
162 		 */
163 		len = strlen(bp);
164 		if (bplen <= 4 * len + 2) {
165 			int newbplen = bplen + MAX(256, 4 * len + 2);
166 			void *newdata;
167 
168 			if ((newdata = realloc(data.data, newbplen)) == NULL)
169 				err(1, NULL);
170 			data.data = newdata;
171 			bplen = newbplen;
172 		}
173 
174 		/* Find the end of the name field. */
175 		if ((p = strchr(bp, info ? ',' : ':')) == NULL) {
176 			warnx("no name field: %.*s", (int)MIN(len, 20), bp);
177 			continue;
178 		}
179 
180 		/* First byte of stored record indicates status. */
181 		switch(st) {
182 		case 1:
183 			((char *)(data.data))[0] = RECOK;
184 			break;
185 		case 2:
186 			((char *)(data.data))[0] = TCERR;
187 			warnx("Record not tc expanded: %.*s", (int)(p - bp), bp);
188 			break;
189 		}
190 
191 		/* Create the stored record. */
192 		if (info) {
193 			/*
194 			 * The record separator is :, so it is necessary to
195 			 * change commas into colons. However, \, should be
196 			 * left alone, unless the \ is the last part of ^\.
197 			 */
198 			data.size = len + 2;
199 			out = ((char *) data.data) + 1;
200 			t = bp;
201 			while (t < bp + len) {
202 				switch (ch = *t++) {
203 				case '^':
204 				case '\\':
205 					*out++ = ch;
206 					if (*t != '\0')
207 						*out++ = *t++;
208 					break;
209 				case ':':
210 					memcpy(out, "\\072", 4);
211 					out += 4;
212 					data.size += 3; /* : already counted */
213 					break;
214 				case ',':
215 					*out++ = ':';
216 					break;
217 				default:
218 					*out++ = ch;
219 					break;
220 				}
221 			}
222 			*out++ = '\0';
223 			if (memchr((char *)data.data + 1, '\0', data.size - 2)) {
224 				warnx("NUL in entry: %.*s", (int)MIN(len, 20), bp);
225 				continue;
226 			}
227 		} else {
228 			char *capbeg, *capend;
229 
230 			t = (char *)data.data + 1;
231 			/* Copy the cap name and trailing ':' */
232 			len = p - bp + 1;
233 			memcpy(t, bp, len);
234 			t += len;
235 
236 			/* Copy entry, collapsing empty fields. */
237 			capbeg = p + 1;
238 			while (*capbeg) {
239 				/* Skip empty fields. */
240 				if ((len = strspn(capbeg, ": \t\n\r")))
241 					capbeg += len;
242 
243 				/* Find the end of this cap and copy it w/ : */
244 				capend = strchr(capbeg, ':');
245 				if (capend)
246 					len = capend - capbeg + 1;
247 				else
248 					len = strlen(capbeg);
249 				memcpy(t, capbeg, len);
250 				t += len;
251 				capbeg += len;
252 			}
253 			*t = '\0';
254 			data.size = t - (char *)data.data + 1;
255 		}
256 
257 		/* Store the record under the name field. */
258 		key.data = bp;
259 		key.size = p - bp;
260 
261 		switch(capdbp->put(capdbp, &key, &data, R_NOOVERWRITE)) {
262 		case -1:
263 			err(1, "put");
264 			/* NOTREACHED */
265 		case 1:
266 			warnx("ignored duplicate: %.*s",
267 			    (int)key.size, (char *)key.data);
268 			continue;
269 		}
270 		++reccnt;
271 
272 		/* If only one name, ignore the rest. */
273 		if ((p = strchr(bp, '|')) == NULL)
274 			continue;
275 
276 		/* The rest of the names reference the entire name. */
277 		((char *)(data.data))[0] = SHADOW;
278 		(void) memmove(&((u_char *)(data.data))[1], key.data, key.size);
279 		data.size = key.size + 1;
280 
281 		/* Store references for other names. */
282 		for (p = t = bp;; ++p) {
283 			if (p > t && (*p == (info ? ',' : ':') || *p == '|')) {
284 				key.size = p - t;
285 				key.data = t;
286 
287 				/*
288 				 * If this is the last entry and contains any
289 				 * spaces, it is a description rather than an
290 				 * alias, so skip it and break.
291 				 */
292 				if (*p != '|' &&
293 				    memchr(key.data, ' ', key.size) != NULL)
294 					break;
295 
296 				switch(capdbp->put(capdbp,
297 				    &key, &data, R_NOOVERWRITE)) {
298 				case -1:
299 					err(1, "put");
300 					/* NOTREACHED */
301 				case 1:
302 					warnx("ignored duplicate: %.*s",
303 					      (int)key.size, (char *)key.data);
304 				}
305 				t = p + 1;
306 			}
307 			if (*p == (info ? ',' : ':'))
308 				break;
309 		}
310 		free(bp);
311 	}
312 
313 	switch(st) {
314 	case -1:
315 		err(1, "file argument");
316 		/* NOTREACHED */
317 	case -2:
318 		errx(1, "potential reference loop detected");
319 		/* NOTREACHED */
320 	}
321 
322 	if (verbose)
323 		(void)printf("cap_mkdb: %d capability records\n", reccnt);
324 }
325 
326 void
327 usage(void)
328 {
329 	(void)fprintf(stderr,
330 	    "usage: cap_mkdb [-iv] [-f outfile] file1 [file2 ...]\n");
331 	exit(1);
332 }
333