This program is free software; you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
+ the Free Software Foundation; either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
+ along with this program. If not, see <http://www.gnu.org/licenses/>.
*/
int ClientDGRAM = -1;
int global_nmb_port = -1;
-extern BOOL global_in_nmbd;
+extern bool rescan_listen_set;
+extern bool global_in_nmbd;
-/* are we running as a daemon ? */
-static BOOL is_daemon;
-
-/* fork or run in foreground ? */
-static BOOL Fork = True;
-
-/* log to standard output ? */
-static BOOL log_stdout;
+extern bool override_logfile;
/* have we found LanMan clients yet? */
-BOOL found_lm_clients = False;
+bool found_lm_clients = False;
/* what server type are we currently */
time_t StartupTime = 0;
+struct event_context *nmbd_event_context(void)
+{
+ static struct event_context *ctx;
+
+ if (!ctx && !(ctx = event_context_init(NULL))) {
+ smb_panic("Could not init nmbd event context");
+ }
+ return ctx;
+}
+
+struct messaging_context *nmbd_messaging_context(void)
+{
+ static struct messaging_context *ctx;
+
+ if (ctx == NULL) {
+ ctx = messaging_init(NULL, server_id_self(),
+ nmbd_event_context());
+ }
+ if (ctx == NULL) {
+ DEBUG(0, ("Could not init nmbd messaging context.\n"));
+ }
+ return ctx;
+}
+
/**************************************************************************** **
Handle a SIGTERM in band.
**************************************************************************** */
DEBUG(0,("Got SIGTERM: going down...\n"));
/* Write out wins.dat file if samba is a WINS server */
- wins_write_database(False);
+ wins_write_database(0,False);
/* Remove all SELF registered names from WINS */
release_wins_names();
Handle a SHUTDOWN message from smbcontrol.
**************************************************************************** */
-static void nmbd_terminate(int msg_type, pid_t src, void *buf, size_t len)
+static void nmbd_terminate(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
{
terminate();
}
static void sig_term(int sig)
{
got_sig_term = 1;
- sys_select_signal();
+ sys_select_signal(SIGTERM);
}
/**************************************************************************** **
static void sig_hup(int sig)
{
reload_after_sighup = 1;
- sys_select_signal();
-}
-
-#if DUMP_CORE
-/**************************************************************************** **
- Prepare to dump a core file - carefully!
- **************************************************************************** */
-
-static BOOL dump_core(void)
-{
- char *p;
- pstring dname;
- pstrcpy( dname, lp_logfile() );
- if ((p=strrchr_m(dname,'/')))
- *p=0;
- pstrcat( dname, "/corefiles" );
- mkdir( dname, 0700 );
- sys_chown( dname, getuid(), getgid() );
- chmod( dname, 0700 );
- if ( chdir(dname) )
- return( False );
- umask( ~(0700) );
-
-#ifdef HAVE_GETRLIMIT
-#ifdef RLIMIT_CORE
- {
- struct rlimit rlp;
- getrlimit( RLIMIT_CORE, &rlp );
- rlp.rlim_cur = MAX( 4*1024*1024, rlp.rlim_cur );
- setrlimit( RLIMIT_CORE, &rlp );
- getrlimit( RLIMIT_CORE, &rlp );
- DEBUG( 3, ( "Core limits now %d %d\n", (int)rlp.rlim_cur, (int)rlp.rlim_max ) );
- }
-#endif
-#endif
-
-
- DEBUG(0,("Dumping core in %s\n",dname));
- abort();
- return( True );
+ sys_select_signal(SIGHUP);
}
-#endif
/**************************************************************************** **
Possibly continue after a fault.
static void fault_continue(void)
{
-#if DUMP_CORE
dump_core();
-#endif
}
/**************************************************************************** **
/************************************************************************** **
Reload the list of network interfaces.
+ Doesn't return until a network interface is up.
************************************************************************** */
-static BOOL reload_interfaces(time_t t)
+static void reload_interfaces(time_t t)
{
static time_t lastt;
int n;
+ bool print_waiting_msg = true;
struct subnet_record *subrec;
- extern BOOL rescan_listen_set;
- extern struct in_addr loopback_ip;
- if (t && ((t - lastt) < NMBD_INTERFACES_RELOAD)) return False;
+ if (t && ((t - lastt) < NMBD_INTERFACES_RELOAD)) {
+ return;
+ }
+
lastt = t;
- if (!interfaces_changed()) return False;
+ if (!interfaces_changed()) {
+ return;
+ }
+
+ try_again:
/* the list of probed interfaces has changed, we may need to add/remove
some subnets */
/* find any interfaces that need adding */
for (n=iface_count() - 1; n >= 0; n--) {
- struct interface *iface = get_interface(n);
+ char str[INET6_ADDRSTRLEN];
+ const struct interface *iface = get_interface(n);
+ struct in_addr ip, nmask;
+
+ if (!iface) {
+ DEBUG(2,("reload_interfaces: failed to get interface %d\n", n));
+ continue;
+ }
+
+ /* Ensure we're only dealing with IPv4 here. */
+ if (iface->ip.ss_family != AF_INET) {
+ DEBUG(2,("reload_interfaces: "
+ "ignoring non IPv4 interface.\n"));
+ continue;
+ }
+
+ ip = ((struct sockaddr_in *)&iface->ip)->sin_addr;
+ nmask = ((struct sockaddr_in *)&iface->netmask)->sin_addr;
/*
* We don't want to add a loopback interface, in case
* ignore it here. JRA.
*/
- if (ip_equal(iface->ip, loopback_ip)) {
- DEBUG(2,("reload_interfaces: Ignoring loopback interface %s\n", inet_ntoa(iface->ip)));
+ if (is_loopback_addr((struct sockaddr *)&iface->ip)) {
+ DEBUG(2,("reload_interfaces: Ignoring loopback "
+ "interface %s\n",
+ print_sockaddr(str, sizeof(str), &iface->ip) ));
continue;
}
for (subrec=subnetlist; subrec; subrec=subrec->next) {
- if (ip_equal(iface->ip, subrec->myip) &&
- ip_equal(iface->nmask, subrec->mask_ip)) break;
+ if (ip_equal_v4(ip, subrec->myip) &&
+ ip_equal_v4(nmask, subrec->mask_ip)) {
+ break;
+ }
}
if (!subrec) {
/* it wasn't found! add it */
- DEBUG(2,("Found new interface %s\n",
- inet_ntoa(iface->ip)));
+ DEBUG(2,("Found new interface %s\n",
+ print_sockaddr(str,
+ sizeof(str), &iface->ip) ));
subrec = make_normal_subnet(iface);
if (subrec)
register_my_workgroup_one_subnet(subrec);
for (subrec=subnetlist; subrec; subrec=subrec->next) {
for (n=iface_count() - 1; n >= 0; n--) {
struct interface *iface = get_interface(n);
- if (ip_equal(iface->ip, subrec->myip) &&
- ip_equal(iface->nmask, subrec->mask_ip)) break;
+ struct in_addr ip, nmask;
+ if (!iface) {
+ continue;
+ }
+ /* Ensure we're only dealing with IPv4 here. */
+ if (iface->ip.ss_family != AF_INET) {
+ DEBUG(2,("reload_interfaces: "
+ "ignoring non IPv4 interface.\n"));
+ continue;
+ }
+ ip = ((struct sockaddr_in *)&iface->ip)->sin_addr;
+ nmask = ((struct sockaddr_in *)&iface->netmask)->sin_addr;
+ if (ip_equal_v4(ip, subrec->myip) &&
+ ip_equal_v4(nmask, subrec->mask_ip)) {
+ break;
+ }
}
if (n == -1) {
/* oops, an interface has disapeared. This is
instead we just wear the memory leak and
remove it from the list of interfaces without
freeing it */
- DEBUG(2,("Deleting dead interface %s\n",
+ DEBUG(2,("Deleting dead interface %s\n",
inet_ntoa(subrec->myip)));
close_subnet(subrec);
}
}
-
+
rescan_listen_set = True;
- /* We need to shutdown if there are no subnets... */
+ /* We need to wait if there are no subnets... */
if (FIRST_SUBNET == NULL) {
- DEBUG(0,("reload_interfaces: No subnets to listen to. Shutting down...\n"));
- return True;
+ void (*saved_handler)(int);
+
+ if (print_waiting_msg) {
+ DEBUG(0,("reload_interfaces: "
+ "No subnets to listen to. Waiting..\n"));
+ print_waiting_msg = false;
+ }
+
+ /*
+ * Whilst we're waiting for an interface, allow SIGTERM to
+ * cause us to exit.
+ */
+ saved_handler = CatchSignal( SIGTERM, SIGNAL_CAST SIG_DFL );
+
+ /* We only count IPv4, non-loopback interfaces here. */
+ while (iface_count_v4_nl() == 0) {
+ sleep(5);
+ load_interfaces();
+ }
+
+ CatchSignal( SIGTERM, SIGNAL_CAST saved_handler );
+
+ /*
+ * We got an interface, go back to blocking term.
+ */
+
+ goto try_again;
}
- return False;
}
/**************************************************************************** **
Reload the services file.
**************************************************************************** */
-static BOOL reload_nmbd_services(BOOL test)
+static bool reload_nmbd_services(bool test)
{
- BOOL ret;
+ bool ret;
set_remote_machine_name("nmbd", False);
if ( lp_loaded() ) {
- pstring fname;
- pstrcpy( fname,lp_configfile());
- if (file_exist(fname,NULL) && !strcsequal(fname,dyn_CONFIGFILE)) {
- pstrcpy(dyn_CONFIGFILE,fname);
+ const char *fname = lp_configfile();
+ if (file_exist(fname) && !strcsequal(fname,get_dyn_CONFIGFILE())) {
+ set_dyn_CONFIGFILE(fname);
test = False;
}
}
if ( test && !lp_file_list_changed() )
return(True);
- ret = lp_load( dyn_CONFIGFILE, True , False, False);
+ ret = lp_load(get_dyn_CONFIGFILE(), True , False, False, True);
/* perhaps the config filename is now set */
if ( !test ) {
/**************************************************************************** **
* React on 'smbcontrol nmbd reload-config' in the same way as to SIGHUP
- * We use buf here to return BOOL result to process() when reload_interfaces()
- * detects that there are no subnets.
**************************************************************************** */
-static void msg_reload_nmbd_services(int msg_type, pid_t src, void *buf, size_t len)
+static void msg_reload_nmbd_services(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id server_id,
+ DATA_BLOB *data)
{
write_browse_list( 0, True );
dump_all_namelists();
reload_nmbd_services( True );
reopen_logs();
-
- if(buf) {
- /* We were called from process() */
- /* If reload_interfaces() returned True */
- /* we need to shutdown if there are no subnets... */
- /* pass this info back to process() */
- *((BOOL*)buf) = reload_interfaces(0);
- }
+ reload_interfaces(0);
}
+static void msg_nmbd_send_packet(struct messaging_context *msg,
+ void *private_data,
+ uint32_t msg_type,
+ struct server_id src,
+ DATA_BLOB *data)
+{
+ struct packet_struct *p = (struct packet_struct *)data->data;
+ struct subnet_record *subrec;
+ struct sockaddr_storage ss;
+ const struct sockaddr_storage *pss;
+ const struct in_addr *local_ip;
+
+ DEBUG(10, ("Received send_packet from %d\n", procid_to_pid(&src)));
+
+ if (data->length != sizeof(struct packet_struct)) {
+ DEBUG(2, ("Discarding invalid packet length from %d\n",
+ procid_to_pid(&src)));
+ return;
+ }
+
+ if ((p->packet_type != NMB_PACKET) &&
+ (p->packet_type != DGRAM_PACKET)) {
+ DEBUG(2, ("Discarding invalid packet type from %d: %d\n",
+ procid_to_pid(&src), p->packet_type));
+ return;
+ }
+
+ in_addr_to_sockaddr_storage(&ss, p->ip);
+ pss = iface_ip((struct sockaddr *)&ss);
+
+ if (pss == NULL) {
+ DEBUG(2, ("Could not find ip for packet from %d\n",
+ procid_to_pid(&src)));
+ return;
+ }
+
+ local_ip = &((const struct sockaddr_in *)pss)->sin_addr;
+ subrec = FIRST_SUBNET;
+
+ p->fd = (p->packet_type == NMB_PACKET) ?
+ subrec->nmb_sock : subrec->dgram_sock;
+
+ for (subrec = FIRST_SUBNET; subrec != NULL;
+ subrec = NEXT_SUBNET_EXCLUDING_UNICAST(subrec)) {
+ if (ip_equal_v4(*local_ip, subrec->myip)) {
+ p->fd = (p->packet_type == NMB_PACKET) ?
+ subrec->nmb_sock : subrec->dgram_sock;
+ break;
+ }
+ }
+
+ if (p->packet_type == DGRAM_PACKET) {
+ p->port = 138;
+ p->packet.dgram.header.source_ip.s_addr = local_ip->s_addr;
+ p->packet.dgram.header.source_port = 138;
+ }
+
+ send_packet(p);
+}
/**************************************************************************** **
The main select loop.
static void process(void)
{
- BOOL run_election;
- BOOL no_subnets;
+ bool run_election;
while( True ) {
time_t t = time(NULL);
-
- /* Check for internal messages */
-
- message_dispatch();
+ TALLOC_CTX *frame = talloc_stackframe();
/*
* Check all broadcast subnets to see if
* (nmbd_packets.c)
*/
- if(listen_for_packets(run_election))
+ if(listen_for_packets(run_election)) {
+ TALLOC_FREE(frame);
return;
+ }
/*
* Handle termination inband.
if(reload_after_sighup) {
DEBUG( 0, ( "Got SIGHUP dumping debug info.\n" ) );
- msg_reload_nmbd_services(MSG_SMB_CONF_UPDATED, (pid_t) 0, (void*) &no_subnets, 0);
- if(no_subnets)
- return;
+ msg_reload_nmbd_services(nmbd_messaging_context(),
+ NULL, MSG_SMB_CONF_UPDATED,
+ procid_self(), NULL);
+
reload_after_sighup = 0;
}
/* check for new network interfaces */
- if(reload_interfaces(t))
- return;
+ reload_interfaces(t);
/* free up temp memory */
- lp_talloc_free();
+ TALLOC_FREE(frame);
}
}
Open the socket communication.
**************************************************************************** */
-static BOOL open_sockets(BOOL isdaemon, int port)
+static bool open_sockets(bool isdaemon, int port)
{
+ struct sockaddr_storage ss;
+ const char *sock_addr = lp_socket_address();
+
/*
* The sockets opened here will be used to receive broadcast
* packets *only*. Interface specific sockets are opened in
* now deprecated.
*/
- if ( isdaemon )
+ if (!interpret_string_addr(&ss, sock_addr,
+ AI_NUMERICHOST|AI_PASSIVE)) {
+ DEBUG(0,("open_sockets: unable to get socket address "
+ "from string %s", sock_addr));
+ return false;
+ }
+ if (ss.ss_family != AF_INET) {
+ DEBUG(0,("open_sockets: unable to use IPv6 socket"
+ "%s in nmbd\n",
+ sock_addr));
+ return false;
+ }
+
+ if (isdaemon) {
ClientNMB = open_socket_in(SOCK_DGRAM, port,
- interpret_addr(lp_socket_address()),
- 0,True);
- else
+ 0, &ss,
+ true);
+ } else {
ClientNMB = 0;
-
- ClientDGRAM = open_socket_in(SOCK_DGRAM,DGRAM_PORT,3,0,True);
+ }
+
+ if (ClientNMB == -1) {
+ return false;
+ }
+
+ ClientDGRAM = open_socket_in(SOCK_DGRAM, DGRAM_PORT,
+ 3, &ss,
+ true);
- if ( ClientNMB == -1 )
- return( False );
+ if (ClientDGRAM == -1) {
+ if (ClientNMB != 0) {
+ close(ClientNMB);
+ }
+ return false;
+ }
/* we are never interested in SIGPIPE */
BlockSignals(True,SIGPIPE);
set_socket_options( ClientNMB, "SO_BROADCAST" );
set_socket_options( ClientDGRAM, "SO_BROADCAST" );
+ /* Ensure we're non-blocking. */
+ set_blocking( ClientNMB, False);
+ set_blocking( ClientDGRAM, False);
+
DEBUG( 3, ( "open_sockets: Broadcast sockets opened.\n" ) );
return( True );
}
/**************************************************************************** **
main program
**************************************************************************** */
+
int main(int argc, const char *argv[])
{
- pstring logfile;
- static BOOL opt_interactive;
+ static bool is_daemon;
+ static bool opt_interactive;
+ static bool Fork = true;
+ static bool no_process_group;
+ static bool log_stdout;
poptContext pc;
+ char *p_lmhosts = NULL;
int opt;
+ enum {
+ OPT_DAEMON = 1000,
+ OPT_INTERACTIVE,
+ OPT_FORK,
+ OPT_NO_PROCESS_GROUP,
+ OPT_LOG_STDOUT
+ };
struct poptOption long_options[] = {
POPT_AUTOHELP
- {"daemon", 'D', POPT_ARG_VAL, &is_daemon, True, "Become a daemon(default)" },
- {"interactive", 'i', POPT_ARG_VAL, &opt_interactive, True, "Run interactive (not a daemon)" },
- {"foreground", 'F', POPT_ARG_VAL, &Fork, False, "Run daemon in foreground (for daemontools & etc)" },
- {"log-stdout", 'S', POPT_ARG_VAL, &log_stdout, True, "Log to stdout" },
- {"hosts", 'H', POPT_ARG_STRING, dyn_LMHOSTSFILE, 'H', "Load a netbios hosts file"},
+ {"daemon", 'D', POPT_ARG_NONE, NULL, OPT_DAEMON, "Become a daemon(default)" },
+ {"interactive", 'i', POPT_ARG_NONE, NULL, OPT_INTERACTIVE, "Run interactive (not a daemon)" },
+ {"foreground", 'F', POPT_ARG_NONE, NULL, OPT_FORK, "Run daemon in foreground (for daemontools & etc)" },
+ {"no-process-group", 0, POPT_ARG_NONE, NULL, OPT_NO_PROCESS_GROUP, "Don't create a new process group" },
+ {"log-stdout", 'S', POPT_ARG_NONE, NULL, OPT_LOG_STDOUT, "Log to stdout" },
+ {"hosts", 'H', POPT_ARG_STRING, &p_lmhosts, 'H', "Load a netbios hosts file"},
{"port", 'p', POPT_ARG_INT, &global_nmb_port, NMB_PORT, "Listen on the specified port" },
POPT_COMMON_SAMBA
{ NULL }
};
+ TALLOC_CTX *frame = talloc_stackframe(); /* Setup tos. */
+
+ load_case_tables();
global_nmb_port = NMB_PORT;
pc = poptGetContext("nmbd", argc, argv, long_options, 0);
- while ((opt = poptGetNextOpt(pc)) != -1) ;
+ while ((opt = poptGetNextOpt(pc)) != -1) {
+ switch (opt) {
+ case OPT_DAEMON:
+ is_daemon = true;
+ break;
+ case OPT_INTERACTIVE:
+ opt_interactive = true;
+ break;
+ case OPT_FORK:
+ Fork = false;
+ break;
+ case OPT_NO_PROCESS_GROUP:
+ no_process_group = true;
+ break;
+ case OPT_LOG_STDOUT:
+ log_stdout = true;
+ break;
+ default:
+ d_fprintf(stderr, "\nInvalid option %s: %s\n\n",
+ poptBadOption(pc, 0), poptStrerror(opt));
+ poptPrintUsage(pc, stderr, 0);
+ exit(1);
+ }
+ };
poptFreeContext(pc);
- global_in_nmbd = True;
+ global_in_nmbd = true;
StartupTime = time(NULL);
sys_srandom(time(NULL) ^ sys_getpid());
- slprintf(logfile, sizeof(logfile)-1, "%s/log.nmbd", dyn_LOGFILEBASE);
- lp_set_logfile(logfile);
+ if (!override_logfile) {
+ char *lfile = NULL;
+ if (asprintf(&lfile, "%s/log.nmbd", get_dyn_LOGFILEBASE()) < 0) {
+ exit(1);
+ }
+ lp_set_logfile(lfile);
+ SAFE_FREE(lfile);
+ }
fault_setup((void (*)(void *))fault_continue );
+ dump_core_setup("nmbd");
/* POSIX demands that signals are inherited. If the invoking process has
* these signals masked, we will have problems, as we won't receive them. */
reopen_logs();
- DEBUG( 0, ( "Netbios nameserver version %s started.\n", SAMBA_VERSION_STRING) );
- DEBUGADD( 0, ( "Copyright Andrew Tridgell and the Samba Team 1994-2003\n" ) );
+ DEBUG(0,("nmbd version %s started.\n", samba_version_string()));
+ DEBUGADD(0,("%s\n", COPYRIGHT_STARTUP_MESSAGE));
+
+ if (!lp_load_initial_only(get_dyn_CONFIGFILE())) {
+ DEBUG(0, ("error opening config file\n"));
+ exit(1);
+ }
+
+ if (nmbd_messaging_context() == NULL) {
+ return 1;
+ }
if ( !reload_nmbd_services(False) )
return(-1);
if (is_daemon && !opt_interactive) {
DEBUG( 2, ( "Becoming a daemon.\n" ) );
- become_daemon(Fork);
+ become_daemon(Fork, no_process_group);
}
#if HAVE_SETPGID
* If we're interactive we want to set our own process group for
* signal management.
*/
- if (opt_interactive)
+ if (opt_interactive && !no_process_group)
setpgid( (pid_t)0, (pid_t)0 );
#endif
+ if (nmbd_messaging_context() == NULL) {
+ return 1;
+ }
+
#ifndef SYNC_DNS
/* Setup the async dns. We do it here so it doesn't have all the other
stuff initialised and thus chewing memory and sockets */
}
#endif
- if (!directory_exist(lp_lockdir(), NULL)) {
+ if (!directory_exist(lp_lockdir())) {
mkdir(lp_lockdir(), 0755);
}
pidfile_create("nmbd");
- message_init();
- message_register(MSG_FORCE_ELECTION, nmbd_message_election);
- message_register(MSG_WINS_NEW_ENTRY, nmbd_wins_new_entry);
- message_register(MSG_SHUTDOWN, nmbd_terminate);
- message_register(MSG_SMB_CONF_UPDATED, msg_reload_nmbd_services);
+
+ if (!reinit_after_fork(nmbd_messaging_context(),
+ nmbd_event_context(), false)) {
+ DEBUG(0,("reinit_after_fork() failed\n"));
+ exit(1);
+ }
+
+ /* get broadcast messages */
+ claim_connection(NULL,"",FLAG_MSG_GENERAL|FLAG_MSG_DBWRAP);
+
+ messaging_register(nmbd_messaging_context(), NULL,
+ MSG_FORCE_ELECTION, nmbd_message_election);
+#if 0
+ /* Until winsrepl is done. */
+ messaging_register(nmbd_messaging_context(), NULL,
+ MSG_WINS_NEW_ENTRY, nmbd_wins_new_entry);
+#endif
+ messaging_register(nmbd_messaging_context(), NULL,
+ MSG_SHUTDOWN, nmbd_terminate);
+ messaging_register(nmbd_messaging_context(), NULL,
+ MSG_SMB_CONF_UPDATED, msg_reload_nmbd_services);
+ messaging_register(nmbd_messaging_context(), NULL,
+ MSG_SEND_PACKET, msg_nmbd_send_packet);
+
+ TimeInit();
DEBUG( 3, ( "Opening sockets %d\n", global_nmb_port ) );
}
/* Load in any static local names. */
- load_lmhosts_file(dyn_LMHOSTSFILE);
- DEBUG(3,("Loaded hosts file %s\n", dyn_LMHOSTSFILE));
+ if (p_lmhosts) {
+ set_dyn_LMHOSTSFILE(p_lmhosts);
+ }
+ load_lmhosts_file(get_dyn_LMHOSTSFILE());
+ DEBUG(3,("Loaded hosts file %s\n", get_dyn_LMHOSTSFILE()));
/* If we are acting as a WINS server, initialise data structures. */
if( !initialise_wins() ) {
exit(1);
}
- /* We can only take signals in the select. */
- BlockSignals( True, SIGTERM );
-
+ TALLOC_FREE(frame);
process();
if (dbf)