finished the asynchronous browse synchronisation code. It even seems
authorAndrew Tridgell <tridge@samba.org>
Sun, 30 Aug 1998 15:58:17 +0000 (15:58 +0000)
committerAndrew Tridgell <tridge@samba.org>
Sun, 30 Aug 1998 15:58:17 +0000 (15:58 +0000)
to work (not a lot of testing yet though).

Now we just need to deal with people worried about having more than
two nmbd processes sometimes. (the async processes are created on
demand for browse sync, so you'll only see more than 2 occasionally)
(This used to be commit a350a54680e4170e2adf571b010ea508e7291780)

source3/Makefile.in
source3/include/proto.h
source3/nmbd/nmbd.c
source3/nmbd/nmbd_browsesync.c
source3/nmbd/nmbd_synclists.c [new file with mode: 0644]

index cee3baef7a99e25c0b2986d3de936474dfffd0e7..1bf3c2bbdc91976fddd0225e3865790be4becae6 100644 (file)
@@ -147,7 +147,7 @@ NMBD_OBJ1 = nmbd/asyncdns.o nmbd/nmbd.o nmbd/nmbd_become_dmb.o \
             nmbd/nmbd_processlogon.o nmbd/nmbd_responserecordsdb.o \
             nmbd/nmbd_sendannounce.o nmbd/nmbd_serverlistdb.o \
             nmbd/nmbd_subnetdb.o nmbd/nmbd_winsproxy.o nmbd/nmbd_winsserver.o \
-            nmbd/nmbd_workgroupdb.o
+            nmbd/nmbd_workgroupdb.o nmbd/nmbd_synclists.o
 
 NMBD_OBJ = $(NMBD_OBJ1) $(PARAM_OBJ) $(LIBSMB_OBJ) $(UBIQX_OBJ) \
            $(PASSDB_OBJ) $(LIB_OBJ)
index 56e7d7f88f0fd57b140bdf1e8db4c0f354bb2455..784d130d23ea483cb5aaaf9d11ba6100fe7b536d 100644 (file)
@@ -579,6 +579,7 @@ void dmb_expire_and_sync_browser_lists(time_t t);
 void announce_and_sync_with_domain_master_browser( struct subnet_record *subrec,
                                                    struct work_record *work);
 void collect_all_workgroup_names_from_wins_server(time_t t);
+void sync_all_dmbs(time_t t);
 
 /*The following definitions come from  nmbd/nmbd_elections.c  */
 
@@ -836,6 +837,13 @@ BOOL we_are_a_wins_client(void);
 struct subnet_record *get_next_subnet_maybe_unicast(struct subnet_record *subrec);
 struct subnet_record *get_next_subnet_maybe_unicast_or_wins_server(struct subnet_record *subrec);
 
+/*The following definitions come from  nmbd/nmbd_synclists.c  */
+
+void sync_browse_lists(struct work_record *work,
+                      char *name, int nm_type, 
+                      struct in_addr ip, BOOL local, BOOL servers);
+void sync_check_completion(void);
+
 /*The following definitions come from  nmbd/nmbd_winsproxy.c  */
 
 void make_wins_proxy_name_query_request( struct subnet_record *subrec, 
index 3bb2584eaf29365c5ca20771919f6a6d28bb54e5..9210ce4dcf8d6263159cbde532b0087e855c80e3 100644 (file)
@@ -396,6 +396,16 @@ static void process(void)
      * (nmbd_packets.c)
      */
     retransmit_or_expire_response_records(t);
+
+    /*
+     * check to see if any remote browse sync child processes have completed
+     */
+    sync_check_completion();
+
+    /*
+     * regularly sync with any other DMBs we know about 
+     */
+    sync_all_dmbs(t);
   }
 } /* process */
 
index ac2cb5b71ec21b5a364572b3b92c30d17f703670..a4faca55152c5d56f969f37e48590ef7a7e5317b 100644 (file)
@@ -34,145 +34,9 @@ extern fstring global_myworkgroup;
 /* This is our local master browser list database. */
 extern struct ubi_dlList lmb_browserlist[];
 
