mirror of
https://gitlab.isc.org/isc-projects/bind9.git
synced 2025-04-18 09:44:09 +03:00
1296 lines
34 KiB
C
1296 lines
34 KiB
C
/*
|
|
* Copyright (C) 2002 Stichting NLnet, Netherlands, stichting@nlnet.nl.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the
|
|
* above copyright notice and this permission notice appear in all
|
|
* copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND STICHTING NLNET
|
|
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
|
* STICHTING NLNET BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
|
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*
|
|
* The development of Dynamically Loadable Zones (DLZ) for Bind 9 was
|
|
* conceived and contributed by Rob Butler.
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the
|
|
* above copyright notice and this permission notice appear in all
|
|
* copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND ROB BUTLER
|
|
* DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE INCLUDING ALL
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL
|
|
* ROB BUTLER BE LIABLE FOR ANY SPECIAL, DIRECT, INDIRECT, OR
|
|
* CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS
|
|
* OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE
|
|
* OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE
|
|
* USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
/*
|
|
* Copyright (C) 1999-2001, 2016 Internet Systems Consortium, Inc. ("ISC")
|
|
*
|
|
* This Source Code Form is subject to the terms of the Mozilla Public
|
|
* License, v. 2.0. If a copy of the MPL was not distributed with this
|
|
* file, You can obtain one at http://mozilla.org/MPL/2.0/.
|
|
*/
|
|
|
|
#ifdef DLZ_BDB
|
|
|
|
/*
|
|
* exit codes
|
|
* 0 everything ok
|
|
* 1 error parsing command line
|
|
* 2 Missing, too many or invalid combination of command line parameters
|
|
* 3 Unable to open BDB database.
|
|
* 4 Unable to allocate memory for, or create lexer.
|
|
* 5 unable to perform BDB cursor operation
|
|
*/
|
|
#include <db.h>
|
|
#include <stdbool.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
|
|
#include <isc/buffer.h>
|
|
#include <isc/commandline.h>
|
|
#include <isc/formatcheck.h>
|
|
#include <isc/lex.h>
|
|
#include <isc/mem.h>
|
|
#include <isc/result.h>
|
|
#include <isc/string.h>
|
|
#include <isc/util.h>
|
|
|
|
/* shut up compiler warnings about no previous prototype */
|
|
|
|
static void
|
|
show_usage(void);
|
|
|
|
int
|
|
getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
|
|
|
|
int
|
|
gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey);
|
|
|
|
void
|
|
bdb_cleanup(void);
|
|
|
|
isc_result_t
|
|
bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags);
|
|
|
|
void
|
|
put_data(bool dns_data, char *input_key, char *input_data);
|
|
|
|
void
|
|
insert_data(void);
|
|
|
|
isc_result_t
|
|
openBDB(void);
|
|
|
|
isc_result_t
|
|
open_lexer(void);
|
|
|
|
void
|
|
close_lexer(void);
|
|
|
|
isc_result_t
|
|
bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata);
|
|
|
|
void
|
|
operation_add(void);
|
|
|
|
void
|
|
operation_bulk(void);
|
|
|
|
void
|
|
operation_listOrDelete(bool dlt);
|
|
|
|
/*%
|
|
* Maximum length of a single data line that
|
|
* may be inserted into database by this program.
|
|
* If you need to insert a line of data that is more
|
|
* than 10,000 characters change this definition.
|
|
*/
|
|
|
|
#define max_data_len 10000
|
|
|
|
/*%
|
|
* BDB database names. If you want to use different
|
|
* database names change them here.
|
|
*/
|
|
|
|
#define dlz_data "dns_data"
|
|
#define dlz_zone "dns_zone"
|
|
#define dlz_host "dns_host"
|
|
#define dlz_client "dns_client"
|
|
|
|
/*%
|
|
* Error code returned by BDB secondary index callback functions.
|
|
* This error is returned if the callback function could not create
|
|
* the secondary index for any reason.
|
|
*/
|
|
|
|
#define BDBparseErr 1
|
|
|
|
/* A struct to hold all the relevant info about the database */
|
|
|
|
typedef struct bdb_instance {
|
|
DB_ENV *dbenv; /* BDB environment */
|
|
DB *data; /* dns_data database handle */
|
|
DBC *cursor; /* database cursor */
|
|
DBC *cursor2; /* second cursor used during list operation. */
|
|
DBC *cursor3; /* third cursor used during list operation */
|
|
DBC *cursor4; /* fourth cursor used during list operation */
|
|
DB *zone; /* zone database handle */
|
|
DB *host; /* host database handle */
|
|
DB *client; /* client database handle */
|
|
} bdb_instance_t;
|
|
|
|
/* Possible operations */
|
|
|
|
#define list 1 /* list data */
|
|
#define dele 2 /* delete data */
|
|
#define add 3 /* add a single piece of data */
|
|
#define bulk 4 /* bulk load data */
|
|
|
|
/*%
|
|
* quit macro is used instead of exit. quit always tries to close the lexer
|
|
* and the BDB database before exiting.
|
|
*/
|
|
|
|
#define quit(i) \
|
|
close_lexer(); \
|
|
bdb_cleanup(); \
|
|
exit(i);
|
|
|
|
/*%
|
|
* checkOp is used to verify that only one operation (list, del, add,
|
|
* bulk from file, bulk from stdin) is specified on the command line.
|
|
* This prevents a user from specifying two operations on the command
|
|
* line, which would make no sense anyway.
|
|
*/
|
|
|
|
#define checkOp(x) \
|
|
if (x != 0) { \
|
|
fprintf(stderr, "\nonly one operation " \
|
|
"(l e d a f s) may be specified\n"); \
|
|
quit(2); \
|
|
}
|
|
|
|
/*%
|
|
* checkParam is used to only allow a parameter to be specified once.
|
|
* I.E. the parameter key can only be used on the command line once.
|
|
* any attempt to use it twice causes an error.
|
|
*/
|
|
|
|
#define checkParam(x, y) \
|
|
if (x != NULL) { \
|
|
fprintf(stderr, \
|
|
"\n%s may only " \
|
|
"be specified once\n", \
|
|
y); \
|
|
quit(2); \
|
|
}
|
|
|
|
/*%
|
|
* checkInvalidParam is used to only allow parameters which make sense for
|
|
* the operation selected. I.E. passing the key parameter makes no sense
|
|
* for the add operation, and thus it isn't allowed.
|
|
*/
|
|
|
|
#define checkInvalidParam(x, y, z) \
|
|
if (x != NULL) { \
|
|
fprintf(stderr, \
|
|
"\n%s " \
|
|
"may not be specified %s\n", \
|
|
y, z); \
|
|
quit(2); \
|
|
}
|
|
|
|
/*%
|
|
* checkInvalidOption is used to only allow parameters which make sense for
|
|
* the operation selected - but checks boolean options.
|
|
* I.E. passing the "b" bare_list parameter makes no sense for the add
|
|
* operation, and thus it isn't allowed.
|
|
* if w == x then output error message "flag", "message"
|
|
*/
|
|
|
|
#define checkInvalidOption(w, x, y, z) \
|
|
if (w == x) { \
|
|
fprintf(stderr, \
|
|
"\n%s " \
|
|
"may not be specified %s\n", \
|
|
y, z); \
|
|
quit(2); \
|
|
}
|
|
|
|
/* Global Variables */
|
|
|
|
int operation = 0; /*%< operation to perform. */
|
|
/*% allow new lock files or DB to be created. */
|
|
bool create_allowed = false;
|
|
char *key = NULL; /*%< key to use in list & del operations */
|
|
|
|
/*% dump DB in DLZBDB bulk format */
|
|
bool list_everything = false;
|
|
unsigned int key_val; /*%< key as unsigned int used in list & del operations */
|
|
char *zone = NULL; /*%< zone to use in list operations */
|
|
char *host = NULL; /*%< host to use in list operations */
|
|
char *c_zone = NULL; /*%< client zone to use in list operations */
|
|
char *c_ip = NULL; /*%< client IP to use in list operations */
|
|
char *a_data = NULL; /*%< data in add operation */
|
|
char *bulk_file = NULL; /*%< bulk data file to load */
|
|
char *db_envdir = NULL; /*%< BDB environment location */
|
|
char *db_file = NULL; /*%< BDB database file location. */
|
|
bdb_instance_t db; /* BDB instance we are operating on */
|
|
isc_lex_t *lexer = NULL; /*%< lexer for use to use in parsing input */
|
|
isc_mem_t *lex_mctx = NULL; /*%< memory context for lexer */
|
|
char lex_data_buf[max_data_len]; /*%< data array to use for lex_buffer below */
|
|
isc_buffer_t lex_buffer; /*%< buffer for lexer during add operation */
|
|
|
|
/*%
|
|
* Displays usage message
|
|
*/
|
|
|
|
static void
|
|
show_usage(void) {
|
|
fprintf(stderr, "\n\n\
|
|
---Usage:---------------------------------------------------------------------\
|
|
\n\n\
|
|
List data:\n\
|
|
dlzbdb -l [-k key] [-z zone] [-h host] [-c client_zone] [-i client_ip]\n\
|
|
BDB_environment BDB_database\n\n\
|
|
Delete data:\n\
|
|
dlzbdb -d [-k key] [-c client_zone] [-i client_ip]\n\
|
|
BDB_environment BDB_database\n\n\
|
|
Bulk load data from file:\n\
|
|
dlzbdb -f file_to_load BDB_environment BDB_database\n\n\
|
|
Bulk load data from stdin\n\
|
|
dlzbdb -s BDB_environment BDB_database\n\n\
|
|
Add data:\n\
|
|
dlzbdb -a \"dns data to be added\" BDB_environment BDB_database\n\n\
|
|
Export data:\n\
|
|
dlzbdb -e BDB_environment BDB_database\n\n\
|
|
Normally operations can only be performed on an existing database files.\n\
|
|
Use the -n flag with any operation to allow files to be created.\n\
|
|
Existing files will NOT be truncated by using the -n flag.\n\
|
|
The -n flag will allow a new database to be created, or allow new\n\
|
|
environment files to be created for an existing database.\n\n\
|
|
---Format for -f & -a options:------------------------------------------------\
|
|
\n\n\
|
|
db_type zone host dns_type ttl ip\n\
|
|
db_type zone host dns_type ttl mx_priority mail_host\n\
|
|
db_type zone host dns_type ttl nm_svr resp_psn serial refresh retry expire min\
|
|
\n\
|
|
db_type zone client_ip\n\n\
|
|
---Examples:------------------------------------------------------------------\
|
|
\n\n\
|
|
d mynm.com www A 10 127.0.0.1\n\
|
|
d mynm.com @ MX 10 5 mail\n\
|
|
d mynm.com @ SOA 10 ns1.mynm.com. root.mynm.com. 2 28800 7200 604800 86400\n\
|
|
c mynm.com 127.0.0.1\n\
|
|
c mynm.com 192.168.0.10\n\
|
|
");
|
|
quit(1);
|
|
}
|
|
|
|
/*% BDB callback to create zone secondary index */
|
|
|
|
int
|
|
getzone(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) {
|
|
char *token, *last;
|
|
|
|
UNUSED(dbp);
|
|
UNUSED(pkey);
|
|
|
|
if ((token = strtok_r(pdata->data, " ", &last)) == NULL) {
|
|
return (BDBparseErr);
|
|
}
|
|
|
|
/* copy string for "zone" secondary index */
|
|
if ((skey->data = strdup(token)) == NULL) {
|
|
return (BDBparseErr);
|
|
}
|
|
/* set required values for BDB */
|
|
skey->size = strlen(skey->data);
|
|
skey->flags = DB_DBT_APPMALLOC;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*%
|
|
* BDB callback to create host secondary index
|
|
*/
|
|
|
|
int
|
|
gethost(DB *dbp, const DBT *pkey, const DBT *pdata, DBT *skey) {
|
|
char *token, *last;
|
|
|
|
UNUSED(dbp);
|
|
UNUSED(pkey);
|
|
|
|
/* we don't care about first token. */
|
|
if ((token = strtok_r(right, " ", &last)) == NULL) {
|
|
return (BDBparseErr);
|
|
}
|
|
|
|
/* get "host" from data string */
|
|
if ((token = strtok_r(NULL, " ", &last)) == NULL) {
|
|
return (BDBparseErr);
|
|
}
|
|
|
|
/* copy string for "host" secondary index */
|
|
if ((skey->data = strdup(token)) == NULL) {
|
|
return (BDBparseErr);
|
|
}
|
|
/* set required values for BDB */
|
|
skey->size = strlen(skey->data);
|
|
skey->flags = DB_DBT_APPMALLOC;
|
|
|
|
return (0);
|
|
}
|
|
|
|
/*%
|
|
* Performs BDB cleanup. Close each database that we opened.
|
|
* Close environment. Set each var to NULL so we know they
|
|
* were closed and don't accidentally try to close them twice.
|
|
*/
|
|
|
|
void
|
|
bdb_cleanup(void) {
|
|
/* close cursors */
|
|
if (db.cursor4 != NULL) {
|
|
db.cursor4->c_close(db.cursor4);
|
|
db.cursor4 = NULL;
|
|
}
|
|
|
|
if (db.cursor3 != NULL) {
|
|
db.cursor3->c_close(db.cursor3);
|
|
db.cursor3 = NULL;
|
|
}
|
|
|
|
if (db.cursor2 != NULL) {
|
|
db.cursor2->c_close(db.cursor2);
|
|
db.cursor2 = NULL;
|
|
}
|
|
|
|
if (db.cursor != NULL) {
|
|
db.cursor->c_close(db.cursor);
|
|
db.cursor = NULL;
|
|
}
|
|
|
|
/* close databases */
|
|
if (db.data != NULL) {
|
|
db.data->close(db.data, 0);
|
|
db.data = NULL;
|
|
}
|
|
if (db.host != NULL) {
|
|
db.host->close(db.host, 0);
|
|
db.host = NULL;
|
|
}
|
|
if (db.zone != NULL) {
|
|
db.zone->close(db.zone, 0);
|
|
db.zone = NULL;
|
|
}
|
|
if (db.client != NULL) {
|
|
db.client->close(db.client, 0);
|
|
db.client = NULL;
|
|
}
|
|
|
|
/* close environment */
|
|
if (db.dbenv != NULL) {
|
|
db.dbenv->close(db.dbenv, 0);
|
|
db.dbenv = NULL;
|
|
}
|
|
}
|
|
|
|
/*% Initializes, sets flags and then opens Berkeley databases. */
|
|
|
|
isc_result_t
|
|
bdb_opendb(DBTYPE db_type, DB **db_out, const char *db_name, int flags) {
|
|
int result;
|
|
int createFlag = 0;
|
|
|
|
/* Initialize the database. */
|
|
if ((result = db_create(db_out, db.dbenv, 0)) != 0) {
|
|
fprintf(stderr,
|
|
"BDB could not initialize %s database. BDB error: %s",
|
|
db_name, db_strerror(result));
|
|
return (ISC_R_FAILURE);
|
|
}
|
|
|
|
/* set database flags. */
|
|
if ((result = (*db_out)->set_flags(*db_out, flags)) != 0) {
|
|
fprintf(stderr,
|
|
"BDB could not set flags for %s database. BDB error: "
|
|
"%s",
|
|
db_name, db_strerror(result));
|
|
return (ISC_R_FAILURE);
|
|
}
|
|
|
|
if (create_allowed == true) {
|
|
createFlag = DB_CREATE;
|
|
}
|
|
/* open the database. */
|
|
if ((result = (*db_out)->open(*db_out, NULL, db_file, db_name, db_type,
|
|
createFlag, 0)) != 0)
|
|
{
|
|
fprintf(stderr,
|
|
"BDB could not open %s database in %s. BDB error: %s",
|
|
db_name, db_file, db_strerror(result));
|
|
return (ISC_R_FAILURE);
|
|
}
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
/*%
|
|
* parses input and adds it to the BDB database
|
|
* Lexer should be instantiated, and either a file or buffer opened for it.
|
|
* The insert_data function is used by both the add, and bulk insert
|
|
* operations
|
|
*/
|
|
|
|
void
|
|
put_data(bool dns_data, char *input_key, char *input_data) {
|
|
int bdbres;
|
|
DBT key, data;
|
|
|
|
/* make sure key & data are completely empty */
|
|
memset(&key, 0, sizeof(key));
|
|
memset(&data, 0, sizeof(data));
|
|
|
|
/* if client data, setup key for insertion */
|
|
if (!dns_data && input_key != NULL) {
|
|
key.data = input_key;
|
|
key.size = strlen(input_key);
|
|
key.flags = 0;
|
|
}
|
|
/* always setup data for insertion */
|
|
data.data = input_data;
|
|
data.size = strlen(input_data);
|
|
data.flags = 0;
|
|
|
|
/* execute insert against appropriate database. */
|
|
if (dns_data) {
|
|
bdbres = db.data->put(db.data, NULL, &key, &data, DB_APPEND);
|
|
} else {
|
|
bdbres = db.client->put(db.client, NULL, &key, &data, 0);
|
|
}
|
|
|
|
/* if something went wrong, log error and quit */
|
|
if (bdbres != 0) {
|
|
fprintf(stderr, "BDB could not insert data. Error: %s",
|
|
db_strerror(bdbres));
|
|
quit(5);
|
|
}
|
|
}
|
|
|
|
void
|
|
insert_data(void) {
|
|
unsigned int opt = ISC_LEXOPT_EOL | /* Want end-of-line token. */
|
|
ISC_LEXOPT_EOF | /* Want end-of-file token. */
|
|
ISC_LEXOPT_QSTRING | /* Recognize qstrings. */
|
|
ISC_LEXOPT_QSTRINGMULTILINE; /* Allow multiline ""
|
|
* strings */
|
|
|
|
isc_result_t result;
|
|
isc_token_t token; /* token from lexer */
|
|
bool loop = true;
|
|
bool have_czone = false;
|
|
char data_arr[max_data_len];
|
|
isc_buffer_t buf;
|
|
char data_arr2[max_data_len];
|
|
isc_buffer_t buf2;
|
|
char data_type = 'u'; /* u =unknown, b =bad token, d/D =DNS, c/C =client
|
|
* IP */
|
|
|
|
/* Initialize buffers */
|
|
isc_buffer_init(&buf, &data_arr, max_data_len);
|
|
isc_buffer_init(&buf2, &data_arr2, max_data_len);
|
|
|
|
while (loop) {
|
|
result = isc_lex_gettoken(lexer, opt, &token);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto data_cleanup;
|
|
}
|
|
|
|
switch (token.type) {
|
|
case isc_tokentype_string:
|
|
if (data_type == 'u') {
|
|
/* store data_type */
|
|
strncpy(&data_type, token.value.as_pointer, 1);
|
|
/* verify data_type was specified correctly on
|
|
* input */
|
|
if (strlen(token.value.as_pointer) > 1 ||
|
|
(data_type != 'd' && data_type != 'D' &&
|
|
data_type != 'c' && data_type != 'C'))
|
|
{
|
|
/* if not, set to 'b' so this line is
|
|
* ignored. */
|
|
data_type = 'b';
|
|
}
|
|
} else if (data_type == 'c' || data_type == 'C') {
|
|
if (have_czone == true) {
|
|
isc_buffer_putstr(
|
|
&buf2, token.value.as_pointer);
|
|
/* add string terminator to buffer */
|
|
isc_buffer_putmem(&buf2, "\0", 1);
|
|
} else {
|
|
isc_buffer_putstr(
|
|
&buf, token.value.as_pointer);
|
|
/* add string terminator to buffer */
|
|
isc_buffer_putmem(&buf, "\0", 1);
|
|
have_czone = true;
|
|
}
|
|
} else {
|
|
isc_buffer_putstr(&buf, token.value.as_pointer);
|
|
isc_buffer_putstr(&buf, " ");
|
|
}
|
|
break;
|
|
case isc_tokentype_qstring:
|
|
isc_buffer_putstr(&buf, "\"");
|
|
isc_buffer_putstr(&buf, token.value.as_pointer);
|
|
isc_buffer_putstr(&buf, "\" ");
|
|
break;
|
|
case isc_tokentype_eol:
|
|
case isc_tokentype_eof:
|
|
|
|
if ((data_type != 'u' &&
|
|
isc_buffer_usedlength(&buf) > 0) ||
|
|
data_type == 'b') {
|
|
/* perform insert operation */
|
|
if (data_type == 'd' || data_type == 'D') {
|
|
/* add string terminator to buffer */
|
|
isc_buffer_putmem(&buf, "\0", 1);
|
|
put_data(true, NULL, (char *)&data_arr);
|
|
} else if (data_type == 'c' || data_type == 'C')
|
|
{
|
|
put_data(false, (char *)&data_arr,
|
|
(char *)&data_arr2);
|
|
} else if (data_type == 'b') {
|
|
fprintf(stderr,
|
|
"Bad / unknown token "
|
|
"encountered on line %lu."
|
|
" Skipping line.",
|
|
isc_lex_getsourceline(lexer) -
|
|
1);
|
|
} else {
|
|
fprintf(stderr,
|
|
"Bad / unknown db data type "
|
|
"encountered on "
|
|
"line %lu. Skipping line\n",
|
|
isc_lex_getsourceline(lexer) -
|
|
1);
|
|
}
|
|
}
|
|
|
|
if (token.type == isc_tokentype_eof) {
|
|
loop = false;
|
|
}
|
|
|
|
/* reset buffer for next insert */
|
|
isc_buffer_clear(&buf);
|
|
isc_buffer_clear(&buf2);
|
|
have_czone = false;
|
|
data_type = 'u';
|
|
break;
|
|
default:
|
|
data_type = 'b';
|
|
break;
|
|
}
|
|
}
|
|
|
|
return;
|
|
|
|
data_cleanup:
|
|
/* let user know we had problems */
|
|
fprintf(stderr,
|
|
"Unknown error processing tokens during \"add\" or "
|
|
"\"bulk\" operation.\nStoped processing on line %lu.",
|
|
isc_lex_getsourceline(lexer));
|
|
}
|
|
|
|
isc_result_t
|
|
openBDB(void) {
|
|
int bdbres;
|
|
isc_result_t result;
|
|
|
|
/* create BDB environment */
|
|
/* Basically BDB allocates and assigns memory to db->dbenv */
|
|
bdbres = db_env_create(&db.dbenv, 0);
|
|
if (bdbres != 0) {
|
|
fprintf(stderr,
|
|
"BDB environment could not be created. BDB error: %s",
|
|
db_strerror(bdbres));
|
|
result = ISC_R_FAILURE;
|
|
goto openBDB_cleanup;
|
|
}
|
|
|
|
/* open BDB environment */
|
|
if (create_allowed == true) {
|
|
/* allowed to create new files */
|
|
bdbres = db.dbenv->open(db.dbenv, db_envdir,
|
|
DB_INIT_CDB | DB_INIT_MPOOL | DB_CREATE,
|
|
0);
|
|
} else { /* not allowed to create new files. */
|
|
bdbres = db.dbenv->open(db.dbenv, db_envdir,
|
|
DB_INIT_CDB | DB_INIT_MPOOL, 0);
|
|
}
|
|
if (bdbres != 0) {
|
|
fprintf(stderr,
|
|
"BDB environment at '%s' could not be opened. BDB "
|
|
"error: %s",
|
|
db_envdir, db_strerror(bdbres));
|
|
result = ISC_R_FAILURE;
|
|
goto openBDB_cleanup;
|
|
}
|
|
|
|
/* open dlz_data database. */
|
|
|
|
result = bdb_opendb(DB_RECNO, &db.data, dlz_data, 0);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto openBDB_cleanup;
|
|
}
|
|
|
|
/* open dlz_host database */
|
|
result = bdb_opendb(DB_BTREE, &db.host, dlz_host, DB_DUP | DB_DUPSORT);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto openBDB_cleanup;
|
|
}
|
|
|
|
/* open dlz_zone database. */
|
|
result = bdb_opendb(DB_BTREE, &db.zone, dlz_zone, DB_DUP | DB_DUPSORT);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto openBDB_cleanup;
|
|
}
|
|
|
|
/* open dlz_client database. */
|
|
result = bdb_opendb(DB_BTREE, &db.client, dlz_client,
|
|
DB_DUP | DB_DUPSORT);
|
|
if (result != ISC_R_SUCCESS) {
|
|
goto openBDB_cleanup;
|
|
}
|
|
|
|
/* associate the host secondary database with the primary database */
|
|
bdbres = db.data->associate(db.data, NULL, db.host, gethost, 0);
|
|
if (bdbres != 0) {
|
|
fprintf(stderr,
|
|
"BDB could not associate %s database with %s. BDB "
|
|
"error: %s",
|
|
dlz_host, dlz_data, db_strerror(bdbres));
|
|
result = ISC_R_FAILURE;
|
|
goto openBDB_cleanup;
|
|
}
|
|
|
|
/* associate the zone secondary database with the primary database */
|
|
bdbres = db.data->associate(db.data, NULL, db.zone, getzone, 0);
|
|
if (bdbres != 0) {
|
|
fprintf(stderr,
|
|
"BDB could not associate %s database with %s. BDB "
|
|
"error: %s",
|
|
dlz_zone, dlz_data, db_strerror(bdbres));
|
|
result = ISC_R_FAILURE;
|
|
goto openBDB_cleanup;
|
|
}
|
|
|
|
return (result);
|
|
|
|
openBDB_cleanup:
|
|
|
|
bdb_cleanup();
|
|
return (result);
|
|
}
|
|
|
|
/*% Create & open lexer to parse input data */
|
|
|
|
isc_result_t
|
|
open_lexer(void) {
|
|
isc_result_t result;
|
|
|
|
/* check if we already opened the lexer, if we did, return success */
|
|
if (lexer != NULL) {
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
/* allocate memory for lexer, and verify it was allocated */
|
|
isc_mem_create(&lex_mctx);
|
|
|
|
/* create lexer */
|
|
result = isc_lex_create(lex_mctx, 1500, &lexer);
|
|
if (result != ISC_R_SUCCESS) {
|
|
fprintf(stderr, "unexpected error creating lexer\n");
|
|
}
|
|
|
|
/* set allowed commenting style */
|
|
isc_lex_setcomments(lexer,
|
|
ISC_LEXCOMMENT_C | /* Allow C comments */
|
|
ISC_LEXCOMMENT_CPLUSPLUS | /* Allow
|
|
* C++
|
|
* comments
|
|
*/
|
|
ISC_LEXCOMMENT_SHELL); /* Allow
|
|
* shellcomments
|
|
*/
|
|
|
|
isc_buffer_init(&lex_buffer, &lex_data_buf, max_data_len);
|
|
|
|
return (result);
|
|
}
|
|
|
|
/*% Close the lexer, and cleanup memory */
|
|
|
|
void
|
|
close_lexer(void) {
|
|
/* If lexer is still open, close it & destroy it. */
|
|
if (lexer != NULL) {
|
|
isc_lex_close(lexer);
|
|
isc_lex_destroy(&lexer);
|
|
}
|
|
|
|
/* if lexer memory is still allocated, destroy it. */
|
|
if (lex_mctx != NULL) {
|
|
isc_mem_destroy(&lex_mctx);
|
|
}
|
|
}
|
|
|
|
/*% Perform add operation */
|
|
|
|
void
|
|
operation_add(void) {
|
|
/* check for any parameters that are not allowed during add */
|
|
checkInvalidParam(key, "k", "for add operation");
|
|
checkInvalidParam(zone, "z", "for add operation");
|
|
checkInvalidParam(host, "h", "for add operation");
|
|
checkInvalidParam(c_zone, "c", "for add operation");
|
|
checkInvalidParam(c_ip, "i", "for add operation");
|
|
checkInvalidOption(list_everything, true, "e", "for add operation");
|
|
|
|
/* if open lexer fails it already prints error messages. */
|
|
if (open_lexer() != ISC_R_SUCCESS) {
|
|
quit(4);
|
|
}
|
|
|
|
/* copy input data to buffer */
|
|
isc_buffer_putstr(&lex_buffer, a_data);
|
|
|
|
/* tell lexer to use buffer as input */
|
|
if (isc_lex_openbuffer(lexer, &lex_buffer) != ISC_R_SUCCESS) {
|
|
fprintf(stderr, "unexpected error opening lexer buffer");
|
|
quit(4);
|
|
}
|
|
|
|
/*common logic for "add" & "bulk" operations are handled by insert_data
|
|
*/
|
|
insert_data();
|
|
}
|
|
|
|
/*% Perform bulk insert operation */
|
|
|
|
void
|
|
operation_bulk(void) {
|
|
/* check for any parameters that are not allowed during bulk */
|
|
checkInvalidParam(key, "k", "for bulk load operation");
|
|
checkInvalidParam(zone, "z", "for bulk load operation");
|
|
checkInvalidParam(host, "h", "for bulk load operation");
|
|
checkInvalidParam(c_zone, "c", "for bulk load operation");
|
|
checkInvalidParam(c_ip, "i", "for bulk load operation");
|
|
checkInvalidOption(list_everything, true, "e",
|
|
"for bulk load operation");
|
|
|
|
/* if open lexer fails it already prints error messages. */
|
|
if (open_lexer() != ISC_R_SUCCESS) {
|
|
quit(4);
|
|
}
|
|
|
|
if (bulk_file == NULL) {
|
|
if (isc_lex_openstream(lexer, stdin) != ISC_R_SUCCESS) {
|
|
fprintf(stderr, "unexpected error opening stdin by "
|
|
"lexer.");
|
|
quit(4);
|
|
}
|
|
} else if (isc_lex_openfile(lexer, bulk_file) != ISC_R_SUCCESS) {
|
|
fprintf(stderr, "unexpected error opening %s by lexer.",
|
|
bulk_file);
|
|
quit(4);
|
|
}
|
|
|
|
/* common logic for "add" & "bulk" operations are handled by insert_data
|
|
*/
|
|
insert_data();
|
|
}
|
|
|
|
isc_result_t
|
|
bulk_write(char type, DB *database, DBC *dbcursor, DBT *bdbkey, DBT *bdbdata) {
|
|
int bdbres;
|
|
db_recno_t recNum;
|
|
char *retkey = NULL, *retdata;
|
|
size_t retklen = 0, retdlen;
|
|
void *p;
|
|
|
|
/* use a 5MB buffer for the bulk dump */
|
|
int buffer_size = 5 * 1024 * 1024;
|
|
|
|
/* try to allocate a 5 MB buffer, if we fail write err msg, die. */
|
|
bdbdata->data = malloc(buffer_size);
|
|
if (bdbdata->data == NULL) {
|
|
fprintf(stderr, "Unable to allocate 5 MB buffer for bulk "
|
|
"database dump\n");
|
|
return (ISC_R_FAILURE);
|
|
}
|
|
bdbdata->ulen = buffer_size;
|
|
bdbdata->flags = DB_DBT_USERMEM;
|
|
|
|
/* get a cursor, make sure it worked. */
|
|
bdbres = database->cursor(database, NULL, &dbcursor, 0);
|
|
if (bdbres != 0) {
|
|
fprintf(stderr, "Unexpected error. BDB Error: %s\n",
|
|
db_strerror(bdbres));
|
|
free(bdbdata->data);
|
|
return (ISC_R_FAILURE);
|
|
}
|
|
|
|
/* loop and dump all data */
|
|
for (;;) {
|
|
/* loop through data until DB_NOTFOUND is returned */
|
|
bdbres = dbcursor->c_get(dbcursor, bdbkey, bdbdata,
|
|
DB_MULTIPLE_KEY | DB_NEXT);
|
|
/* if not successful did we encounter DB_NOTFOUND, or */
|
|
/* have a different problem. */
|
|
if (bdbres != 0) {
|
|
if (bdbres != DB_NOTFOUND) {
|
|
fprintf(stderr,
|
|
"Unexpected error. BDB Error: %s\n",
|
|
db_strerror(bdbres));
|
|
free(bdbdata->data);
|
|
return (ISC_R_FAILURE);
|
|
}
|
|
/* Hit DB_NOTFOUND which means end of data. */
|
|
break;
|
|
} /* end of if (bdbres !=0) */
|
|
|
|
for (DB_MULTIPLE_INIT(p, bdbdata);;) {
|
|
if (type == 'c') {
|
|
DB_MULTIPLE_KEY_NEXT(p, bdbdata, retkey,
|
|
retklen, retdata, retdlen);
|
|
} else {
|
|
DB_MULTIPLE_RECNO_NEXT(p, bdbdata, recNum,
|
|
retdata, retdlen);
|
|
}
|
|
|
|
if (p == NULL) {
|
|
break;
|
|
}
|
|
if (type == 'c') {
|
|
printf("c %.*s %.*s\n", (int)retklen, retkey,
|
|
(int)retdlen, retdata);
|
|
} else {
|
|
printf("d %.*s\n", (int)retdlen, retdata);
|
|
}
|
|
} /* end of for (DB_MULTIPLE_INIT....) */
|
|
} /* end of for (;;) */
|
|
|
|
/* free the buffer we created earlier */
|
|
free(bdbdata->data);
|
|
|
|
return (ISC_R_SUCCESS);
|
|
}
|
|
|
|
/*%
|
|
* Perform listOrDelete operation
|
|
* if dlt == true, delete data
|
|
* else list data
|
|
*/
|
|
|
|
void
|
|
operation_listOrDelete(bool dlt) {
|
|
int bdbres = 0;
|
|
DBC *curList[3];
|
|
DBT bdbkey, bdbdata;
|
|
db_recno_t recno;
|
|
int curIndex = 0;
|
|
|
|
/* verify that only allowed parameters were passed. */
|
|
if (dlt == true) {
|
|
checkInvalidParam(zone, "z", "for delete operation");
|
|
checkInvalidParam(host, "h", "for delete operation");
|
|
checkInvalidOption(list_everything, true, "e",
|
|
"for delete operation");
|
|
checkInvalidOption(create_allowed, true, "n",
|
|
"for delete operation");
|
|
} else if (key != NULL || zone != NULL || host != NULL) {
|
|
checkInvalidParam(c_zone, "c",
|
|
"for list when k, z or h are specified");
|
|
checkInvalidParam(c_ip, "i",
|
|
"for list when k, z, or h are specified");
|
|
checkInvalidOption(list_everything, true, "e",
|
|
"for list when k, z, or h are specified");
|
|
checkInvalidOption(create_allowed, true, "n",
|
|
"for list operation");
|
|
} else if (c_ip != NULL || c_zone != NULL) {
|
|
checkInvalidOption(list_everything, true, "e",
|
|
"for list when c or i are specified");
|
|
checkInvalidOption(create_allowed, true, "n",
|
|
"for list operation");
|
|
}
|
|
|
|
memset(&bdbkey, 0, sizeof(bdbkey));
|
|
memset(&bdbdata, 0, sizeof(bdbdata));
|
|
|
|
/* Dump database in "dlzbdb" bulk format */
|
|
if (list_everything == true) {
|
|
if (bulk_write('c', db.client, db.cursor, &bdbkey, &bdbdata) !=
|
|
ISC_R_SUCCESS) {
|
|
return;
|
|
}
|
|
memset(&bdbkey, 0, sizeof(bdbkey));
|
|
memset(&bdbdata, 0, sizeof(bdbdata));
|
|
bulk_write('d', db.data, db.cursor2, &bdbkey, &bdbdata);
|
|
return;
|
|
} /* end if (list_everything) */
|
|
|
|
/* set NULL the 2nd and 3rd positions in curList. */
|
|
/* that way later when add cursors to the join list */
|
|
/* it is already null terminated. */
|
|
curList[1] = curList[2] = NULL;
|
|
|
|
if (key != NULL) {
|
|
/* make sure other parameters weren't */
|
|
checkInvalidParam(zone, "z", "when k is specified");
|
|
checkInvalidParam(host, "h", "when k is specified");
|
|
|
|
recno = key_val;
|
|
bdbkey.data = &recno;
|
|
bdbkey.size = sizeof(recno);
|
|
|
|
if (dlt == true) {
|
|
bdbres = db.data->del(db.data, NULL, &bdbkey, 0);
|
|
} else {
|
|
bdbdata.flags = DB_DBT_REALLOC;
|
|
bdbres = db.data->get(db.data, NULL, &bdbkey, &bdbdata,
|
|
0);
|
|
|
|
if (bdbres == 0) {
|
|
printf("KEY | DATA\n");
|
|
printf("%lu | %.*s\n", *(u_long *)bdbkey.data,
|
|
(int)bdbdata.size, (char *)bdbdata.data);
|
|
}
|
|
} /* closes else of if (dlt == true) */
|
|
if (bdbres == DB_NOTFOUND) {
|
|
printf("Key not found in database");
|
|
}
|
|
} /* closes if (key != NULL) */
|
|
|
|
/* if zone is passed */
|
|
if (zone != NULL) {
|
|
/* create a cursor and make sure it worked */
|
|
bdbres = db.zone->cursor(db.zone, NULL, &db.cursor2, 0);
|
|
if (bdbres != 0) {
|
|
fprintf(stderr, "Unexpected error. BDB Error: %s\n",
|
|
db_strerror(bdbres));
|
|
return;
|
|
}
|
|
|
|
bdbkey.data = zone;
|
|
bdbkey.size = strlen(zone);
|
|
bdbres = db.cursor2->c_get(db.cursor2, &bdbkey, &bdbdata,
|
|
DB_SET);
|
|
if (bdbres != 0) {
|
|
if (bdbres != DB_NOTFOUND) {
|
|
fprintf(stderr,
|
|
"Unexpected error. BDB Error: %s\n",
|
|
db_strerror(bdbres));
|
|
} else {
|
|
printf("Zone not found in database");
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* add cursor to cursor list for later use in join */
|
|
curList[curIndex++] = db.cursor2;
|
|
}
|
|
|
|
/* if host is passed */
|
|
if (host != NULL) {
|
|
/* create a cursor and make sure it worked. */
|
|
bdbres = db.host->cursor(db.host, NULL, &db.cursor3, 0);
|
|
if (bdbres != 0) {
|
|
fprintf(stderr, "Unexpected error. BDB Error: %s\n",
|
|
db_strerror(bdbres));
|
|
return;
|
|
}
|
|
bdbkey.data = host;
|
|
bdbkey.size = strlen(host);
|
|
bdbres = db.cursor3->c_get(db.cursor3, &bdbkey, &bdbdata,
|
|
DB_SET);
|
|
if (bdbres != 0) {
|
|
if (bdbres != DB_NOTFOUND) {
|
|
fprintf(stderr,
|
|
"Unexpected error. BDB Error: %s\n",
|
|
db_strerror(bdbres));
|
|
} else {
|
|
printf("Host not found in database");
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* add cursor to cursor list for later use in join */
|
|
curList[curIndex++] = db.cursor3;
|
|
}
|
|
|
|
if (zone != NULL || host != NULL) {
|
|
/* join any cursors */
|
|
bdbres = db.data->join(db.data, curList, &db.cursor4, 0);
|
|
if (bdbres != 0) {
|
|
fprintf(stderr, "Unexpected error. BDB Error: %s\n",
|
|
db_strerror(bdbres));
|
|
return;
|
|
}
|
|
|
|
memset(&bdbkey, 0, sizeof(bdbkey));
|
|
bdbkey.flags = DB_DBT_REALLOC;
|
|
memset(&bdbdata, 0, sizeof(bdbdata));
|
|
bdbdata.flags = DB_DBT_REALLOC;
|
|
|
|
/* print a header to explain the output */
|
|
printf("KEY | DATA\n");
|
|
/* loop and list all results. */
|
|
while (bdbres == 0) {
|
|
/* get data */
|
|
bdbres = db.cursor4->c_get(db.cursor4, &bdbkey,
|
|
&bdbdata, 0);
|
|
/* verify call had no errors */
|
|
if (bdbres != 0) {
|
|
break;
|
|
}
|
|
printf("%lu | %.*s\n", *(u_long *)bdbkey.data,
|
|
(int)bdbdata.size, (char *)bdbdata.data);
|
|
} /* closes while loop */
|
|
}
|
|
|
|
if (c_ip != NULL && c_zone == NULL) {
|
|
fprintf(stderr, "i may only be specified when c is also "
|
|
"specified\n");
|
|
quit(2);
|
|
}
|
|
/* if client_zone was passed */
|
|
if (c_zone != NULL) {
|
|
/* create a cursor and make sure it worked. */
|
|
if (dlt == true) {
|
|
/* open read-write cursor */
|
|
bdbres = db.client->cursor(db.client, NULL, &db.cursor,
|
|
DB_WRITECURSOR);
|
|
} else {
|
|
/* open read only cursor */
|
|
bdbres = db.client->cursor(db.client, NULL, &db.cursor,
|
|
0);
|
|
/* print a header to explain the output */
|
|
printf("CLIENT_ZONE | CLIENT_IP\n");
|
|
}
|
|
|
|
bdbkey.data = c_zone;
|
|
bdbkey.size = strlen(c_zone);
|
|
|
|
if (c_ip != NULL) {
|
|
bdbdata.data = c_ip;
|
|
bdbdata.size = strlen(c_ip);
|
|
bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata,
|
|
DB_GET_BOTH);
|
|
if (bdbres == DB_NOTFOUND) {
|
|
printf("Client zone & IP not found in "
|
|
"database");
|
|
}
|
|
} else {
|
|
bdbdata.flags = DB_DBT_REALLOC;
|
|
bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata,
|
|
DB_SET);
|
|
if (bdbres == DB_NOTFOUND) {
|
|
printf("Client zone not found in database");
|
|
}
|
|
}
|
|
|
|
while (bdbres == 0) {
|
|
if (dlt == false) {
|
|
printf("%.*s | %.*s\n", (int)bdbkey.size,
|
|
(char *)bdbkey.data, (int)bdbdata.size,
|
|
(char *)bdbdata.data);
|
|
} else {
|
|
/* delete record. */
|
|
bdbres = db.cursor->c_del(db.cursor, 0);
|
|
if (bdbres != 0) {
|
|
fprintf(stderr,
|
|
"Unexpected error. BDB Error: "
|
|
"%s\n",
|
|
db_strerror(bdbres));
|
|
break;
|
|
}
|
|
}
|
|
if (c_ip != NULL) {
|
|
break;
|
|
}
|
|
bdbres = db.cursor->c_get(db.cursor, &bdbkey, &bdbdata,
|
|
DB_NEXT_DUP);
|
|
if (bdbres != 0) {
|
|
break;
|
|
}
|
|
} /* end while loop */
|
|
}
|
|
|
|
if (bdbres != 0 && bdbres != DB_NOTFOUND) {
|
|
fprintf(stderr,
|
|
"Unexpected error during list operation "
|
|
"BDB error: %s",
|
|
db_strerror(bdbres));
|
|
}
|
|
|
|
if (bdbkey.flags == DB_DBT_REALLOC && bdbkey.data != NULL) {
|
|
free(bdbkey.data);
|
|
}
|
|
if (bdbdata.flags == DB_DBT_REALLOC && bdbdata.data != NULL) {
|
|
free(bdbdata.data);
|
|
}
|
|
}
|
|
|
|
int
|
|
main(int argc, char **argv) {
|
|
int ch;
|
|
char *endp;
|
|
|
|
/* there has to be at least 2 args, some operations require more */
|
|
if (argc < 2) {
|
|
show_usage();
|
|
}
|
|
|
|
/* use the ISC commandline parser to get all the program arguments */
|
|
while ((ch = isc_commandline_parse(argc, argv,
|
|
"ldesna:f:k:z:h:c:i:")) != -1) {
|
|
switch (ch) {
|
|
case 'n':
|
|
create_allowed = true;
|
|
break;
|
|
case 'l':
|
|
checkOp(operation);
|
|
operation = list;
|
|
break;
|
|
case 'd':
|
|
checkOp(operation);
|
|
operation = dele;
|
|
break;
|
|
case 'a':
|
|
checkOp(operation);
|
|
operation = add;
|
|
a_data = isc_commandline_argument;
|
|
break;
|
|
case 'f':
|
|
checkOp(operation);
|
|
operation = bulk;
|
|
bulk_file = isc_commandline_argument;
|
|
break;
|
|
case 's':
|
|
checkOp(operation);
|
|
operation = bulk;
|
|
break;
|
|
case 'k':
|
|
checkParam(key, "k");
|
|
key = isc_commandline_argument;
|
|
key_val = strtoul(key, &endp, 10);
|
|
if (*endp != '\0' || key_val < 1) {
|
|
fprintf(stderr, "Error converting key to "
|
|
"integer");
|
|
}
|
|
break;
|
|
case 'z':
|
|
checkParam(zone, "z");
|
|
zone = isc_commandline_argument;
|
|
break;
|
|
case 'h':
|
|
checkParam(host, "h");
|
|
host = isc_commandline_argument;
|
|
break;
|
|
case 'c':
|
|
checkParam(c_zone, "c");
|
|
c_zone = isc_commandline_argument;
|
|
break;
|
|
case 'i':
|
|
checkParam(c_ip, "i");
|
|
c_ip = isc_commandline_argument;
|
|
break;
|
|
case 'e':
|
|
checkOp(operation);
|
|
operation = list;
|
|
list_everything = true;
|
|
break;
|
|
case '?':
|
|
show_usage();
|
|
break;
|
|
default:
|
|
/* should never reach this point */
|
|
fprintf(stderr, "unexpected error parsing command "
|
|
"arguments\n");
|
|
quit(1);
|
|
break;
|
|
}
|
|
}
|
|
|
|
argc -= isc_commandline_index;
|
|
argv += isc_commandline_index;
|
|
|
|
/* argc & argv have been modified, so now only "extra" parameters are */
|
|
/* left in argc & argv. "Extra" parameters are any parameters that were
|
|
*/
|
|
/* not passed using a command line flag. Exactly 2 args should be left.
|
|
*/
|
|
/* The first should be the BDB environment path, the second should be
|
|
* the */
|
|
/* BDB database. The BDB database path can be either relative to the */
|
|
/* BDB environment path, or absolute. */
|
|
if (argc < 2) {
|
|
fprintf(stderr, "Both a Berkeley DB environment and file "
|
|
"must be specified");
|
|
quit(2);
|
|
} else if (argc > 2) {
|
|
fprintf(stderr, "Too many parameters. Check command line for "
|
|
"errors.");
|
|
quit(2);
|
|
}
|
|
|
|
/* get db_file to operate on */
|
|
db_envdir = argv[0];
|
|
db_file = argv[1];
|
|
|
|
if (openBDB() != ISC_R_SUCCESS) {
|
|
/* openBDB already prints error messages, don't do it here. */
|
|
bdb_cleanup();
|
|
quit(3);
|
|
}
|
|
|
|
switch (operation) {
|
|
case list:
|
|
operation_listOrDelete(false);
|
|
break;
|
|
case dele:
|
|
operation_listOrDelete(true);
|
|
break;
|
|
case add:
|
|
operation_add();
|
|
break;
|
|
case bulk:
|
|
operation_bulk();
|
|
break;
|
|
default:
|
|
fprintf(stderr, "\nNo operation was selected. "
|
|
"Select an operation (l d a f)");
|
|
quit(2);
|
|
break;
|
|
}
|
|
|
|
quit(0);
|
|
}
|
|
#endif /* ifdef DLZ_BDB */
|