Convert all uses of uint8/16/32 to _t in nmbd and the include file.
[samba.git] / source3 / nmbd / nmbd_serverlistdb.c
index b3dca36c8465f734d929936f0d7292c255cb2a9e..b4057193b2f23b40fcc6ad3fac016cfaab4d5864 100644 (file)
@@ -1,14 +1,13 @@
 /* 
-   Unix SMB/Netbios implementation.
-   Version 1.9.
+   Unix SMB/CIFS implementation.
    NBT netbios routines and daemon - version 2
-   Copyright (C) Andrew Tridgell 1994-1997
-   Copyright (C) Luke Kenneth Casson Leighton 1994-199
-   Copyright (C) Jeremy Allison 1994-1997
+   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
+   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/>.
    
 */
 
 #include "includes.h"
-#include "smb.h"
-
-extern int ClientNMB;
-
-extern int DEBUGLEVEL;
-
-extern pstring myname;
-extern fstring myworkgroup;
-extern char **my_netbios_names;
+#include "system/filesys.h"
+#include "../librpc/gen_ndr/svcctl.h"
+#include "nmbd/nmbd.h"
 
 int updatecount = 0;
 
@@ -41,27 +33,18 @@ int updatecount = 0;
 
 void remove_all_servers(struct work_record *work)
 {
-  struct server_record *servrec;
-  struct server_record *nexts;
-
-  for (servrec = work->serverlist; servrec; servrec = nexts)
-  {
-    DEBUG(7,("remove_all_servers: Removing server %s\n",servrec->serv.name));
-    nexts = servrec->next;
-
-    if (servrec->prev)
-      servrec->prev->next = servrec->next;
-    if (servrec->next)
-      servrec->next->prev = servrec->prev;
-
-    if (work->serverlist == servrec)
-      work->serverlist = servrec->next;
-
-    free((char *)servrec);
-
-  }
-
-  work->subnet->work_changed = True;
+       struct server_record *servrec;
+       struct server_record *nexts;
+
+       for (servrec = work->serverlist; servrec; servrec = nexts) {
+               DEBUG(7,("remove_all_servers: Removing server %s\n",servrec->serv.name));
+               nexts = servrec->next;
+               DLIST_REMOVE(work->serverlist, servrec);
+               ZERO_STRUCTP(servrec);
+               SAFE_FREE(servrec);
+       }
+
+       work->subnet->work_changed = True;
 }
 
 /***************************************************************************
@@ -71,39 +54,23 @@ void remove_all_servers(struct work_record *work)
 static void add_server_to_workgroup(struct work_record *work,
                              struct server_record *servrec)
 {
-  struct server_record *servrec2;
-
-  if (!work->serverlist)
-  {
-    work->serverlist = servrec;
-    servrec->prev = NULL;
-    servrec->next = NULL;
-    return;
-  }
-
-  for (servrec2 = work->serverlist; servrec2->next; servrec2 = servrec2->next)
-    ;
-
-  servrec2->next = servrec;
-  servrec->next = NULL;
-  servrec->prev = servrec2;
-  work->subnet->work_changed = True;
+       DLIST_ADD_END(work->serverlist, servrec, struct server_record *);
+       work->subnet->work_changed = True;
 }
 
 /****************************************************************************
   Find a server in a server list.
   **************************************************************************/
 
-struct server_record *find_server_in_workgroup(struct work_record *work, char *name)
+struct server_record *find_server_in_workgroup(struct work_record *work, const char *name)
 {
-  struct server_record *ret;
+       struct server_record *ret;
   
-  for (ret = work->serverlist; ret; ret = ret->next)
-  {
-    if (strequal(ret->serv.name,name))
-      return ret;
-  }
-  return NULL;
+       for (ret = work->serverlist; ret; ret = ret->next) {
+               if (strequal(ret->serv.name,name))
+                       return ret;
+       }
+       return NULL;
 }
 
 