-static struct work_record *call_work;
-static struct subnet_record *call_subrec;
-
-/*******************************************************************
-  This is the NetServerEnum callback.
-  ******************************************************************/
-
-static void callback(char *sname, uint32 stype, char *comment)
-{
-  struct work_record *work;
-
-  stype &= ~SV_TYPE_LOCAL_LIST_ONLY;
-
-  if (stype & SV_TYPE_DOMAIN_ENUM) 
-  {
-    /* See if we can find the workgroup on this subnet. */
-    if(( work = find_workgroup_on_subnet( call_subrec, sname )) != NULL)
-    {
-      /* We already know about this workgroup - update the ttl. */
-      update_workgroup_ttl( work, lp_max_ttl() );
-    }
-    else
-    {
-      /* Create the workgroup on the subnet. */
-      create_workgroup_on_subnet( call_subrec, sname, lp_max_ttl() );
-    }
-  }
-  else
-  {
-    /* Server entry. */
-    struct server_record *servrec;
-
-    work = call_work;
-
-    if(( servrec = find_server_in_workgroup( work, sname )) != NULL)
-    {
-      /* Check that this is not a locally known server - if so ignore the
-         entry. */
-      if(!(servrec->serv.type & SV_TYPE_LOCAL_LIST_ONLY))
-      {
-        /* We already know about this server - update the ttl. */
-        update_server_ttl(servrec, lp_max_ttl() );
-        /* Update the type. */
-        servrec->serv.type = stype;
-      }
-    }
-    else
-    {
-      /* Create the server in the workgroup. */ 
-      create_server_on_workgroup(work, sname,stype,lp_max_ttl(),comment);
-    }
-  }
-}
-
-/*******************************************************************
-  Synchronise browse lists with another browse server.
-  Log in on the remote server's SMB port to their IPC$ service,
-  do a NetServerEnum and update our server and workgroup databases.
-******************************************************************/
-
-static void sync_browse_lists(struct subnet_record *subrec, struct work_record *work,
-                      char *name, int nm_type, struct in_addr ip, BOOL local)
-{
-  extern fstring local_machine;
-  static struct cli_state cli;
-  uint32 local_type = local ? SV_TYPE_LOCAL_LIST_ONLY : 0;
-
-  if( DEBUGLVL( 2 ) )
-  {
-    dbgtext( "sync_browse_lists():\n" );
-    dbgtext( "Sync browse lists with server %s<%02x> ", name, nm_type );
-    dbgtext( "at IP %s ", inet_ntoa( ip ) );
-    dbgtext( "for workgroup %s\n", work->work_group );
-  }
-
-  /* Check we're not trying to sync with ourselves. This can happen if we are
-     a domain *and* a local master browser. */
-  if(ismyip(ip))
-  {
-    DEBUG(2,("sync_browse_lists: We are both a domain and a local master browser for workgroup %s. \
-Do not sync with ourselves.\n", work->work_group ));
-    return;
-  }
-
-  if (!cli_initialise(&cli) || !cli_connect(&cli, name, &ip))
-  {
-    DEBUG(0,("sync_browse_lists: Failed to start browse sync with %s\n", name));
-    return;
-  }
-
-  if (!cli_session_request(&cli, name, nm_type, local_machine))
-  {
-    DEBUG(0,("sync_browse_lists: %s rejected the browse sync session\n",name));
-    cli_shutdown(&cli);
-    return;
-  }
-
-  if (!cli_negprot(&cli))
-  {
-    DEBUG(0,("sync_browse_lists: %s rejected the negprot\n",name));
-    cli_shutdown(&cli);
-    return;
-  }
-
-  if (!cli_session_setup(&cli, "", "", 1, "", 0, work->work_group))
-  {
-    DEBUG(0,("sync_browse_lists: %s rejected the browse sync sessionsetup\n", 
-             name));
-    cli_shutdown(&cli);
-    return;
-  }
-
-  if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1))
-  {
-    DEBUG(0,("sync_browse_lists: %s refused browse sync IPC$ connect\n", name));
-    cli_shutdown(&cli);
-    return;
-  }
-
-  call_work = work;
-  call_subrec = subrec;
-
-  /* Fetch a workgroup list. */
-  cli_NetServerEnum(&cli, work->work_group, 
-                    local_type|SV_TYPE_DOMAIN_ENUM,
-                    callback);
-
-  /* Now fetch a server list. */
-  cli_NetServerEnum(&cli, work->work_group, 
-                    local?SV_TYPE_LOCAL_LIST_ONLY:SV_TYPE_ALL,
-                    callback);
-
-  cli_shutdown(&cli);
-}
-
 /****************************************************************************
 As a domain master browser, do a sync with a local master browser.
 **************************************************************************/
