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