xref: /minix3/external/bsd/bind/dist/contrib/dlz/bin/dlzbdb/dlzbdb.c (revision 00b67f09dd46474d133c95011a48590a8e8f94c7)
1 /*	$NetBSD: dlzbdb.c,v 1.3 2014/12/10 04:37:55 christos Exp $	*/
2 
3 /*
4  * Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
5  *
6  * Permission to use, copy, modify, and distribute this software for any
7  * purpose with or without fee is hereby granted, provided that the
8  * above copyright notice and this permission notice appear in all
9  * copies.
10  *
11  * THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
12  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
13  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
14  * STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
15  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
16  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
17  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
18  * USE OR PERFORMANCE OF THIS SOFTWARE.
19  *
20  * The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
21  * conceived and contributed by Rob Butler.
22  *
23  * Permission to use, copy, modify, and distribute this software for any
24  * purpose with or without fee is hereby granted, provided that the
25  * above copyright notice and this permission notice appear in all
26  * copies.
27  *
28  * THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
29  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
30  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
31  * ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
32  * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
33  * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
34  * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
35  * USE OR PERFORMANCE OF THIS SOFTWARE.
36  */
37 
38 /*
39  * Copyright (C) 1999-2001  Internet Software Consortium.
40  *
41  * Permission to use, copy, modify, and distribute this software for any
42  * purpose with or without fee is hereby granted, provided that the above
43  * copyright notice and this permission notice appear in all copies.
44  *
45  * THE SOFTWARE IS PROVIDED "AS IS" AND INTERNET SOFTWARE CONSORTIUM
46  * DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
47  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
48  * INTERNET SOFTWARE CONSORTIUM BE LIABLE FOR ANY SPECIAL, DIRECT,
49  * INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING
50  * FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT,
51  * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION
52  * WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
53  */
54 
55 #ifdef DLZ_BDB
56 
57 /*
58  * exit codes
59  * 0 everything ok
60  * 1 error parsing command line
61  * 2 Missing, too many or invalid combination of command line parameters
62  * 3 Unable to open BDB database.
63  * 4 Unable to allocate memory for, or create lexer.
64  * 5 unable to perform BDB cursor operation
65  */
66 
67 #include <config.h>
68 #include <stdio.h>
69 #include <string.h>
70 #include <stdlib.h>
71 
72 #include <isc/buffer.h>
73 #include <isc/commandline.h>
74 #include <isc/formatcheck.h>
75 #include <isc/lex.h>
76 #include <isc/mem.h>
77 #include <isc/result.h>
78 #include <isc/string.h>
79 #include <isc/util.h>
80 
81 #include <db.h>
82 
83 /* shut up compiler warnings about no previous prototype */
84 
85 static void
86 show_usage(void);
87 
88 int
89 getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
90 
91 int
92 gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
93 
94 void
95 bdb_cleanup(void);
96 
97 isc_result_t
98 bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags);
99 
100 void
101 put_data(isc_boolean_t dns_data, char *input_key, char *input_data);
102 
103 void
104 insert_data(void);
105 
106 isc_result_t
107 openBDB(void);
108 
109 isc_result_t
110 open_lexer(void);
111 
112 void
113 close_lexer(void);
114 
115 isc_result_t
116 bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata);
117 
118 void
119 operation_add(void);
120 
121 void
122 operation_bulk(void);
123 
124 void
125 operation_listOrDelete(isc_boolean_t dlt);
126 
127 
128 /*%
129  * Maximum length of a single data line that
130  * may be inserted into database by this program.
131  * If you need to insert a line of data that is more
132  * than 10,000 characters change this definition.
133  */
134 
135 #define max_data_len 10000
136 
137 /*%
138  * BDB database names.  If you want to use different
139  * database names change them here.
140  */
141 
142 #define dlz_data "dns_data"
143 #define dlz_zone "dns_zone"
144 #define dlz_host "dns_host"
145 #define dlz_client "dns_client"
146 
147 
148 /*%
149  * Error code returned by BDB secondary index callback functions.
150  * This error is returned if the callback function could not create
151  * the secondary index for any reason.
152  */
153 
154 #define BDBparseErr 1
155 
156 /* A struct to hold all the relevant info about the database */
157 
158 typedef struct bdb_instance {
159 	DB_ENV	*dbenv;		/* BDB environment */
160 	DB	*data;		/* dns_data database handle */
161 	DBC	*cursor;	/* database cursor */
162 	DBC	*cursor2; /* second cursor used during list operation. */
163 	DBC	*cursor3; /* third cursor used during list operation */
164 	DBC	*cursor4; /* fourth cursor used during list operation */
165 	DB	*zone;		/* zone database handle */
166 	DB	*host;		/* host database handle */
167 	DB	*client;	/* client database handle */
168 } bdb_instance_t;
169 
170 /* Possible operations */
171 
172 #define list 1		/* list data */
173 #define dele 2		/* delete data */
174 #define add 3		/* add a single piece of data */
175 #define bulk 4		/* bulk load data */
176 
177 
178 /*%
179  * quit macro is used instead of exit.  quit always trys to close the lexer
180  * and the BDB database before exiting.
181  */
182 
183 #define quit(i) 	close_lexer(); bdb_cleanup(); exit(i);
184 
185 /*%
186  * checkOp is used to verify that only one operation (list, del, add,
187  * bulk from file, bulk from stdin) is specified on the command line.
188  * This prevents a user from specifying two operations on the command
189  * line, which would make no sense anyway.
190  */
191 
192 #define checkOp(x) if (x != 0) {fprintf(stderr, "\nonly one operation "\
193 				"(l e d a f s) may be specified\n"); quit(2);}
194 
195 /*%
196  * checkParam is used to only allow a parameter to be specified once.
197  * I.E. the parameter key can only be used on the command line once.
198  * any attempt to use it twice causes an error.
199  */
200 
201 #define checkParam(x, y) if (x != NULL) {fprintf(stderr, "\n%s may only "\
202 					 "be specified once\n", y); quit(2);}
203 
204 /*%
205  * checkInvalidParam is used to only allow paramters which make sense for
206  * the operation selected.  I.E. passing the key parameter makes no sense
207  * for the add operation, and thus it isn't allowed.
208  */
209 
210 #define checkInvalidParam(x, y, z) if (x != NULL) {fprintf(stderr, "\n%s "\
211 						"may not be specified %s\n", y, z); quit(2);}
212 
213 /*%
214  * checkInvalidOption is used to only allow paramters which make sense for
215  * the operation selected - but checks boolean options.
216  * I.E. passing the "b" bare_list parameter makes no sense for the add
217  * operation, and thus it isn't allowed.
218  * if w == x then output error message "flag", "message"
219  */
220 
221 #define checkInvalidOption(w, x, y, z) if (w == x) {fprintf(stderr, "\n%s "\
222 						"may not be specified %s\n", y, z); quit(2);}
223 
224 /* Global Variables */
225 
226 int operation = 0;		/*%< operation to perform. */
227 /*% allow new lock files or DB to be created. */
228 isc_boolean_t create_allowed = isc_boolean_false;
229 char *key = NULL;		/*%< key to use in list & del operations */
230 
231 /*% dump DB in DLZBDB bulk format */
232 isc_boolean_t list_everything = isc_boolean_false;
233 unsigned int key_val; /*%< key as unsigned int used in list & del operations */
234 char *zone = NULL;		/*%< zone to use in list operations */
235 char *host = NULL;		/*%< host to use in list operations */
236 char *c_zone = NULL;	 /*%< client zone to use in list operations */
237 char *c_ip = NULL;	   /*%< client IP to use in list operations */
238 char *a_data = NULL;		/*%< data in add operation */
239 char *bulk_file = NULL;		/*%< bulk data file to load */
240 char *db_envdir = NULL;		/*%< BDB environment location  */
241 char *db_file = NULL;		/*%< BDB database file location. */
242 bdb_instance_t db;		/* BDB instance we are operating on */
243 isc_lex_t *lexer = NULL; /*%< lexer for use to use in parsing input */
244 isc_mem_t *lex_mctx = NULL;	/*%< memory context for lexer */
245 char lex_data_buf[max_data_len]; /*%< data array to use for lex_buffer below */
246 isc_buffer_t lex_buffer;   /*%< buffer for lexer during add operation */
247 
248 
249 /*%
250  * Displays usage message
251  */
252 
253 static void
show_usage(void)254 show_usage(void) {
255 	fprintf(stderr, "\n\n\
256 ---Usage:---------------------------------------------------------------------\
257 \n\n\
258    List data:\n\
259       dlzbdb -l [-k key] [-z zone] [-h host] [-c client_zone] [-i client_ip]\n\
260                BDB_environment BDB_database\n\n\
261    Delete data:\n\
262       dlzbdb -d [-k key] [-c client_zone] [-i client_ip]\n\
263                BDB_environment BDB_database\n\n\
264    Bulk load data from file:\n\
265       dlzbdb -f file_to_load BDB_environment BDB_database\n\n\
266    Bulk load data from stdin\n\
267       dlzbdb -s BDB_environment BDB_database\n\n\
268    Add data:\n\
269       dlzbdb -a \"dns data to be added\" BDB_environment BDB_database\n\n\
270    Export data:\n\
271       dlzbdb -e BDB_environment BDB_database\n\n\
272    Normally operations can only be performed on an existing database files.\n\
273    Use the -n flag with any operation to allow files to be created.\n\
274    Existing files will NOT be truncated by using the -n flag.\n\
275    The -n flag will allow a new database to be created, or allow new\n\
276    environment files to be created for an existing database.\n\n\
277 ---Format for -f & -a options:------------------------------------------------\
278 \n\n\
279 db_type zone host dns_type ttl ip\n\
280 db_type zone host dns_type ttl mx_priority mail_host\n\
281 db_type zone host dns_type ttl nm_svr resp_psn serial refresh retry expire min\
282 \n\
283 db_type zone client_ip\n\n\
284 ---Examples:------------------------------------------------------------------\
285 \n\n\
286 d mynm.com www A 10 127.0.0.1\n\
287 d mynm.com @ MX 10 5 mail\n\
288 d mynm.com @ SOA 10 ns1.mynm.com. root.mynm.com. 2 28800 7200 604800 86400\n\
289 c mynm.com 127.0.0.1\n\
290 c mynm.com 192.168.0.10\n\
291 ");
292 quit(1);
293 }
294 
295 
296 /*% BDB callback to create zone secondary index */
297 
298 int
getzone(DB * dbp,const DBT * pkey,const DBT * pdata,DBT * skey)299 getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) {
300 	char *tmp;
301 	char *left;
302 	char *right;
303 	int result=0;
304 
305 	UNUSED(dbp);
306 	UNUSED(pkey);
307 
308 	/* Allocate memory to use in parsing the string */
309 	tmp = right = malloc(pdata->size + 1);
310 
311 	/* verify memory was allocated */
312 	if (right == NULL) {
313 		result = BDBparseErr;
314 		goto getzone_cleanup;
315 	}
316 
317 	/* copy data string into newly allocated memory */
318 	strncpy(right, pdata->data, pdata->size);
319 	right[pdata->size] = '\0';
320 
321 	/* split string at the first space */
322 	left = isc_string_separate(&right, " ");
323 
324 	/* copy string for "zone" secondary index */
325 	skey->data = strdup(left);
326 	if (skey->data == NULL) {
327 		result = BDBparseErr;
328 		goto getzone_cleanup;
329 	}
330 	/* set required values for BDB */
331 	skey->size = strlen(skey->data);
332 	skey->flags = DB_DBT_APPMALLOC;
333 
334  getzone_cleanup:
335 
336 	/* cleanup memory */
337 	if (tmp != NULL)
338 		free(tmp);
339 
340 	return result;
341 }
342 
343 /*%
344  * BDB callback to create host secondary index
345  */
346 
347 int
gethost(DB * dbp,const DBT * pkey,const DBT * pdata,DBT * skey)348 gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) {
349 	char *tmp;
350 	char *left;
351 	char *right;
352 	int result=0;
353 
354 	UNUSED(dbp);
355 	UNUSED(pkey);
356 
357 	/* allocate memory to use in parsing the string */
358 	tmp = right = malloc(pdata->size + 1);
359 
360 	/* verify memory was allocated */
361 	if (tmp == NULL) {
362 		result = BDBparseErr;
363 		goto gethost_cleanup;
364 	}
365 
366 	/* copy data string into newly allocated memory */
367 	strncpy(right, pdata->data, pdata->size);
368 	right[pdata->size] = '\0';
369 
370 	/* we don't care about left string. */
371 	/* memory of left string will be freed when tmp is freed. */
372 	isc_string_separate(&right, " ");
373 
374 	/* verify right still has some characters left */
375 	if (right == NULL) {
376 		result = BDBparseErr;
377 		goto gethost_cleanup;
378 	}
379 
380 	/* get "host" from data string */
381 	left = isc_string_separate(&right, " ");
382 	/* copy string for "host" secondary index */
383 	skey->data = strdup(left);
384 	if (skey->data == NULL) {
385 		result = BDBparseErr;
386 		goto gethost_cleanup;
387 	}
388 	/* set required values for BDB */
389 	skey->size = strlen(skey->data);
390 	skey->flags = DB_DBT_APPMALLOC;
391 
392  gethost_cleanup:
393 
394 	/* cleanup memory */
395 	if (tmp != NULL)
396 		free(tmp);
397 
398 	return result;
399 }
400 
401 /*%
402  * Performs BDB cleanup. Close each database that we opened.
403  * Close environment.  Set each var to NULL so we know they
404  * were closed and don't accidentally try to close them twice.
405  */
406 
407 void
bdb_cleanup(void)408 bdb_cleanup(void) {
409 
410 	/* close cursors */
411 	if (db.cursor4 != NULL) {
412 		db.cursor4->c_close(db.cursor4);
413 		db.cursor4 = NULL;
414 	}
415 
416 	if (db.cursor3 != NULL) {
417 		db.cursor3->c_close(db.cursor3);
418 		db.cursor3 = NULL;
419 	}
420 
421 	if (db.cursor2 != NULL) {
422 		db.cursor2->c_close(db.cursor2);
423 		db.cursor2 = NULL;
424 	}
425 
426 	if (db.cursor != NULL) {
427 		db.cursor->c_close(db.cursor);
428 		db.cursor = NULL;
429 	}
430 
431 	/* close databases */
432 	if (db.data != NULL) {
433 		db.data->close(db.data, 0);
434 		db.data = NULL;
435 	}
436 	if (db.host != NULL) {
437 		db.host->close(db.host, 0);
438 		db.host = NULL;
439 	}
440 	if (db.zone != NULL) {
441 		db.zone->close(db.zone, 0);
442 		db.zone = NULL;
443 	}
444 	if (db.client != NULL) {
445 		db.client->close(db.client, 0);
446 		db.client = NULL;
447 	}
448 
449 	/* close environment */
450 	if (db.dbenv != NULL) {
451 		db.dbenv->close(db.dbenv, 0);
452 		db.dbenv = NULL;
453 	}
454 }
455 
456 /*% Initializes, sets flags and then opens Berkeley databases. */
457 
458 isc_result_t
bdb_opendb(DBTYPE db_type,DB ** db_out,const char * db_name,int flags)459 bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags) {
460 
461 	int result;
462 	int createFlag = 0;
463 
464 	/* Initialize the database. */
465 	if ((result = db_create(db_out, db.dbenv, 0)) != 0) {
466 		fprintf(stderr, "BDB could not initialize %s database. BDB error: %s",
467 			db_name, db_strerror(result));
468 		return ISC_R_FAILURE;
469 	}
470 
471 	/* set database flags. */
472 	if ((result = (*db_out)->set_flags(*db_out, flags)) != 0) {
473 		fprintf(stderr, "BDB could not set flags for %s database. BDB error: %s",
474 			db_name, db_strerror(result));
475 		return ISC_R_FAILURE;
476 	}
477 
478 	if (create_allowed == isc_boolean_true) {
479 		createFlag = DB_CREATE;
480 	}
481 	/* open the database. */
482 	if ((result = (*db_out)->open(*db_out, NULL, db_file, db_name, db_type,
483 				      createFlag, 0)) != 0) {
484 		fprintf(stderr, "BDB could not open %s database in %s. BDB error: %s",
485 			db_name, db_file, db_strerror(result));
486 		return ISC_R_FAILURE;
487 	}
488 
489 	return ISC_R_SUCCESS;
490 }
491 
492 /*%
493  * parses input and adds it to the BDB database
494  * Lexer should be instantiated, and either a file or buffer opened for it.
495  * The insert_data function is used by both the add, and bulk insert
496  * operations
497  */
498 
499 void
put_data(isc_boolean_t dns_data,char * input_key,char * input_data)500 put_data(isc_boolean_t dns_data, char *input_key, char *input_data) {
501 
502 	int bdbres;
503 	DBT key, data;
504 
505 	/* make sure key & data are completely empty */
506 	memset(&key, 0, sizeof(key));
507 	memset(&data, 0, sizeof(data));
508 
509 	/* if client data, setup key for insertion */
510 	if (!dns_data && input_key != NULL) {
511 		key.data = input_key;
512 		key.size = strlen(input_key);
513 		key.flags = 0;
514 	}
515 	/* always setup data for insertion */
516 	data.data = input_data;
517 	data.size = strlen(input_data);
518 	data.flags = 0;
519 
520 	/* execute insert against appropriate database. */
521 	if (dns_data) {
522 		bdbres = db.data->put(db.data, NULL, &key, &data, DB_APPEND);
523 	} else {
524 		bdbres = db.client->put(db.client, NULL, &key, &data, 0);
525 	}
526 
527 	/* if something went wrong, log error and quit */
528 	if (bdbres != 0) {
529 		fprintf(stderr, "BDB could not insert data.  Error: %s",
530 			db_strerror(bdbres));
531 		quit(5);
532 	}
533 }
534 
535 void
insert_data(void)536 insert_data(void) {
537 	unsigned int opt =
538 		ISC_LEXOPT_EOL |		/* Want end-of-line token. */
539 		ISC_LEXOPT_EOF | 		/* Want end-of-file token. */
540 		ISC_LEXOPT_QSTRING |		/* Recognize qstrings. */
541 		ISC_LEXOPT_QSTRINGMULTILINE;	/* Allow multiline "" strings */
542 
543 	isc_result_t result;
544 	isc_token_t token;	/* token from lexer */
545 	isc_boolean_t loop = isc_boolean_true;
546 	isc_boolean_t have_czone = isc_boolean_false;
547 	char data_arr[max_data_len];
548 	isc_buffer_t buf;
549 	char data_arr2[max_data_len];
550 	isc_buffer_t buf2;
551 	char data_type = 'u'; /* u =unknown, b =bad token, d/D =DNS, c/C =client IP */
552 
553 	/* Initialize buffers */
554 	isc_buffer_init(&buf, &data_arr, max_data_len);
555 	isc_buffer_init(&buf2, &data_arr2, max_data_len);
556 
557 	while (loop) {
558 		result = isc_lex_gettoken(lexer, opt, &token);
559 		if (result != ISC_R_SUCCESS)
560 			goto data_cleanup;
561 
562 		switch(token.type) {
563 		case isc_tokentype_string:
564 			if (data_type == 'u') {
565 				/* store data_type */
566 				strncpy(&data_type, token.value.as_pointer, 1);
567 				/* verify data_type was specified correctly on input */
568 				if (strlen(token.value.as_pointer) > 1 || (
569 					    data_type != 'd' && data_type != 'D' &&
570 					    data_type != 'c' && data_type != 'C') ) {
571 					/* if not, set to 'b' so this line is ignored. */
572 					data_type = 'b';
573 				}
574 			} else if (data_type == 'c' || data_type == 'C') {
575 				if (have_czone == isc_boolean_true) {
576 					isc_buffer_putstr(&buf2, token.value.as_pointer);
577 					/* add string terminator to buffer */
578 					isc_buffer_putmem(&buf2, "\0", 1);
579 				} else {
580 					isc_buffer_putstr(&buf, token.value.as_pointer);
581 					/* add string terminator to buffer */
582 					isc_buffer_putmem(&buf, "\0", 1);
583 					have_czone = isc_boolean_true;
584 				}
585 			} else {
586 				isc_buffer_putstr(&buf, token.value.as_pointer);
587 				isc_buffer_putstr(&buf, " ");
588 			}
589 			break;
590 		case isc_tokentype_qstring:
591 			isc_buffer_putstr(&buf, "\"");
592 			isc_buffer_putstr(&buf, token.value.as_pointer);
593 			isc_buffer_putstr(&buf, "\" ");
594 			break;
595 		case isc_tokentype_eol:
596 		case isc_tokentype_eof:
597 
598 			if ((data_type != 'u' && isc_buffer_usedlength(&buf) > 0) || data_type == 'b') {
599 				/* perform insert operation */
600 				if (data_type == 'd' || data_type == 'D') {
601 					/* add string terminator to buffer */
602 					isc_buffer_putmem(&buf, "\0", 1);
603 					put_data(isc_boolean_true, NULL, (char *) &data_arr);
604 				} else if (data_type == 'c' || data_type == 'C') {
605 					put_data(isc_boolean_false, (char *) &data_arr,
606 						 (char *) &data_arr2);
607 				} else if (data_type == 'b') {
608 					fprintf(stderr, "Bad / unknown token encountered on line %lu."\
609 						"  Skipping line.",	isc_lex_getsourceline(lexer) - 1);
610 				} else {
611 					fprintf(stderr, "Bad / unknown db data type encountered on " \
612 						"line %lu.  Skipping line\n", isc_lex_getsourceline(lexer) - 1);
613 				}
614 			}
615 
616 			if (token.type == isc_tokentype_eof) {
617 				loop = isc_boolean_false;
618 			}
619 
620 			/* reset buffer for next insert */
621 			isc_buffer_clear(&buf);
622 			isc_buffer_clear(&buf2);
623 			have_czone = isc_boolean_false;
624 			data_type ='u';
625 			break;
626 		default:
627 			data_type = 'b';
628 			break;
629 		}
630 	}
631 
632 	return;
633 
634  data_cleanup:
635 	/* let user know we had problems */
636 	fprintf(stderr, "Unknown error processing tokens during \"add\" or " \
637 		"\"bulk\" operation.\nStoped processing on line %lu.",
638 		isc_lex_getsourceline(lexer));
639 }
640 
641 
642 isc_result_t
openBDB(void)643 openBDB(void) {
644 
645 	int bdbres;
646 	isc_result_t result;
647 
648 	/* create BDB environment  */
649 	/* Basically BDB allocates and assigns memory to db->dbenv */
650 	bdbres = db_env_create(&db.dbenv, 0);
651 	if (bdbres != 0) {
652 		fprintf(stderr, "BDB environment could not be created. BDB error: %s",
653 			db_strerror(bdbres));
654 		result = ISC_R_FAILURE;
655 		goto openBDB_cleanup;
656 	}
657 
658 	/* open BDB environment */
659 	if (create_allowed == isc_boolean_true) {
660 		/* allowed to create new files */
661 		bdbres = db.dbenv->open(db.dbenv, db_envdir,
662 					DB_INIT_CDB | DB_INIT_MPOOL | DB_CREATE, 0);
663 	} else {	/* not allowed to create new files. */
664 		bdbres = db.dbenv->open(db.dbenv, db_envdir,
665 					DB_INIT_CDB | DB_INIT_MPOOL, 0);
666 	}
667 	if (bdbres != 0) {
668 		fprintf(stderr, "BDB environment at '%s' could not be opened. BDB " \
669 			"error: %s", db_envdir, db_strerror(bdbres));
670 		result = ISC_R_FAILURE;
671 		goto openBDB_cleanup;
672 	}
673 
674 	/* open dlz_data database. */
675 
676 	result = bdb_opendb(DB_RECNO, &db.data, dlz_data, 0);
677 	if (result != ISC_R_SUCCESS)
678 		goto openBDB_cleanup;
679 
680 	/* open dlz_host database */
681 	result = bdb_opendb(DB_BTREE, &db.host, dlz_host, DB_DUP | DB_DUPSORT);
682 	if (result != ISC_R_SUCCESS)
683 		goto openBDB_cleanup;
684 
685 	/* open dlz_zone database. */
686 	result = bdb_opendb(DB_BTREE, &db.zone, dlz_zone, DB_DUP | DB_DUPSORT);
687 	if (result != ISC_R_SUCCESS)
688 		goto openBDB_cleanup;
689 
690 	/* open dlz_client database. */
691 	result = bdb_opendb(DB_BTREE, &db.client, dlz_client, DB_DUP | DB_DUPSORT);
692 	if (result != ISC_R_SUCCESS)
693 		goto openBDB_cleanup;
694 
695 	/* associate the host secondary database with the primary database */
696 	bdbres = db.data->associate(db.data, NULL, db.host, gethost, 0);
697 	if (bdbres != 0) {
698 		fprintf(stderr, "BDB could not associate %s database with %s. BDB "\
699 			"error: %s", dlz_host, dlz_data, db_strerror(bdbres));
700 		result = ISC_R_FAILURE;
701 		goto openBDB_cleanup;
702 	}
703 
704 	/* associate the zone secondary database with the primary database */
705 	bdbres = db.data->associate(db.data, NULL, db.zone, getzone, 0);
706 	if (bdbres != 0) {
707 		fprintf(stderr, "BDB could not associate %s database with %s. BDB "\
708 			"error: %s", dlz_zone, dlz_data, db_strerror(bdbres));
709 		result = ISC_R_FAILURE;
710 		goto openBDB_cleanup;
711 	}
712 
713 	return result;
714 
715  openBDB_cleanup:
716 
717 	bdb_cleanup();
718 	return result;
719 }
720 
721 /*% Create & open lexer to parse input data */
722 
723 isc_result_t
open_lexer(void)724 open_lexer(void) {
725 	isc_result_t result;
726 
727 	/* check if we already opened the lexer, if we did, return success */
728 	if (lexer != NULL)
729 		return ISC_R_SUCCESS;
730 
731 	/* allocate memory for lexer, and verify it was allocated */
732 	result = isc_mem_create(0, 0, &lex_mctx);
733 	if (result != ISC_R_SUCCESS) {
734 		fprintf(stderr, "unexpected error creating lexer\n");
735 		return result;
736 	}
737 
738 	/* create lexer */
739 	result = isc_lex_create(lex_mctx, 1500, &lexer);
740 	if (result != ISC_R_SUCCESS)
741 		fprintf(stderr, "unexpected error creating lexer\n");
742 
743 	/* set allowed commenting style */
744 	isc_lex_setcomments(lexer, ISC_LEXCOMMENT_C |	/* Allow C comments */
745 			    ISC_LEXCOMMENT_CPLUSPLUS |	/* Allow C++ comments */
746 			    ISC_LEXCOMMENT_SHELL);		/* Allow shellcomments */
747 
748 	isc_buffer_init(&lex_buffer, &lex_data_buf, max_data_len);
749 
750 	return result;
751 }
752 
753 /*% Close the lexer, and cleanup memory */
754 
755 void
close_lexer(void)756 close_lexer(void) {
757 
758 	/* If lexer is still open, close it & destroy it. */
759 	if (lexer != NULL) {
760 		isc_lex_close(lexer);
761 		isc_lex_destroy(&lexer);
762 	}
763 
764 	/* if lexer memory is still allocated, destroy it. */
765 	if (lex_mctx != NULL)
766 		isc_mem_destroy(&lex_mctx);
767 }
768 
769 /*% Perform add operation */
770 
771 void
operation_add(void)772 operation_add(void) {
773 	/* check for any parameters that are not allowed during add */
774 	checkInvalidParam(key, "k", "for add operation");
775 	checkInvalidParam(zone, "z", "for add operation");
776 	checkInvalidParam(host, "h", "for add operation");
777 	checkInvalidParam(c_zone, "c", "for add operation");
778 	checkInvalidParam(c_ip, "i", "for add operation");
779 	checkInvalidOption(list_everything, isc_boolean_true, "e",
780 			   "for add operation");
781 
782 	/* if open lexer fails it alread prints error messages. */
783 	if (open_lexer() != ISC_R_SUCCESS) {
784 		quit(4);
785 	}
786 
787 	/* copy input data to buffer */
788 	isc_buffer_putstr(&lex_buffer, a_data);
789 
790 	/* tell lexer to use buffer as input  */
791 	if (isc_lex_openbuffer(lexer, &lex_buffer) != ISC_R_SUCCESS) {
792 		fprintf(stderr, "unexpected error opening lexer buffer");
793 		quit(4);
794 	}
795 
796 	/*common logic for "add" & "bulk" operations are handled by insert_data */
797 	insert_data();
798 }
799 
800 /*% Perform bulk insert operation */
801 
802 void
operation_bulk(void)803 operation_bulk(void) {
804 	/* check for any parameters that are not allowed during bulk */
805 	checkInvalidParam(key, "k", "for bulk load operation");
806 	checkInvalidParam(zone, "z", "for bulk load operation");
807 	checkInvalidParam(host, "h", "for bulk load operation");
808 	checkInvalidParam(c_zone, "c", "for bulk load operation");
809 	checkInvalidParam(c_ip, "i", "for bulk load operation");
810 	checkInvalidOption(list_everything, isc_boolean_true, "e",
811 			   "for bulk load operation");
812 
813 	/* if open lexer fails it already prints error messages. */
814 	if (open_lexer() != ISC_R_SUCCESS) {
815 		quit(4);
816 	}
817 
818 	if (bulk_file == NULL) {
819 		if (isc_lex_openstream(lexer, stdin) != ISC_R_SUCCESS) {
820 			fprintf(stderr, "unexpected error opening stdin by lexer.");
821 			quit(4);
822 		}
823 	} else if (isc_lex_openfile(lexer, bulk_file) != ISC_R_SUCCESS) {
824 		fprintf(stderr, "unexpected error opening %s by lexer.", bulk_file);
825 		quit(4);
826 	}
827 
828 	/* common logic for "add" & "bulk" operations are handled by insert_data */
829 	insert_data();
830 }
831 
832 isc_result_t
bulk_write(char type,DB * database,DBC * dbcursor,DBT * bdbkey,DBT * bdbdata)833 bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata) {
834 
835 	int bdbres;
836 	db_recno_t recNum;
837 	char *retkey = NULL, *retdata;
838 	size_t retklen = 0, retdlen;
839 	void *p;
840 
841 	/* use a 5MB buffer for the bulk dump */
842 	int buffer_size = 5 * 1024 * 1024;
843 
844 	/* try to allocate a 5 MB buffer, if we fail write err msg, die. */
845 	bdbdata->data = malloc(buffer_size);
846 	if (bdbdata->data == NULL) {
847 		fprintf(stderr,
848 			"Unable to allocate 5 MB buffer for bulk database dump\n");
849 		return ISC_R_FAILURE;
850 	}
851 	bdbdata->ulen = buffer_size;
852 	bdbdata->flags = DB_DBT_USERMEM;
853 
854 	/* get a cursor, make sure it worked. */
855 	bdbres = database->cursor(database, NULL, &dbcursor, 0);
856 	if (bdbres != 0) {
857 		fprintf(stderr, "Unexpected error. BDB Error: %s\n",db_strerror(bdbres));
858 		free(bdbdata->data);
859 		return ISC_R_FAILURE;
860 	}
861 
862 	/* loop and dump all data */
863 	for (;;) {
864 
865 		/* loop through data until DB_NOTFOUND is returned */
866 		bdbres = dbcursor->c_get(dbcursor, bdbkey, bdbdata,
867 					 DB_MULTIPLE_KEY | DB_NEXT);
868 		/* if not successful did we encounter DB_NOTFOUND, or */
869 		/* have  a different problem. */
870 		if (bdbres != 0) {
871 			if (bdbres != DB_NOTFOUND) {
872 				fprintf(stderr, "Unexpected error. BDB Error: %s\n",
873 					db_strerror(bdbres));
874 				free(bdbdata->data);
875 				return ISC_R_FAILURE;
876 			}
877 			/* Hit DB_NOTFOUND which means end of data. */
878 			break;
879 		} /* end of if (bdbres !=0) */
880 
881 		for (DB_MULTIPLE_INIT(p, bdbdata);;) {
882 			if (type == 'c')
883 				DB_MULTIPLE_KEY_NEXT(p, bdbdata, retkey, retklen, retdata, retdlen);
884 			else
885 				DB_MULTIPLE_RECNO_NEXT(p, bdbdata, recNum, retdata, retdlen);
886 
887 			if (p == NULL)
888 				break;
889 			if (type == 'c')
890 				printf("c %.*s %.*s\n",(int)retklen, retkey,(int)retdlen, retdata);
891 			else
892 				printf("d %.*s\n", (int)retdlen, retdata);
893 		} /* end of for (DB_MULTIPLE_INIT....) */
894 
895 	} /* end of for (;;) */
896 
897 	/* free the buffer we created earlier */
898 	free(bdbdata->data);
899 
900 	return ISC_R_SUCCESS;
901 }
902 
903 /*%
904  * Perform listOrDelete operation
905  * if dlt == true, delete data
906  * else list data
907  */
908 
909 void
operation_listOrDelete(isc_boolean_t dlt)910 operation_listOrDelete(isc_boolean_t dlt) {
911 
912 	int bdbres = 0;
913 	DBC *curList[3];
914 	DBT bdbkey, bdbdata;
915 	db_recno_t recno;
916 	int curIndex = 0;
917 
918 
919 	/* verify that only allowed parameters were passed. */
920 	if (dlt == isc_boolean_true) {
921 		checkInvalidParam(zone, "z", "for delete operation");
922 		checkInvalidParam(host, "h", "for delete operation");
923 		checkInvalidOption(list_everything, isc_boolean_true, "e",
924 				   "for delete operation");
925 		checkInvalidOption(create_allowed, isc_boolean_true, "n",
926 				   "for delete operation");
927 	} else if (key != NULL || zone != NULL || host != NULL) {
928 		checkInvalidParam(c_zone, "c", "for list when k, z or h are specified");
929 		checkInvalidParam(c_ip, "i", "for list when k, z, or h are specified");
930 		checkInvalidOption(list_everything, isc_boolean_true, "e",
931 				   "for list when k, z, or h are specified");
932 		checkInvalidOption(create_allowed, isc_boolean_true, "n",
933 				   "for list operation");
934 	} else if (c_ip != NULL || c_zone != NULL) {
935 		checkInvalidOption(list_everything, isc_boolean_true, "e",
936 				   "for list when c or i are specified");
937 		checkInvalidOption(create_allowed, isc_boolean_true, "n",
938 				   "for list operation");
939 	}
940 
941 	memset(&bdbkey, 0, sizeof(bdbkey));
942 	memset(&bdbdata, 0, sizeof(bdbdata));
943 
944 	/* Dump database in "dlzbdb" bulk format */
945 	if (list_everything == isc_boolean_true) {
946 		if (bulk_write('c', db.client, db.cursor, &bdbkey, &bdbdata)
947 		    != ISC_R_SUCCESS)
948 			return;
949 		memset(&bdbkey, 0, sizeof(bdbkey));
950 		memset(&bdbdata, 0, sizeof(bdbdata));
951 		bulk_write('d', db.data, db.cursor2, &bdbkey, &bdbdata);
952 		return;
953 	} /* end if (list_everything) */
954 
955 	/* set NULL the 2nd and 3rd positions in curList. */
956 	/* that way later when add cursors to the join list */
957 	/* it is already null terminated. */
958 	curList[1] = curList[2] = NULL;
959 
960 	if (key != NULL) {
961 		/* make sure other parameters weren't */
962 		checkInvalidParam(zone, "z", "when k is specified");
963 		checkInvalidParam(host, "h", "when k is specified");
964 
965 		recno = key_val;
966 		bdbkey.data = &recno;
967 		bdbkey.size = sizeof(recno);
968 
969 		if (dlt == isc_boolean_true) {
970 			bdbres = db.data->del(db.data, NULL, &bdbkey, 0);
971 		} else {
972 			bdbdata.flags = DB_DBT_REALLOC;
973 			bdbres = db.data->get(db.data, NULL, &bdbkey, &bdbdata, 0);
974 
975 			if (bdbres == 0) {
976 				printf("KEY | DATA\n");
977 				printf("%lu | %.*s\n", *(u_long *) bdbkey.data,
978 				       (int)bdbdata.size, (char *)bdbdata.data);
979 			}
980 		} /* closes else of if (dlt == isc_boolean_true) */
981 		if (bdbres == DB_NOTFOUND) {
982 			printf("Key not found in database");
983 		}
984 	}	/* closes if (key != NULL) */
985 
986 		/* if zone is passed */
987 	if (zone != NULL) {
988 		/* create a cursor and make sure it worked */
989 		bdbres = db.zone->cursor(db.zone, NULL, &db.cursor2, 0);
990 		if (bdbres != 0) {
991 			fprintf(stderr, "Unexpected error. BDB Error: %s\n",
992 				db_strerror(bdbres));
993 			return;
994 		}
995 
996 		bdbkey.data = zone;
997 		bdbkey.size = strlen(zone);
998 		bdbres = db.cursor2->c_get(db.cursor2, &bdbkey, &bdbdata, DB_SET);
999 		if (bdbres != 0) {
1000 			if (bdbres != DB_NOTFOUND) {
1001 				fprintf(stderr, "Unexpected error. BDB Error: %s\n",
1002 					db_strerror(bdbres));
1003 			} else {
1004 				printf("Zone not found in database");
1005 			}
1006 			return;
1007 		}
1008 
1009 		/* add cursor to cursor list for later use in join */
1010 		curList[curIndex++] = db.cursor2;
1011 	}
1012 
1013 	/* if host is passed */
1014 	if (host != NULL) {
1015 
1016 		/* create a cursor and make sure it worked. */
1017 		bdbres = db.host->cursor(db.host, NULL, &db.cursor3, 0);
1018 		if (bdbres != 0) {
1019 			fprintf(stderr, "Unexpected error. BDB Error: %s\n",
1020 				db_strerror(bdbres));
1021 			return;
1022 		}
1023 		bdbkey.data = host;
1024 		bdbkey.size = strlen(host);
1025 		bdbres = db.cursor3->c_get(db.cursor3, &bdbkey, &bdbdata, DB_SET);
1026 		if (bdbres != 0) {
1027 			if (bdbres != DB_NOTFOUND) {
1028 				fprintf(stderr, "Unexpected error. BDB Error: %s\n",
1029 					db_strerror(bdbres));
1030 			} else {
1031 				printf("Host not found in database");
1032 			}
1033 			return;
1034 		}
1035 
1036 		/* add cursor to cursor list for later use in join */
1037 		curList[curIndex++] = db.cursor3;
1038 	}
1039 
1040 
1041 	if (zone != NULL || host != NULL) {
1042 
1043 		/* join any cursors */
1044 		bdbres = db.data->join(db.data, curList, &db.cursor4, 0);
1045 		if (bdbres != 0) {
1046 			fprintf(stderr, "Unexpected error. BDB Error: %s\n",
1047 				db_strerror(bdbres));
1048 			return;
1049 		}
1050 
1051 		memset(&bdbkey, 0, sizeof(bdbkey));
1052 		bdbkey.flags = DB_DBT_REALLOC;
1053 		memset(&bdbdata, 0, sizeof(bdbdata));
1054 		bdbdata.flags = DB_DBT_REALLOC;
1055 
1056 		/* print a header to explain the output */
1057 		printf("KEY | DATA\n");
1058 		/* loop and list all results. */
1059 		while (bdbres == 0) {
1060 			/* get data */
1061 			bdbres = db.cursor4->c_get(db.cursor4, &bdbkey, &bdbdata, 0);
1062 			/* verify call had no errors */
1063 			if (bdbres != 0) {
1064 				break;
1065 			}
1066 			printf("%lu | %.*s\n", *(u_long *) bdbkey.data,
1067 			       (int)bdbdata.size, (char *)bdbdata.data);
1068 		} /* closes while loop */
1069 	}
1070 
1071 	if (c_ip != NULL && c_zone == NULL) {
1072 		fprintf(stderr, "i may only be specified when c is also specified\n");
1073 		quit(2);
1074 	}
1075 	/* if client_zone was passed */
1076 	if (c_zone != NULL) {
1077 
1078 		/* create a cursor and make sure it worked. */
1079 		if (dlt == isc_boolean_true) {
1080 			/* open read-write cursor */
1081 			bdbres = db.client->cursor(db.client, NULL, &db.cursor,
1082 						   DB_WRITECURSOR);
1083 		} else {
1084 			/* open read only cursor */
1085 			bdbres = db.client->cursor(db.client, NULL, &db.cursor, 0);
1086 			/* print a header to explain the output */
1087 			printf("CLIENT_ZONE | CLIENT_IP\n");
1088 		}
1089 
1090 		bdbkey.data = c_zone;
1091 		bdbkey.size = strlen(c_zone);
1092 
1093 		if (c_ip != NULL) {
1094 			bdbdata.data = c_ip;
1095 			bdbdata.size = strlen(c_ip);
1096 			bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_GET_BOTH);
1097 			if (bdbres == DB_NOTFOUND) {
1098 				printf("Client zone & IP not found in database");
1099 			}
1100 		} else {
1101 			bdbdata.flags = DB_DBT_REALLOC;
1102 			bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_SET);
1103 			if (bdbres == DB_NOTFOUND) {
1104 				printf("Client zone not found in database");
1105 			}
1106 		}
1107 
1108 		while (bdbres == 0) {
1109 			if (dlt == isc_boolean_false) {
1110 				printf("%.*s | %.*s\n", (int)bdbkey.size, (char *) bdbkey.data,
1111 				       (int)bdbdata.size, (char *) bdbdata.data);
1112 			} else {
1113 				/* delete record. */
1114 				bdbres = db.cursor->c_del(db.cursor, 0);
1115 				if (bdbres != 0) {
1116 					fprintf(stderr, "Unexpected error. BDB Error: %s\n",
1117 						db_strerror(bdbres));
1118 					break;
1119 				}
1120 			}
1121 			if (c_ip != NULL) {
1122 				break;
1123 			}
1124 			bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata, DB_NEXT_DUP);
1125 			if (bdbres != 0) {
1126 				break;
1127 			}
1128 		} /* end while loop */
1129 	}
1130 
1131 
1132 	if (bdbres != 0 && bdbres != DB_NOTFOUND) {
1133 		fprintf(stderr, "Unexpected error during list operation " \
1134 			"BDB error: %s", db_strerror(bdbres));
1135 	}
1136 
1137 	if (bdbkey.flags == DB_DBT_REALLOC && bdbkey.data != NULL) {
1138 		free(bdbkey.data);
1139 	}
1140 	if (bdbdata.flags == DB_DBT_REALLOC && bdbdata.data != NULL) {
1141 		free(bdbdata.data);
1142 	}
1143 }
1144 
1145 
1146 int
main(int argc,char ** argv)1147 main(int argc, char **argv) {
1148 
1149 	int ch;
1150 	char *endp;
1151 
1152 	/* there has to be at least 2 args, some operations require more */
1153 	if (argc < 2)
1154 		show_usage();
1155 
1156 	/* use the ISC commandline parser to get all the program arguments */
1157 	while ((ch= isc_commandline_parse(argc, argv, "ldesna:f:k:z:h:c:i:")) != -1) {
1158 		switch (ch) {
1159 		case 'n':
1160 			create_allowed = isc_boolean_true;
1161 			break;
1162 		case 'l':
1163 			checkOp(operation);
1164 			operation = list;
1165 			break;
1166 		case 'd':
1167 			checkOp(operation);
1168 			operation = dele;
1169 			break;
1170 		case 'a':
1171 			checkOp(operation);
1172 			operation = add;
1173 			a_data = isc_commandline_argument;
1174 			break;
1175 		case 'f':
1176 			checkOp(operation);
1177 			operation = bulk;
1178 			bulk_file = isc_commandline_argument;
1179 			break;
1180 		case 's':
1181 			checkOp(operation);
1182 			operation = bulk;
1183 			break;
1184 		case 'k':
1185 			checkParam(key, "k");
1186 			key = isc_commandline_argument;
1187 			key_val = strtoul(key, &endp, 10);
1188 			if (*endp != '\0' || key_val < 1) {
1189 				fprintf(stderr, "Error converting key to integer");
1190 			}
1191 			break;
1192 		case 'z':
1193 			checkParam(zone, "z");
1194 			zone = isc_commandline_argument;
1195 			break;
1196 		case 'h':
1197 			checkParam(host, "h");
1198 			host = isc_commandline_argument;
1199 			break;
1200 		case 'c':
1201 			checkParam(c_zone, "c");
1202 			c_zone = isc_commandline_argument;
1203 			break;
1204 		case 'i':
1205 			checkParam(c_ip, "i");
1206 			c_ip = isc_commandline_argument;
1207 			break;
1208 		case 'e':
1209 			checkOp(operation);
1210 			operation = list;
1211 			list_everything = isc_boolean_true;
1212 			break;
1213 		case '?':
1214 			show_usage();
1215 			break;
1216 		default:
1217 			/* should never reach this point */
1218 			fprintf(stderr, "unexpected error parsing command arguments\n");
1219 			quit(1);
1220 			break;
1221 		}
1222 	}
1223 
1224 	argc -= isc_commandline_index;
1225 	argv += isc_commandline_index;
1226 
1227 	/* argc & argv have been modified, so now only "extra" parameters are */
1228 	/* left in argc & argv.  "Extra" parameters are any parameters that were */
1229 	/* not passed using a command line flag.  Exactly 2 args should be left. */
1230 	/* The first should be the BDB environment path, the second should be the */
1231 	/* BDB database.  The BDB database path can be either relative to the */
1232 	/* BDB environment path, or absolute. */
1233 	if (argc < 2) {
1234 		fprintf(stderr, "Both a Berkeley DB environment and file "\
1235 			"must be specified");
1236 		quit(2);
1237 	} else if (argc > 2) {
1238 		fprintf(stderr, "Too many parameters. Check command line for errors.");
1239 		quit(2);
1240 	}
1241 
1242 	/* get db_file to operate on */
1243 	db_envdir = argv[0];
1244 	db_file = argv[1];
1245 
1246 	if (openBDB() != ISC_R_SUCCESS) {
1247 		/* openBDB already prints error messages, don't do it here. */
1248 		bdb_cleanup();
1249 		quit(3);
1250 	}
1251 
1252 	switch(operation) {
1253 	case list:
1254 		operation_listOrDelete(isc_boolean_false);
1255 		break;
1256 	case dele:
1257 		operation_listOrDelete(isc_boolean_true);
1258 		break;
1259 	case add:
1260 		operation_add();
1261 		break;
1262 	case bulk:
1263 		operation_bulk();
1264 		break;
1265 	default:
1266 		fprintf(stderr, "\nNo operation was selected. "\
1267 			"Select an operation (l d a f)");
1268 		quit(2);
1269 		break;
1270 	}
1271 
1272 	quit(0);
1273 }
1274 #endif
1275 
1276