-
 static void sync_with_lmb(struct browse_cache_record *browc)
 {                     
   struct work_record *work;
@@ -198,7 +62,7 @@ for workgroup %s and we are not a domain master browser on this workgroup. Error
   DEBUG(2, ("sync_with_lmb: Initiating sync with local master browser %s<0x20> at IP %s for \
 workgroup %s\n", browc->lmb_name, inet_ntoa(browc->ip), browc->work_group));
 
-  sync_browse_lists(unicast_subnet, work, browc->lmb_name, 0x20, browc->ip, True);
+  sync_browse_lists(work, browc->lmb_name, 0x20, browc->ip, True, True);
 
   browc->sync_time += (CHECK_TIME_DMB_TO_LMB_SYNC * 60);
 }
@@ -206,7 +70,6 @@ workgroup %s\n", browc->lmb_name, inet_ntoa(browc->ip), browc->work_group));
 /****************************************************************************
 Sync or expire any local master browsers.
 **************************************************************************/
-
 void dmb_expire_and_sync_browser_lists(time_t t)
 {
   static time_t last_run = 0;
@@ -272,8 +135,8 @@ static void sync_with_dmb(struct work_record *work)
   DEBUG(2, ("sync_with_dmb: Initiating sync with domain master browser %s at IP %s for \
 workgroup %s\n", namestr(&work->dmb_name), inet_ntoa(work->dmb_addr), work->work_group));
 
-  sync_browse_lists(unicast_subnet, work, work->dmb_name.name, work->dmb_name.name_type, 
-                    work->dmb_addr, False);
+  sync_browse_lists(work, work->dmb_name.name, work->dmb_name.name_type, 
+                    work->dmb_addr, False, True);
 }
 
 /****************************************************************************
@@ -654,7 +517,6 @@ for name %s. This means it is probably not a Samba 1.9.18 or above WINS server.\
  <1b> name in the reply - this is the workgroup name. Add this to the unicast
  subnet. This is expensive, so we only do this every 15 minutes.
 **************************************************************************/
-
 void collect_all_workgroup_names_from_wins_server(time_t t)
 {
   static time_t lastrun = 0;
@@ -689,3 +551,45 @@ void collect_all_workgroup_names_from_wins_server(time_t t)
              find_all_domain_master_names_query_fail,
              NULL);
 } 
