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/*
23 * Copyright 2008 Sun Microsystems, Inc.  All rights reserved.
24 * Use is subject to license terms.
25 */
26
27/*	Copyright (c) 1983, 1984, 1985, 1986, 1987, 1988, 1989 AT&T */
28/*	  All Rights Reserved   */
29
30/*
31 * Portions of this source code were derived from Berkeley
32 * under license from the Regents of the University of
33 * California.
34 */
35
36#undef NULL
37#include <stdio.h>
38#include <sys/types.h>
39#include <sys/file.h>
40#include <sys/param.h>
41#include <sys/stat.h>
42#include <ctype.h>
43#include <limits.h>
44#include <string.h>
45#include <unistd.h>
46#include <stdlib.h>
47#include <sys/systeminfo.h>
48#include <dlfcn.h>
49
50#include "ypdefs.h"
51#include "ypsym.h"
52USE_YP_MASTER_NAME
53USE_YP_LAST_MODIFIED
54USE_YP_INPUT_FILE
55USE_YP_OUTPUT_NAME
56USE_YP_DOMAIN_NAME
57USE_YP_SECURE
58USE_YP_INTERDOMAIN
59USE_DBM
60
61#ifdef SYSVCONFIG
62extern void sysvconfig();
63#endif
64extern int yp_getalias();
65
66#define	MAXLINE 4096		/* max length of input line */
67#define	DEFAULT_SEP	" "
68static char *get_date();
69static char *any();
70static void addpair();
71static void unmake();
72static void usage();
73
74int   inode_dev_valid = 0;
75ino64_t inode;
76dev_t dev;
77
78/*
79 * Interpose close(2) to enable us to keep one of the output
80 * files open until process exit.
81 */
82#pragma weak _close = close
83int
84close(int filedes) {
85
86	struct stat64	sb;
87	static int	(*fptr)() = 0;
88
89	if (fptr == 0) {
90		fptr = (int (*)())dlsym(RTLD_NEXT, "close");
91		if (fptr == 0) {
92			fprintf(stderr, "makedbm: dlopen(close): %s\n",
93				dlerror());
94			errno = ELIBACC;
95			return (-1);
96		}
97	}
98
99	if (inode_dev_valid != 0 && fstat64(filedes, &sb) == 0) {
100		if (sb.st_ino == inode && sb.st_dev == dev) {
101			/* Keep open; pretend successful */
102			return (0);
103		}
104	}
105
106	return ((*fptr)(filedes));
107}
108
109int
110main(argc, argv)
111	int argc;
112	char **argv;
113{
114	FILE *infp, *outfp;
115	datum key, content, tmp;
116	char buf[MAXLINE];
117	char pagbuf[MAXPATHLEN];
118	char tmppagbuf[MAXPATHLEN];
119	char dirbuf[MAXPATHLEN];
120	char tmpdirbuf[MAXPATHLEN];
121	char *p, ic;
122	char *infile, *outfile;
123	char outalias[MAXPATHLEN];
124	char outaliasmap[MAXNAMLEN];
125	char outaliasdomain[MAXNAMLEN];
126	char *last_slash, *next_to_last_slash;
127	char *infilename, *outfilename, *mastername, *domainname,
128	    *interdomain_bind, *security, *lower_case_keys;
129	char key_sep[] = DEFAULT_SEP;
130	char local_host[MAX_MASTER_NAME];
131	int cnt, i;
132	DBM *fdb;
133	struct stat64 statbuf;
134	int num_del_to_match = 0;
135	/* flag to indicate if matching char can be escaped */
136	int count_esp = 0;
137
138	/* Ignore existing umask, always force 077 (owner rw only) */
139	umask(077);
140
141	infile = outfile = NULL; /* where to get files */
142	/* name to imbed in database */
143	infilename = outfilename = mastername = domainname = interdomain_bind =
144	    security = lower_case_keys = NULL;
145	argv++;
146	argc--;
147	while (argc > 0) {
148		if (argv[0][0] == '-' && argv[0][1]) {
149			switch (argv[0][1]) {
150				case 'i':
151					infilename = argv[1];
152					argv++;
153					argc--;
154					break;
155				case 'o':
156					outfilename = argv[1];
157					argv++;
158					argc--;
159					break;
160				case 'm':
161					mastername = argv[1];
162					argv++;
163					argc--;
164					break;
165				case 'b':
166					interdomain_bind = argv[0];
167					break;
168				case 'd':
169					domainname = argv[1];
170					argv++;
171					argc--;
172					break;
173				case 'l':
174					lower_case_keys = argv[0];
175					break;
176				case 's':
177					security = argv[0];
178					break;
179				case 'S' :
180					if (strlen(argv[1]) != 1) {
181						fprintf(stderr,
182							"bad separator\n");
183						usage();
184					}
185					key_sep[0] = argv[1][0];
186					argv++;
187					argc--;
188					break;
189				case 'D' :
190					num_del_to_match = atoi(argv[1]);
191					argv++;
192					argc--;
193					break;
194				case 'E' :
195					count_esp = 1;
196					break;
197				case 'u':
198					unmake(argv[1]);
199					argv++;
200					argc--;
201					exit(0);
202				default:
203					usage();
204			}
205		} else if (infile == NULL)
206			infile = argv[0];
207		else if (outfile == NULL)
208			outfile = argv[0];
209		else
210			usage();
211		argv++;
212		argc--;
213	}
214	if (infile == NULL || outfile == NULL)
215		usage();
216
217	/*
218	 *  do alias mapping if necessary
219	 */
220	last_slash = strrchr(outfile, '/');
221	if (last_slash) {
222		*last_slash = '\0';
223		next_to_last_slash = strrchr(outfile, '/');
224		if (next_to_last_slash) *next_to_last_slash = '\0';
225	} else next_to_last_slash = NULL;
226
227#ifdef DEBUG
228	if (last_slash) printf("last_slash=%s\n", last_slash+1);
229	if (next_to_last_slash) printf("next_to_last_slash=%s\n",
230		next_to_last_slash+1);
231#endif /* DEBUG */
232
233	/* reads in alias file for system v filename translation */
234#ifdef SYSVCONFIG
235	sysvconfig();
236#endif
237
238	if (last_slash && next_to_last_slash) {
239		if (yp_getalias(last_slash+1, outaliasmap, MAXALIASLEN) < 0) {
240			if ((int)strlen(last_slash+1) <= MAXALIASLEN)
241				strcpy(outaliasmap, last_slash+1);
242			else
243				fprintf(stderr,
244				    "makedbm: warning: no alias for %s\n",
245				    last_slash+1);
246		}
247#ifdef DEBUG
248		printf("%s\n", last_slash+1);
249		printf("%s\n", outaliasmap);
250#endif /* DEBUG */
251		if (yp_getalias(next_to_last_slash+1, outaliasdomain,
252		    NAME_MAX) < 0) {
253			if ((int)strlen(last_slash+1) <= NAME_MAX)
254				strcpy(outaliasdomain, next_to_last_slash+1);
255			else
256				fprintf(stderr,
257				    "makedbm: warning: no alias for %s\n",
258				    next_to_last_slash+1);
259		}
260#ifdef DEBUG
261		printf("%s\n", next_to_last_slash+1);
262		printf("%s\n", outaliasdomain);
263#endif /* DEBUG */
264		sprintf(outalias, "%s/%s/%s", outfile, outaliasdomain,
265			outaliasmap);
266#ifdef DEBUG
267		printf("outlias=%s\n", outalias);
268#endif /* DEBUG */
269
270	} else if (last_slash) {
271		if (yp_getalias(last_slash+1, outaliasmap, MAXALIASLEN) < 0) {
272			if ((int)strlen(last_slash+1) <= MAXALIASLEN)
273				strcpy(outaliasmap, last_slash+1);
274			else
275				fprintf(stderr,
276				    "makedbm: warning: no alias for %s\n",
277				    last_slash+1);
278		}
279		if (yp_getalias(outfile, outaliasdomain, NAME_MAX) < 0) {
280			if ((int)strlen(outfile) <= NAME_MAX)
281				strcpy(outaliasdomain, outfile);
282			else
283				fprintf(stderr,
284				    "makedbm: warning: no alias for %s\n",
285				    last_slash+1);
286		}
287		sprintf(outalias, "%s/%s", outaliasdomain, outaliasmap);
288	} else {
289		if (yp_getalias(outfile, outalias, MAXALIASLEN) < 0) {
290			if ((int)strlen(last_slash+1) <= MAXALIASLEN)
291				strcpy(outalias, outfile);
292			else
293				fprintf(stderr,
294				    "makedbm: warning: no alias for %s\n",
295				    outfile);
296			}
297	}
298#ifdef DEBUG
299	fprintf(stderr, "outalias=%s\n", outalias);
300	fprintf(stderr, "outfile=%s\n", outfile);
301#endif /* DEBUG */
302
303	strcpy(tmppagbuf, outalias);
304	strcat(tmppagbuf, ".tmp");
305	strcpy(tmpdirbuf, tmppagbuf);
306	strcat(tmpdirbuf, dbm_dir);
307	strcat(tmppagbuf, dbm_pag);
308
309	/* Loop until we can lock the tmpdirbuf file */
310	for (;;) {
311
312		if (strcmp(infile, "-") != 0)
313			infp = fopen(infile, "r");
314		else if (fstat64(fileno(stdin), &statbuf) == -1) {
315			fprintf(stderr, "makedbm: can't open stdin\n");
316			exit(1);
317		} else
318			infp = stdin;
319
320		if (infp == NULL) {
321			fprintf(stderr, "makedbm: can't open %s\n", infile);
322			exit(1);
323		}
324
325		if ((outfp = fopen(tmpdirbuf, "w")) == (FILE *)NULL) {
326			fprintf(stderr, "makedbm: can't create %s\n",
327				tmpdirbuf);
328			exit(1);
329		}
330
331		if (lockf(fileno(outfp), F_TLOCK, 0) == 0) {
332			/* Got exclusive access; save inode and dev */
333			if (fstat64(fileno(outfp), &statbuf) != 0) {
334				fprintf(stderr, "makedbm: can't fstat ");
335				perror(tmpdirbuf);
336				exit(1);
337			}
338			inode		= statbuf.st_ino;
339			dev		= statbuf.st_dev;
340			inode_dev_valid	= 1;
341			break;
342		}
343
344		if (errno != EAGAIN) {
345			fprintf(stderr, "makedbm: can't lock ");
346			perror(tmpdirbuf);
347			exit(1);
348		}
349
350		/*
351		 * Someone else is holding the lock.
352		 * Close both output and input file
353		 * (the latter to ensure consistency
354		 * if the input file is updated while
355		 * we're suspended), wait a little,
356		 * and try again.
357		 */
358		if (infp != stdin)
359			(void) fclose(infp);
360		(void) fclose(outfp);
361		sleep(1);
362	}
363
364	if (fopen(tmppagbuf, "w") == (FILE *)NULL) {
365		fprintf(stderr, "makedbm: can't create %s\n", tmppagbuf);
366		exit(1);
367	}
368	strcpy(dirbuf, outalias);
369	strcat(dirbuf, ".tmp");
370	if ((fdb = dbm_open(dirbuf, O_RDWR | O_CREAT, 0644)) == NULL) {
371		fprintf(stderr, "makedbm: can't open %s\n", dirbuf);
372		exit(1);
373	}
374	strcpy(dirbuf, outalias);
375	strcpy(pagbuf, outalias);
376	strcat(dirbuf, dbm_dir);
377	strcat(pagbuf, dbm_pag);
378	while (fgets(buf, sizeof (buf), infp) != NULL) {
379		p = buf;
380		cnt = strlen(buf) - 1; /* erase trailing newline */
381		while (p[cnt-1] == '\\') {
382			p += cnt-1;
383			if (fgets(p, sizeof (buf)-(p-buf), infp) == NULL)
384				goto breakout;
385			cnt = strlen(p) - 1;
386		}
387		if (strcmp(key_sep, DEFAULT_SEP) == 0) {
388			p = any(buf, " \t\n", num_del_to_match, count_esp);
389		} else {
390			p = any(buf, key_sep, num_del_to_match, count_esp);
391		}
392		key.dptr = buf;
393		key.dsize = p - buf;
394		for (;;) {
395			if (p == NULL || *p == NULL) {
396				fprintf(stderr,
397	"makedbm: source files is garbage!\n");
398				exit(1);
399			}
400			if (*p != ' ' && *p != '\t' && *p != key_sep[0])
401				break;
402			p++;
403		}
404		content.dptr = p;
405		content.dsize = strlen(p) - 1; /* erase trailing newline */
406		if (lower_case_keys) {
407			for (i = (strncmp(key.dptr, "YP_MULTI_", 9) ? 0 : 9);
408					i < key.dsize; i++) {
409
410				ic = *(key.dptr+i);
411				if (isascii(ic) && isupper(ic))
412					*(key.dptr+i) = tolower(ic);
413			}
414		}
415		tmp = dbm_fetch(fdb, key);
416		if (tmp.dptr == NULL) {
417			if (dbm_store(fdb, key, content, 1) != 0) {
418				printf("problem storing %.*s %.*s\n",
419				    key.dsize, key.dptr,
420				    content.dsize, content.dptr);
421				exit(1);
422			}
423		}
424#ifdef DEBUG
425		else {
426			printf("duplicate: %.*s %.*s\n",
427			    key.dsize, key.dptr,
428			    content.dsize, content.dptr);
429		}
430#endif
431	}
432	breakout:
433	addpair(fdb, yp_last_modified, get_date(infile));
434	if (infilename)
435		addpair(fdb, yp_input_file, infilename);
436	if (outfilename)
437		addpair(fdb, yp_output_file, outfilename);
438	if (domainname)
439		addpair(fdb, yp_domain_name, domainname);
440	if (security)
441		addpair(fdb, yp_secure, "");
442	if (interdomain_bind)
443	    addpair(fdb, yp_interdomain, "");
444	if (!mastername) {
445		sysinfo(SI_HOSTNAME, local_host, sizeof (local_host) - 1);
446		mastername = local_host;
447	}
448	addpair(fdb, yp_master_name, mastername);
449	(void) dbm_close(fdb);
450#ifdef DEBUG
451	fprintf(stderr, ".tmp ndbm map closed. ndbm successful !\n");
452#endif
453	if (rename(tmppagbuf, pagbuf) < 0) {
454		perror("makedbm: rename");
455		unlink(tmppagbuf);		/* Remove the tmp files */
456		unlink(tmpdirbuf);
457		exit(1);
458	}
459	if (rename(tmpdirbuf, dirbuf) < 0) {
460		perror("makedbm: rename");
461		unlink(tmppagbuf); /* Remove the tmp files */
462		unlink(tmpdirbuf);
463		exit(1);
464	}
465/*
466 *	sprintf(buf, "mv %s %s", tmppagbuf, pagbuf);
467 *	if (system(buf) < 0)
468 *		perror("makedbm: rename");
469 *	sprintf(buf, "mv %s %s", tmpdirbuf, dirbuf);
470 *	if (system(buf) < 0)
471 *		perror("makedbm: rename");
472 */
473	exit(0);
474}
475
476
477/*
478 * scans cp, looking for a match with any character
479 * in match.  Returns pointer to place in cp that matched
480 * (or NULL if no match)
481 *
482 * It will find the num_del_to_match+1
483 * matching character in the line.
484 *
485 * The backslash escapes a delimiter if count_esp==1
486 * We don't count it as a character match if
487 * an escape character precedes a matching character.
488 *
489 */
490static char *
491any(cp, match, num_del_to_match, count_esp)
492	register char *cp;
493	char *match;
494	int num_del_to_match;
495	int count_esp;
496{
497	register char *mp, c, prev_char;
498	int num_del_matched;
499
500	num_del_matched = 0;
501	prev_char = ' ';
502	while (c = *cp) {
503		for (mp = match; *mp; mp++) {
504			if (*mp == c) {
505				if (!count_esp) {
506					num_del_matched++;
507				} else if (prev_char != '\\') {
508					num_del_matched++;
509				}
510				if (num_del_matched > num_del_to_match)
511					return (cp);
512			}
513		}
514		prev_char = c;
515		cp++;
516	}
517	return ((char *)0);
518}
519
520static char *
521get_date(name)
522	char *name;
523{
524	struct stat filestat;
525	static char ans[MAX_ASCII_ORDER_NUMBER_LENGTH];
526	/* ASCII numeric string */
527
528	if (strcmp(name, "-") == 0)
529		sprintf(ans, "%010ld", (long)time(0));
530	else {
531		if (stat(name, &filestat) < 0) {
532			fprintf(stderr, "makedbm: can't stat %s\n", name);
533			exit(1);
534		}
535		sprintf(ans, "%010ld", (long)filestat.st_mtime);
536	}
537	return (ans);
538}
539
540void
541usage()
542{
543	fprintf(stderr,
544"usage: makedbm -u file\n	makedbm [-b] [-l] [-s] [-i YP_INPUT_FILE] "
545	    "[-o YP_OUTPUT_FILE] [-d YP_DOMAIN_NAME] [-m YP_MASTER_NAME] "
546	    "[-S DELIMITER] [-D NUM_DELIMITER_TO_SKIP] [-E] "
547	    "infile outfile\n");
548	exit(1);
549}
550
551void
552addpair(fdb, str1, str2)
553DBM *fdb;
554char *str1, *str2;
555{
556	datum key;
557	datum content;
558
559	key.dptr = str1;
560	key.dsize = strlen(str1);
561	content.dptr  = str2;
562	content.dsize = strlen(str2);
563	if (dbm_store(fdb, key, content, 1) != 0) {
564		printf("makedbm: problem storing %.*s %.*s\n",
565		    key.dsize, key.dptr, content.dsize, content.dptr);
566		exit(1);
567	}
568}
569
570void
571unmake(file)
572	char *file;
573{
574	datum key, content;
575	DBM *fdb;
576
577	if (file == NULL)
578		usage();
579
580	if ((fdb = dbm_open(file, O_RDONLY, 0644)) == NULL) {
581		fprintf(stderr, "makedbm: couldn't open %s dbm file\n", file);
582		exit(1);
583	}
584
585	for (key = dbm_firstkey(fdb); key.dptr != NULL;
586		key = dbm_nextkey(fdb)) {
587		content = dbm_fetch(fdb, key);
588		printf("%.*s %.*s\n", key.dsize, key.dptr,
589		    content.dsize, content.dptr);
590	}
591
592	dbm_close(fdb);
593}
594