xref: /illumos-gate/usr/src/cmd/ypcmd/makedbm.c (revision fd6d41c5025e9fb45a115fc82d86e9983d1e9fd6)
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"
52 USE_YP_MASTER_NAME
53 USE_YP_LAST_MODIFIED
54 USE_YP_INPUT_FILE
55 USE_YP_OUTPUT_NAME
56 USE_YP_DOMAIN_NAME
57 USE_YP_SECURE
58 USE_YP_INTERDOMAIN
59 USE_DBM
60 
61 #ifdef SYSVCONFIG
62 extern void sysvconfig();
63 #endif
64 extern int yp_getalias();
65 
66 #define	MAXLINE 4096		/* max length of input line */
67 #define	DEFAULT_SEP	" "
68 static char *get_date();
69 static char *any();
70 static void addpair();
71 static void unmake();
72 static void usage();
73 
74 int   inode_dev_valid = 0;
75 ino64_t inode;
76 dev_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
83 int
84 close(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 
109 int
110 main(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  */
490 static char *
491 any(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 
520 static char *
521 get_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 
540 void
541 usage()
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 
551 void
552 addpair(fdb, str1, str2)
553 DBM *fdb;
554 char *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 
570 void
571 unmake(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