+
+
+/****************************************************************************
+ If we are a domain master browser on the unicast subnet, do a regular sync
+ with all other DMBs that we know of on that subnet
+**************************************************************************/
+void sync_all_dmbs(time_t t)
+{
+       static time_t lastrun = 0;
+       struct work_record *work;
+
+       /* Only do this if we are using a WINS server. */
+       if(we_are_a_wins_client() == False)
+               return;
+
+       /* Check to see if we are a domain master browser on the
+           unicast subnet. */
+       work = find_workgroup_on_subnet(unicast_subnet, global_myworkgroup);
+       if (!work) return;
+
+       if (!AM_DOMAIN_MASTER_BROWSER(work))
+               return;
+
+       if ((lastrun != 0) && (t < lastrun + (15 * 60)))
+               return;
+     
+
+       for (work=unicast_subnet->workgrouplist; work; work = work->next) {
+               if (strcmp(global_myworkgroup, work->work_group) &&
+                   !ip_equal(work->dmb_addr, ipzero)) {
+                       lastrun = t;
+
+                       DEBUG(3,("initiating DMB<->DMB sync with %s(%s)\n",
+                                work->dmb_name.name, 
+                                inet_ntoa(work->dmb_addr)));
+                       sync_browse_lists(work, 
+                                         work->dmb_name.name,
+                                         work->dmb_name.name_type, 
+                                         work->dmb_addr, False, False);
+               }
+       }
+} 
diff --git a/source3/nmbd/nmbd_synclists.c b/source3/nmbd/nmbd_synclists.c
new file mode 100644 (file)
index 0000000..b62d9b7
--- /dev/null
@@ -0,0 +1,282 @@
+/* 
+   Unix SMB/Netbios implementation.
+   Version 1.9.
+   NBT netbios routines and daemon - version 2
+   Copyright (C) Andrew Tridgell 1994-1998
+   Copyright (C) Luke Kenneth Casson Leighton 1994-1998
+   Copyright (C) Jeremy Allison 1994-1998
+   
+   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
+   (at your option) any later version.
+   
+   This program is distributed in the hope that it will be useful,
+   but WITHOUT ANY WARRANTY; without even the implied warranty of
+   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+   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.
+   
+*/
+
+/* this file handles asynchronous browse synchronisation requests. The
+   requests are done by forking and putting the result in a file in the
+   locks directory. We do it this way because we don't want nmbd to be
+   blocked waiting for some server to respond on a TCP connection. This
+   also allows us to have more than 1 sync going at once (tridge) */
+
+#include "includes.h"
+#include "smb.h"
+
+extern int DEBUGLEVEL;
+
+struct sync_record {
+       struct sync_record *next, *prev;
+       fstring workgroup;
+       fstring server;
+       pstring fname;
+       struct in_addr ip;
+       int pid;
+};
+
+/* a linked list of current sync connections */
+static struct sync_record *syncs;
+
+static FILE *fp;
+
+/*******************************************************************
+  This is the NetServerEnum callback.
+  ******************************************************************/
+static void callback(char *sname, uint32 stype, char *comment)
+{
+       fprintf(fp,"\"%s\" %08X \"%s\"\n", sname, stype, comment);
+}
+
+
+/*******************************************************************
+  Synchronise browse lists with another browse server.
+  Log in on the remote server's SMB port to their IPC$ service,
+  do a NetServerEnum and record the results in fname
+******************************************************************/
+static void sync_child(char *name, int nm_type, 
+                      char *workgroup,
+                      struct in_addr ip, BOOL local, BOOL servers,
+                      char *fname)
+{
+       extern fstring local_machine;
+       static struct cli_state cli;
+       uint32 local_type = local ? SV_TYPE_LOCAL_LIST_ONLY : 0;
+
+       if (!cli_initialise(&cli) || !cli_connect(&cli, name, &ip)) {
+               fclose(fp);
+               return;
+       }
+
+       if (!cli_session_request(&cli, name, nm_type, local_machine)) {
+               cli_shutdown(&cli);
+               fclose(fp);
+               return;
+       }
+
+       if (!cli_negprot(&cli)) {
+               cli_shutdown(&cli);
+               return;
+       }
+
+       if (!cli_session_setup(&cli, "", "", 1, "", 0, workgroup)) {
+               cli_shutdown(&cli);
+               return;
+       }
+
+       if (!cli_send_tconX(&cli, "IPC$", "IPC", "", 1)) {
+               cli_shutdown(&cli);
+               return;
+       }
+
+       /* Fetch a workgroup list. */
+       cli_NetServerEnum(&cli, workgroup, 
+                         local_type|SV_TYPE_DOMAIN_ENUM,
+                         callback);
+       
+       /* Now fetch a server list. */
+       if (servers) {
+               cli_NetServerEnum(&cli, workgroup, 
+                                 local?SV_TYPE_LOCAL_LIST_ONLY:SV_TYPE_ALL,
+                                 callback);
+       }
+       
+       cli_shutdown(&cli);
+}
+
+
+/*******************************************************************
+  initialise a browse sync with another browse server.  Log in on the
+  remote server's SMB port to their IPC$ service, do a NetServerEnum
+  and record the results
+******************************************************************/
+void sync_browse_lists(struct work_record *work,
+                      char *name, int nm_type, 
+                      struct in_addr ip, BOOL local, BOOL servers)
+{
+       struct sync_record *s;
+       static int counter;
+
+       /* Check we're not trying to sync with ourselves. This can
+          happen if we are a domain *and* a local master browser. */
+       if (ismyip(ip)) {
+               return;
+       }
+
+       s = (struct sync_record *)malloc(sizeof(*s));
+       if (!s) return;
+
+       ZERO_STRUCTP(s);
+       
+       fstrcpy(s->workgroup, work->work_group);
+       fstrcpy(s->server, name);
+       s->ip = ip;
+
+       slprintf(s->fname, sizeof(pstring)-1,
+                "%s/sync.%d", lp_lockdir(), counter++);
+       string_sub(s->fname,"//", "/");
+       
+       DLIST_ADD(syncs, s);
+
+       /* the parent forks and returns, leaving the child to do the
+          actual sync */
+       CatchChild();
+       if ((s->pid = fork())) return;
+
+       DEBUG(2,("Initiating browse sync for %s to %s(%s)\n",
+                work->work_group, name, inet_ntoa(ip)));
+
+       fp = fopen(s->fname,"w");
+       if (!fp) _exit(1);      
+
+       sync_child(name, nm_type, work->work_group, ip, local, servers,
+                  s->fname);
+
+       fclose(fp);
+       _exit(0);
+}
+
+/**********************************************************************
+handle one line from a completed sync file
+ **********************************************************************/
+static void complete_one(struct sync_record *s, 
+                        char *sname, uint32 stype, char *comment)
+{
+       struct work_record *work;
+       struct server_record *servrec;
+
+       stype &= ~SV_TYPE_LOCAL_LIST_ONLY;
+
+       if (stype & SV_TYPE_DOMAIN_ENUM) {
+               /* See if we can find the workgroup on this subnet. */
+               if((work=find_workgroup_on_subnet(unicast_subnet, sname))) {
+                       /* We already know about this workgroup -
+                           update the ttl. */
+                       update_workgroup_ttl(work,lp_max_ttl());
+               } else {
+                       /* Create the workgroup on the subnet. */
+                       work = create_workgroup_on_subnet(unicast_subnet, 
+                                                         sname, lp_max_ttl());
+                       if (work) {
+                               /* remember who the master is */
+                               fstrcpy(work->local_master_browser_name, 
+                                       comment);
+                       }
+               }
+               return;
+       } 
+
+       work = find_workgroup_on_subnet(unicast_subnet, s->workgroup);
+       if (!work) {
+               DEBUG(3,("workgroup %s doesn't exist on unicast subnet?\n",
+                        s->workgroup));
+               return;
+       }
+
+       if ((servrec = find_server_in_workgroup( work, sname))) {
+               /* Check that this is not a locally known
+                  server - if so ignore the entry. */
+               if(!(servrec->serv.type & SV_TYPE_LOCAL_LIST_ONLY)) {
+                       /* We already know about this server - update
+                           the ttl. */
+                       update_server_ttl(servrec, lp_max_ttl());
+                       /* Update the type. */
+                       servrec->serv.type = stype;
+               }
+               return;
+       } 
+
+       /* Create the server in the workgroup. */ 
+       create_server_on_workgroup(work, sname,stype, lp_max_ttl(), comment);
+}
+               
+
+/**********************************************************************
+read the completed sync info
+ **********************************************************************/
+static void complete_sync(struct sync_record *s)
+{
+       FILE *f;
+       fstring server, type_str;
+       unsigned type;
+       pstring comment;
+       pstring line;
+       char *ptr;
+       int count=0;
+
+       f = fopen(s->fname,"r");
+       
+       while (!feof(f)) {
+               
+               if (!fgets_slash(line,sizeof(pstring),f)) continue;
+               
+               ptr = line;
+
+               DEBUG(9,("sync line [%s]\n", line));
+               
+               if (!next_token(&ptr,server,NULL) ||
+                   !next_token(&ptr,type_str,NULL) ||
+                   !next_token(&ptr,comment,NULL)) {
+                       continue;
+               }
+
+               sscanf(type_str, "%X", &type);
+
+               complete_one(s, server, type, comment);
+
+               count++;
+       }
+
+       fclose(f);
+
+       unlink(s->fname);
+
+       DEBUG(2,("sync with %s(%s) for workgroup %s completed (%d records)\n",
+                s->server, inet_ntoa(s->ip), s->workgroup, count));
+}
+
+/**********************************************************************
+check for completion of any of the child processes
+ **********************************************************************/
+void sync_check_completion(void)
+{
+       struct sync_record *s, *next;
+
+       for (s=syncs;s;s=next) {
+               next = s->next;
+               if (!process_exists(s->pid)) {
+                       /* it has completed - grab the info */
+                       complete_sync(s);
+                       DLIST_REMOVE(syncs, s);
+                       ZERO_STRUCTP(s);
+                       free(s);
+               }
+       }
+}