cap_mkdb.c revision 1.16
1/*	$OpenBSD: cap_mkdb.c,v 1.16 2009/10/27 23:59:36 deraadt 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
47void	 db_build(char **);
48void	 dounlink(void);
49void	 usage(void);
50int	 igetnext(char **, char **);
51int	 main(int, char *[]);
52
53DB *capdbp;
54int info, verbose;
55char *capname, buf[8 * 1024];
56
57HASHINFO 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 */
73int
74main(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
123void
124dounlink(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 */
142void
143db_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				switch(capdbp->put(capdbp,
287				    &key, &data, R_NOOVERWRITE)) {
288				case -1:
289					err(1, "put");
290					/* NOTREACHED */
291				case 1:
292					warnx("ignored duplicate: %.*s",
293					      (int)key.size, (char *)key.data);
294				}
295				t = p + 1;
296			}
297			if (*p == (info ? ',' : ':'))
298				break;
299		}
300		free(bp);
301	}
302
303	switch(st) {
304	case -1:
305		err(1, "file argument");
306		/* NOTREACHED */
307	case -2:
308		errx(1, "potential reference loop detected");
309		/* NOTREACHED */
310	}
311
312	if (verbose)
313		(void)printf("cap_mkdb: %d capability records\n", reccnt);
314}
315
316void
317usage(void)
318{
319	(void)fprintf(stderr,
320	    "usage: cap_mkdb [-iv] [-f outfile] file1 [file2 ...]\n");
321	exit(1);
322}
323