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