@@ -111,18 +78,12 @@ struct server_record *find_server_in_workgroup(struct work_record *work, char *n
   Remove a server entry from this workgroup.
   ****************************************************************************/
 
-static void remove_server_from_workgroup(struct work_record *work, struct server_record *servrec)
+void remove_server_from_workgroup(struct work_record *work, struct server_record *servrec)
 {
-  if (servrec->prev)
-    servrec->prev->next = servrec->next;
-  if (servrec->next)
-    servrec->next->prev = servrec->prev;
-
-  if (work->serverlist == servrec) 
-    work->serverlist = servrec->next; 
-
-  free((char *)servrec);
-  work->subnet->work_changed = True;
+       DLIST_REMOVE(work->serverlist, servrec);
+       ZERO_STRUCTP(servrec);
+       SAFE_FREE(servrec);
+       work->subnet->work_changed = True;
 }
 
 /****************************************************************************
@@ -130,50 +91,51 @@ static void remove_server_from_workgroup(struct work_record *work, struct server
   ****************************************************************************/
 
 struct server_record *create_server_on_workgroup(struct work_record *work,
-                                                 char *name,int servertype, 
-                                                 int ttl,char *comment)
+                                                 const char *name,int servertype, 
+                                                 int ttl, const char *comment)
 {
-  struct server_record *servrec;
+       struct server_record *servrec;
   
-  if (name[0] == '*')
-  {
-      DEBUG(7,("create_server_on_workgroup: not adding name starting with '*' (%s)\n",
-             name));
-      return (NULL);
-  }
+       if (name[0] == '*') {
+               DEBUG(7,("create_server_on_workgroup: not adding name starting with '*' (%s)\n",
+                       name));
+               return (NULL);
+       }
   
-  if((servrec = find_server_in_workgroup(work, name)) != NULL)
-  {
-    DEBUG(0,("create_server_on_workgroup: Server %s already exists on \
+       if(find_server_in_workgroup(work, name) != NULL) {
+               DEBUG(0,("create_server_on_workgroup: Server %s already exists on \
 workgroup %s. This is a bug.\n", name, work->work_group));
-    return NULL;
-  }
+               return NULL;
+       }
   
-  if((servrec = (struct server_record *)malloc(sizeof(*servrec))) == NULL)
-  {
-    DEBUG(0,("create_server_entry_on_workgroup: malloc fail !\n"));
-    return NULL;
-  }
+       if((servrec = SMB_MALLOC_P(struct server_record)) == NULL) {
+               DEBUG(0,("create_server_entry_on_workgroup: malloc fail !\n"));
+               return NULL;
+       }
 
-  bzero((char *)servrec,sizeof(*servrec));
+       memset((char *)servrec,'\0',sizeof(*servrec));
  
-  servrec->subnet = work->subnet;
+       servrec->subnet = work->subnet;
  
-  StrnCpy(servrec->serv.name,name,sizeof(servrec->serv.name)-1);
-  StrnCpy(servrec->serv.comment,comment,sizeof(servrec->serv.comment)-1);
-  strupper(servrec->serv.name);
-  servrec->serv.type  = servertype;
-
-  update_server_ttl(servrec, ttl);
+       fstrcpy(servrec->serv.name,name);
+       fstrcpy(servrec->serv.comment,comment);
+       if (!strupper_m(servrec->serv.name)) {
+               DEBUG(2,("strupper_m %s failed\n", servrec->serv.name));
+               SAFE_FREE(servrec);
+               return NULL;
+       }
+       servrec->serv.type  = servertype;
+
+       update_server_ttl(servrec, ttl);
   
-  add_server_to_workgroup(work, servrec);
+       add_server_to_workgroup(work, servrec);
       
-  DEBUG(3,("create_server_on_workgroup: Created server entry %s of type %x (%s) on \
+       DEBUG(3,("create_server_on_workgroup: Created server entry %s of type %x (%s) on \
 workgroup %s.\n", name,servertype,comment, work->work_group));
  
-  work->subnet->work_changed = True;
+       work->subnet->work_changed = True;
  
-  return(servrec);
+       return(servrec);
 }
 
 /*******************************************************************
@@ -182,15 +144,15 @@ workgroup %s.\n", name,servertype,comment, work->work_group));
 
 void update_server_ttl(struct server_record *servrec, int ttl)
 {
-  if(ttl > lp_max_ttl())
-    ttl = lp_max_ttl();
+       if(ttl > lp_max_ttl())
+               ttl = lp_max_ttl();
 
-  if(is_myname(servrec->serv.name))
-    servrec->death_time = PERMANENT_TTL;
-  else
-    servrec->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
+       if(is_myname(servrec->serv.name))
+               servrec->death_time = PERMANENT_TTL;
+       else
+               servrec->death_time = (ttl != PERMANENT_TTL) ? time(NULL)+(ttl*3) : PERMANENT_TTL;
 
-  servrec->subnet->work_changed = True;
+       servrec->subnet->work_changed = True;
 }
 
 /*******************************************************************
@@ -201,20 +163,18 @@ void update_server_ttl(struct server_record *servrec, int ttl)
 
 void expire_servers(struct work_record *work, time_t t)
 {
-  struct server_record *servrec;
-  struct server_record *nexts;
+       struct server_record *servrec;
+       struct server_record *nexts;
   
-  for (servrec = work->serverlist; servrec; servrec = nexts)
-  {
-    nexts = servrec->next;
-
-    if ((servrec->death_time != PERMANENT_TTL) && ((t == -1) || (servrec->death_time < t)))
-    {
-      DEBUG(3,("expire_old_servers: Removing timed out server %s\n",servrec->serv.name));
-      remove_server_from_workgroup(work, servrec);
-      work->subnet->work_changed = True;
-    }
-  }
+       for (servrec = work->serverlist; servrec; servrec = nexts) {
+               nexts = servrec->next;
+
+               if ((servrec->death_time != PERMANENT_TTL) && ((t == -1) || (servrec->death_time < t))) {
+                       DEBUG(3,("expire_old_servers: Removing timed out server %s\n",servrec->serv.name));
+                       remove_server_from_workgroup(work, servrec);
+                       work->subnet->work_changed = True;
+               }
+       }
 }
 
 /*******************************************************************
@@ -223,231 +183,227 @@ void expire_servers(struct work_record *work, time_t t)
  out this server record from an earlier subnet.
 ******************************************************************/
 
-static uint32 write_this_server_name( struct subnet_record *subrec,
+static uint32_t write_this_server_name( struct subnet_record *subrec,
                                       struct work_record *work,
                                       struct server_record *servrec)
 {
-  struct subnet_record *ssub;
-  struct work_record *iwork;
-  struct server_record *sserv;
-
-  /* Go through all the subnets we have already seen. */
-  for (ssub = FIRST_SUBNET; ssub != subrec; ssub = NEXT_SUBNET_INCLUDING_UNICAST(ssub)) 
-  {
-    for(iwork = ssub->workgrouplist; iwork; iwork = iwork->next)
-    {
-      if((sserv = find_server_in_workgroup( iwork, servrec->serv.name)) != NULL)
-      {
-        /*
-         * We have already written out this server record, don't
-         * do it again. This gives precedence to servers we have seen
-         * on the broadcast subnets over servers that may have been
-         * added via a sync on the unicast_subet.
-         *
-         * The correct way to do this is to have a serverlist file
-         * per subnet - this means changes to smbd as well. I may
-         * add this at a later date (JRA).
-         */
-
-        return 0;
-      }
-    }
-  }
-
-  return servrec->serv.type;
+       struct subnet_record *ssub;
+       struct work_record *iwork;
+
+       /* Go through all the subnets we have already seen. */
+       for (ssub = FIRST_SUBNET; ssub && (ssub != subrec); ssub = NEXT_SUBNET_INCLUDING_UNICAST(ssub)) {
+               for(iwork = ssub->workgrouplist; iwork; iwork = iwork->next) {
+                       if(find_server_in_workgroup( iwork, servrec->serv.name) != NULL) {
+                               /*
+                                * We have already written out this server record, don't
+                                * do it again. This gives precedence to servers we have seen
+                                * on the broadcast subnets over servers that may have been
+                                * added via a sync on the unicast_subet.
+                                *
+                                * The correct way to do this is to have a serverlist file
+                                * per subnet - this means changes to smbd as well. I may
+                                * add this at a later date (JRA).
+                                */
+
+                               return 0;
+                       }
+               }
+       }
+
+       return servrec->serv.type;
 }
 
 /*******************************************************************
  Decide if we should write out a workgroup record for this workgroup.
- We return zero if we should not. Don't write out myworkgroup (we've
+ We return zero if we should not. Don't write out lp_workgroup() (we've
  already done it) and also don't write out a second workgroup record
  on the unicast subnet that we've already written out on one of the
  broadcast subnets.
 ******************************************************************/
 
-static uint32 write_this_workgroup_name( struct subnet_record *subrec, 
+static uint32_t write_this_workgroup_name( struct subnet_record *subrec,
                                          struct work_record *work)
 {
-  struct subnet_record *ssub;
+       struct subnet_record *ssub;
 
-  if(strequal(myworkgroup, work->work_group))
-    return 0;
+       if(strequal(lp_workgroup(), work->work_group))
+               return 0;
 
-  /* This is a workgroup we have seen on a broadcast subnet. All
-     these have the same type. */
+       /* This is a workgroup we have seen on a broadcast subnet. All
+               these have the same type. */
 
-  if(subrec != unicast_subnet)
-    return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY);
+       if(subrec != unicast_subnet)
+               return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY);
 
-  for(ssub = FIRST_SUBNET; ssub;  ssub = NEXT_SUBNET_EXCLUDING_UNICAST(ssub))
-  {
-    /* This is the unicast subnet so check if we've already written out
-       this subnet when we passed over the broadcast subnets. */
+       for(ssub = FIRST_SUBNET; ssub;  ssub = NEXT_SUBNET_EXCLUDING_UNICAST(ssub)) {
+               /* This is the unicast subnet so check if we've already written out
+                       this subnet when we passed over the broadcast subnets. */
 
-    if(find_workgroup_on_subnet( ssub, work->work_group) != NULL)
-      return 0;
-  }
+               if(find_workgroup_on_subnet( ssub, work->work_group) != NULL)
+                       return 0;
+       }
 
-  /* All workgroups on the unicast subnet (except our own, which we
-     have already written out) cannot be local. */
+       /* All workgroups on the unicast subnet (except our own, which we
+               have already written out) cannot be local. */
 
-  return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT);
+       return (SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT);
 }
 
 /*******************************************************************
   Write out the browse.dat file.
   ******************************************************************/
 
-void write_browse_list(time_t t, BOOL force_write)
-{   
-  struct subnet_record *subrec;
-  struct work_record *work;
-  struct server_record *servrec;
-  pstring fname,fnamenew;
-  uint32 stype;
-  fstring tmp;
-  int i;
-  FILE *fp;
-  BOOL list_changed = force_write;
-  static time_t lasttime = 0;
-    
-  if (!lasttime)
-    lasttime = t;
-  if (t - lasttime < 5)
-    return;
-
-  for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
-  {
-    if(subrec->work_changed)
-    {
-      list_changed = True;
-      break;
-    }
-  }
-
-  if(!list_changed)
-    return;
-
-  lasttime = t;
-  updatecount++;
-    
-  dump_workgroups();
-  pstrcpy(fname,lp_lockdir());
-  trim_string(fname,NULL,"/");
-  strcat(fname,"/");
-  strcat(fname,SERVER_LIST);
-  pstrcpy(fnamenew,fname);
-  strcat(fnamenew,".");
-  fp = fopen(fnamenew,"w");
-  if (!fp)
-  {
-    DEBUG(0,("write_browse_list: Can't open file %s. Error was %s\n",
-              fnamenew,strerror(errno)));
-    return;
-  } 
-  
-  /*
-   * Write out a record for our workgroup. Use the record from the first
-   * subnet.
-   */
-
-  if((work = find_workgroup_on_subnet(FIRST_SUBNET, myworkgroup)) == NULL)
-  { 
-    DEBUG(0,("write_browse_list: Fatal error - cannot find my workgroup %s\n",
-             myworkgroup));
-    fclose(fp);
-    return;
-  }
-
-  sprintf(tmp, "\"%s\"", work->work_group);
-  fprintf(fp, "%-25s ", tmp);
-  fprintf(fp, "%08x ", SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY);
-  sprintf(tmp, "\"%s\" ", work->local_master_browser_name);
-  fprintf(fp, "%-30s", tmp);
-  fprintf(fp, "\"%s\"\n", work->work_group);
-
-  /* 
-   * We need to do something special for our own names.
-   * This is due to the fact that we may be a local master browser on
-   * one of our broadcast subnets, and a domain master on the unicast
-   * subnet. We iterate over the subnets and only write out the name
-   * once.
-   */
-
-  for (i=0; my_netbios_names[i]; i++)
-  {
-    stype = 0;
-    for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec))
-    {
-      if((work = find_workgroup_on_subnet( subrec, myworkgroup )) == NULL)
-        continue;
-      if((servrec = find_server_in_workgroup( work, my_netbios_names[i])) == NULL)
-        continue;
-
-      stype |= servrec->serv.type;
-    }
-
-    /* Output server details, plus what workgroup they're in. */
-    sprintf(tmp, "\"%s\"", my_netbios_names[i]);
-    fprintf(fp, "%-25s ", tmp);
-    fprintf(fp, "%08x ", stype);
-    sprintf(tmp, "\"%s\" ", lp_serverstring());
-    fprintf(fp, "%-30s", tmp);
-    fprintf(fp, "\"%s\"\n", myworkgroup);
-  }
-      
-  for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) 
-  { 
-    subrec->work_changed = False;
-
-    for (work = subrec->workgrouplist; work ; work = work->next)
-    {
-      /* Write out a workgroup record for a workgroup. */
-      uint32 wg_type = write_this_workgroup_name( subrec, work);
-
-      if(wg_type)
-      {
-        sprintf(tmp, "\"%s\"", work->work_group);
-        fprintf(fp, "%-25s ", tmp);
-
-        fprintf(fp, "%08x ", wg_type);
-        sprintf(tmp, "\"%s\" ", work->local_master_browser_name);
-        fprintf(fp, "%-30s", tmp);
-        fprintf(fp, "\"%s\"\n", work->work_group);
-      }
-
-      /* Now write out any server records a workgroup may have. */
-
-      for (servrec = work->serverlist; servrec ; servrec = servrec->next)
-      {
-        uint32 serv_type;
-
-        /* We have already written our names here. */
-        if(is_myname(servrec->serv.name))
-          continue; 
-
-        serv_type = write_this_server_name(subrec, work, servrec);
-
-        if(serv_type)
-        {
-          /* Output server details, plus what workgroup they're in. */
-          sprintf(tmp, "\"%s\"", servrec->serv.name);
-          fprintf(fp, "%-25s ", tmp);
-          fprintf(fp, "%08x ", serv_type);
-          sprintf(tmp, "\"%s\" ", servrec->serv.comment);
-          fprintf(fp, "%-30s", tmp);
-          fprintf(fp, "\"%s\"\n", work->work_group);
-        }
-      }
-    }
-  } 
-  
-  fclose(fp);
-  unlink(fname);
-  chmod(fnamenew,0644);
-  rename(fnamenew,fname);
-  DEBUG(3,("write_browse_list: Wrote browse list into file %s\n",fname));
+void write_browse_list_entry(XFILE *fp, const char *name, uint32_t rec_type,
+               const char *local_master_browser_name, const char *description)
+{
+       fstring tmp;
+
+       slprintf(tmp,sizeof(tmp)-1, "\"%s\"", name);
+       x_fprintf(fp, "%-25s ", tmp);
+       x_fprintf(fp, "%08x ", rec_type);
+       slprintf(tmp, sizeof(tmp)-1, "\"%s\" ", local_master_browser_name);
+       x_fprintf(fp, "%-30s", tmp);
+       x_fprintf(fp, "\"%s\"\n", description);
+}
+
+void write_browse_list(time_t t, bool force_write)
+{
+       struct subnet_record *subrec;
+       struct work_record *work;
+       struct server_record *servrec;
+       char *fname;
+       char *fnamenew;
+       uint32_t stype;
+       int i;
+       XFILE *fp;
+       bool list_changed = force_write;
+       static time_t lasttime = 0;
+       TALLOC_CTX *ctx = talloc_tos();
+
+       /* Always dump if we're being told to by a signal. */
+       if(force_write == False) {
+               if (!lasttime)
+                       lasttime = t;
+               if (t - lasttime < 5)
+                       return;
+       }
+
+       lasttime = t;
+
+       dump_workgroups(force_write);
+
+       for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+               if(subrec->work_changed) {
+                       list_changed = True;
+                       break;
+               }
+       }
+
+       if(!list_changed)
+               return;
+
+       updatecount++;
+
+       fname = cache_path(SERVER_LIST);
+       if (!fname) {
+               return;
+       }
+       fnamenew = talloc_asprintf(ctx, "%s.",
+                               fname);
+       if (!fnamenew) {
+               talloc_free(fname);
+               return;
+       }
+
+       fp = x_fopen(fnamenew,O_WRONLY|O_CREAT|O_TRUNC, 0644);
+
+       if (!fp) {
+               DEBUG(0,("write_browse_list: Can't open file %s. Error was %s\n",
+                       fnamenew,strerror(errno)));
+               talloc_free(fnamenew);
+               talloc_free(fname);
+               return;
+       }
+
+       /*
+        * Write out a record for our workgroup. Use the record from the first
+        * subnet.
+        */
+
+       if((work = find_workgroup_on_subnet(FIRST_SUBNET, lp_workgroup())) == NULL) { 
+               DEBUG(0,("write_browse_list: Fatal error - cannot find my workgroup %s\n",
+                       lp_workgroup()));
+               x_fclose(fp);
+               talloc_free(fnamenew);
+               talloc_free(fname);
+               return;
+       }
+
+       write_browse_list_entry(fp, work->work_group,
+               SV_TYPE_DOMAIN_ENUM|SV_TYPE_NT|SV_TYPE_LOCAL_LIST_ONLY,
+               work->local_master_browser_name, work->work_group);
+
+       /*
+        * We need to do something special for our own names.
+        * This is due to the fact that we may be a local master browser on
+        * one of our broadcast subnets, and a domain master on the unicast
+        * subnet. We iterate over the subnets and only write out the name
+        * once.
+        */
+
+       for (i=0; my_netbios_names(i); i++) {
+               stype = 0;
+               for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) {
+                       if((work = find_workgroup_on_subnet( subrec, lp_workgroup() )) == NULL)
+                               continue;
+                       if((servrec = find_server_in_workgroup( work, my_netbios_names(i))) == NULL)
+                               continue;
+
+                       stype |= servrec->serv.type;
+               }
+
+               /* Output server details, plus what workgroup they're in. */
+               write_browse_list_entry(fp, my_netbios_names(i), stype,
+                       string_truncate(lp_server_string(talloc_tos()), MAX_SERVER_STRING_LENGTH), lp_workgroup());
+       }
+
+       for (subrec = FIRST_SUBNET; subrec ; subrec = NEXT_SUBNET_INCLUDING_UNICAST(subrec)) { 
+               subrec->work_changed = False;
+
+               for (work = subrec->workgrouplist; work ; work = work->next) {
+                       /* Write out a workgroup record for a workgroup. */
+                       uint32_t wg_type = write_this_workgroup_name( subrec, work);
+
+                       if(wg_type) {
+                               write_browse_list_entry(fp, work->work_group, wg_type,
+                                               work->local_master_browser_name,
+                                               work->work_group);
+                       }
+
+                       /* Now write out any server records a workgroup may have. */
+
+                       for (servrec = work->serverlist; servrec ; servrec = servrec->next) {
+                               uint32_t serv_type;
+
+                               /* We have already written our names here. */
+                               if(is_myname(servrec->serv.name))
+                                       continue;
+
+                               serv_type = write_this_server_name(subrec, work, servrec);
+                               if(serv_type) {
+                                       /* Output server details, plus what workgroup they're in. */
+                                       write_browse_list_entry(fp, servrec->serv.name, serv_type,
+                                               servrec->serv.comment, work->work_group);
+                               }
+                       }
+               }
+       }
+
+       x_fclose(fp);
+       unlink(fname);
+       chmod(fnamenew,0644);
+       rename(fnamenew,fname);
+       DEBUG(3,("write_browse_list: Wrote browse list into file %s\n",fname));
+       talloc_free(fnamenew);
+       talloc_free(fname);
 }