Explorar el Código

Merge branch 'newhub'

* newhub:
  * Write userfile after receiving it
  * Don't write out hubs if not a localhub/hub
  * Write userfile when adding/removing users/bots
  * Don't rewrite binary / notify bots if the hubs didn't change
  * Signal child leaf bots to reread the binary (new hubs) when writing userfile
  * Update docs
  * Support legacy hubs format when loading internal users
  * Align pack data to 16 byte boundary as well
  * Keep settings.hubs around as a default and populate into settings.conf_hubs via -C
  * Write out userfile in more places to trigger hub saving for localhubs
  * Save hubs to binary when saving userfile
  * Load hubs from conf.hubs on start
  * Read/Write hubs to conf
  * Move hubs to CONF binary area
  * Add cmd_newhub
  * Add help for cmd_newhub
  * Update bdlib
  * Cleanup help flags for newleaf

Conflicts:
	src/chanprog.c
Bryan Drewery hace 14 años
padre
commit
fe60598329
Se han modificado 14 ficheros con 245 adiciones y 93 borrados
  1. 2 0
      doc/UPDATES
  2. 18 11
      doc/help.txt
  3. 6 2
      src/binary.c
  4. 31 64
      src/chanprog.c
  5. 63 0
      src/cmds.c
  6. 83 1
      src/conf.c
  7. 3 0
      src/conf.h
  8. 1 2
      src/debug.c
  9. 10 1
      src/main.c
  10. 3 5
      src/misc.c
  11. 8 0
      src/mod/share.mod/share.c
  12. 8 4
      src/settings.h
  13. 8 2
      src/userrec.c
  14. 1 1
      src/userrec.h

+ 2 - 0
doc/UPDATES

@@ -15,6 +15,8 @@
   * When 'mdop' protection is on, re-op all previously opped clients automatically.
   * When 'mop' protection is on, deop all previously regular clients automatically.
   * Add './wraith -V' which will display the packconfig that the bot is using.
+  * Add cmd_newhub for adding a new hub. All bots will add this to their config.
+  * Hubs can now be overridden inside the botconfig (-C)
 
 maint
   * Fix various compile warnings with newer GCC

+ 18 - 11
doc/help.txt

@@ -56,7 +56,7 @@ See also: invites, -invite, stick, unstick
    have no flags, an optional hostmask, and a random pass/secpass.
    List as many hosts as needed.
  
-See also: -user, +host, -host, clearhosts%{+ni}, newleaf%{-}
+See also: -user, +host, -host, clearhosts%{+ni}, newleaf%{-}%{+a}, newhub%{-}
 ::-ban
 ###  $b-ban$b <banmask or number> [channel]
    Removes the specified ban from the list of bans stored on the bot. You may
@@ -68,7 +68,7 @@ See also: bans, +ban, stick, unstick
    This is exactly the same as $b'%d-user'$b (it removes a user record). It is
    included for convenience.
  
-See also: +user, -user%{+n}, newleaf%{-}
+See also: +user, -user%{+ni}, newleaf%{-}%{+a}, newhub%{-}
 ::-chan
 ### $b-chan$b <channel>
    This removes ALL information about a channel from the bot. 
@@ -121,7 +121,7 @@ See also: invites, +invite, stick, unstick
 ###  $b-user$b <handle> [anotherhandle] ...
    Removes the specified handles user records.
  
-See also: +user%{+ai}, -bot%{-}%{+nai}, newleaf%{-}
+See also: +user%{+ai}, -bot%{-}%{+ni}, newleaf%{-}%{+a}, newhub%{-}
 ::about:
 ###  $babout$b
    If you feel you are missing from the list feel free to contact bryan.
@@ -354,7 +354,7 @@ See also: version, netversion, about%{+m|m}, status%{-}
    and update all bots instead.
 %{-}
  
-See also: link
+See also: link%{+a}, newhub%{-}
 ::chaninfo
 ###  $bchaninfo$b <channel>
    This lists all the settings for the bot on the given channel.
@@ -775,7 +775,7 @@ See also: console, echo, login, page, strip
                if you change any of the following: 
     $bhomedir$b, $bbinpath$b, $bbinname$b, $bportmin$b, $bportmax$b
  
-See also: newleaf
+See also: newleaf, newhub
 ::console:
 ###  $bconsole$b [channel] [modes]
    Changes your console level so that you will see only the types of console
@@ -1248,7 +1248,7 @@ See also: netlag
    Attempts to link to another hub.  This command is deprecated and not 
    recommended for use.
  
-See also: unlink, newleaf%{+a}, -bot%{-}
+See also: unlink%{+ni}, newleaf%{-}%{+a}, newhub, -bot%{-}
 ::login
 ###  $blogin$b <banner|bots|channels|whom> [on/off]
    Sets various login options.
@@ -1433,6 +1433,11 @@ See also: botversion, version, about%{+m|m}, status%{-}
    the results over DCC.
  
 See also: w
+:hub:newhub:
+###  $bnewhub$b <handle> <address> <port> [hublevel]
+    Add a new hub to the net.
+ 
+See also: chaddr, newleaf
 :hub:newleaf:
 ###  $bnewleaf$b <handle> [hostmask] [anotherhostmask] ...
    Adds a new leaf to the botnet with the specified handle and hostname.
@@ -1440,6 +1445,8 @@ See also: w
  
    The binary config botline will also be outputted, but should be
    checked for correctness.
+ 
+%{+a}See also: newhub%{-}
 ::newpass
 ###  $bnewpass$b <password|rand>
    Changes your password on the bot. This is similar to the '/msg <bot> pass'
@@ -1520,7 +1527,7 @@ See also: restart
    The local bot must, however, have a bot record for the bot you wish to
    relay to. Typing '%dquit' or "*bye*" on a line by itself will end the relay.
  
-See also: bots%{+n}, newleaf%{-}%{+a}, -bot%{-}
+See also: bots%{+n}, newleaf%{-}%{+a}, newhub, -bot%{-}
 :leaf:release
 ###  $brelease$b
    Releases the bot's nick if it is on the jupenick. It gives an estimated
@@ -1624,8 +1631,8 @@ See also: reload, backup
 [N]  $bserver-port$b     Default port to use for server connections.
 [N]  $bserver-port-ssl$b Default port to use for SSL server connections.
 [B]  $bserver-use-ssl$b  Use SSL for IRC server connections?
-
-
+ 
+ 
 [B]  $bauth-chan$b       If set, auth cmds will work in channels as well as in msg, otherwise 
                           only in msg.
 [S]  $bauth-key$b        The authkey used during authing. Give to users if they need to auth. 
@@ -1665,7 +1672,7 @@ See also: reload, backup
 [N]  $bmsgburst$b        How many messages to burst at once to server. (Too high will excess flood)
 [N]  $bmsgrate$b         How often (msecs) to dequeue msgs to the server. Only used on
                            non-ratbox servers. Too small a value can result in Excess Flood.
-
+ 
 [L]  $brbl-servers$b     Servers to use for RBL checking in channels that are +rbl.
  
 [S]  $brealname$b        The bot's "real name" when connecting. (supports '$n' expansion)
@@ -1896,7 +1903,7 @@ See also: dump
    to unlink them. If "*" is specified as the parameter, all bots will be
    unlinked.
  
-See also: %{+n}link, %{-}bots, downbots%{+n}, newleaf, bottree%{-}
+See also: %{+n}link, %{-}bots, downbots%{+n}, newleaf, bottree%{-}%{+a}, newhub%{-}
 ::unstick
 ###  $bunstick$b [ban/exempt/invite] <hostmask/number> [channel]
    Makes a "sticky" ban, exempt, or invite normal again.

+ 6 - 2
src/binary.c

@@ -52,9 +52,9 @@
 settings_t settings = {
   SETTINGS_HEADER,
   /* -- STATIC -- */
-  "", "", "", "", "", "", "", "", "",
+  "", "", "", "", "", "", "", "", "", "",
   /* -- DYNAMIC -- */
-  "", "", "", "", "", "", "", "", "",
+  "", "", "", "", "", "", "", "", "", "",
   /* -- PADDING */
   ""
 };
@@ -519,6 +519,7 @@ static void edpack(settings_t *incfg, const char *in_hash, int what)
 
   /* -- DYNAMIC -- */
   dofield(incfg->dynamic_initialized);
+  dofield(incfg->conf_hubs);
   dofield(incfg->bots);
   dofield(incfg->uid);
   dofield(incfg->autocron);
@@ -552,6 +553,7 @@ tellconfig(settings_t *incfg)
 //  dofield(incfg->salt2);
   // -- DYNAMIC --
   dofield(incfg->dynamic_initialized);
+  dofield(incfg->conf_hubs);
   dofield(incfg->bots);
   dofield(incfg->uid);
   dofield(incfg->autocron);
@@ -728,6 +730,8 @@ void conf_to_bin(conf_t *in, bool move, int die)
                            bot->net.ip6 ? bot->net.ip6 : "");
     }
 
+  simple_snprintf(settings.conf_hubs, sizeof(settings.conf_hubs), in->hubs.join(',').c_str());
+
   newbin = binname;
 //  tellconfig(&settings); 
   write_settings(newbin, -1, 1);

+ 31 - 64
src/chanprog.c

@@ -382,81 +382,48 @@ void reaffirm_owners()
 }
 
 bool is_hub(const char* nick) {
-  char *p = settings.hubs, *p2 = NULL, hubbuf[HANDLEN + 1] ="";
-  size_t len = 0;
-
-  while (p && *p) {
-    if ((p2 = strchr(p, ' '))) {
-
-      len = p2 - p + 1;
-      if (len > sizeof(hubbuf))
-        len = sizeof(hubbuf);
-      strlcpy(hubbuf, p, len);
-      if (!strncasecmp(nick, hubbuf, HANDLEN))
-        return 1;
+  for (size_t idx = 0; idx < conf.hubs.length(); ++idx) {
+    bd::String hub(conf.hubs[idx]);
+    if (!strncasecmp(nick, newsplit(hub).c_str(), HANDLEN)) {
+      return true;
     }
-    if ((p = strchr(p, ',')))
-      p++;
   }
-  return 0;
+  return false;
 }
 
 void load_internal_users()
 {
-  char *p = NULL, *ln = NULL, *hand = NULL, *ip = NULL, *port = NULL, *pass = NULL, *q = NULL;
+  char *p = NULL, *ln = NULL, *hand = NULL, *pass = NULL, *q = NULL;
   char *hosts = NULL, buf[2048] = "", tmp[51] = "";
-  int i, hublevel = 0;
-  struct bot_addr *bi = NULL;
+  int i;
   struct userrec *u = NULL;
 
   /* hubs */
-  strlcpy(buf, settings.hubs, sizeof(buf));
-  p = buf;
-  while (p) {
-    ln = p;
-    p = strchr(p, ',');
-    if (p)
-      *p++ = 0;
-    hand = ln;
-    ip = NULL;
-    port = NULL;
-    u = NULL;
-    for (i = 0; ln; i++) {
-      switch (i) {
-        case 0:
-          hand = ln;
-          break;
-        case 1:
-          ip = ln;
-          break;
-        case 2:
-          port = ln;
-          hublevel++;		/* We must increment this even if it is already added */
-          if (!get_user_by_handle(userlist, hand)) {
-            userlist = adduser(userlist, hand, "none", "-", USER_OP, 1);
-            u = get_user_by_handle(userlist, hand);
-
-            simple_snprintf(tmp, sizeof(tmp), "%li [internal]", (long)now);
-            set_user(&USERENTRY_ADDED, u, tmp);
-
-            bi = (struct bot_addr *) my_calloc(1, sizeof(struct bot_addr));
-
-            bi->address = strdup(ip);
-            bi->telnet_port = atoi(port) ? atoi(port) : 0;
-            bi->relay_port = bi->telnet_port;
-            bi->hublevel = hublevel;
-            if (conf.bot->hub && (!bi->hublevel) && (!strcasecmp(hand, conf.bot->nick)))
-              bi->hublevel = 99;
-            bi->uplink = (char *) my_calloc(1, 1);
-            set_user(&USERENTRY_BOTADDR, u, bi);
-            /* set_user(&USERENTRY_PASS, get_user_by_handle(userlist, hand), SALT2); */
-          }
-          break;
-        default:
-          break;
+  for (size_t idx = 0; idx < conf.hubs.length(); ++idx) {
+    bd::Array<bd::String> params(static_cast<bd::String>(conf.hubs[idx]).split(' '));
+    bd::String handle(params[0]);
+    if (!get_user_by_handle(userlist, const_cast<char*>(handle.c_str()))) {
+      bd::String address(params[1]);
+      port_t port = atoi(static_cast<bd::String>(params[2]).c_str());
+      unsigned short hublevel = params.length() == 4 ? atoi(static_cast<bd::String>(params[3]).c_str()) : (idx + 1);
+
+      userlist = adduser(userlist, handle.c_str(), "none", "-", USER_OP, 1);
+      u = get_user_by_handle(userlist, const_cast<char*>(handle.c_str()));
+
+      simple_snprintf(tmp, sizeof(tmp), "%li [internal]", (long)now);
+      set_user(&USERENTRY_ADDED, u, tmp);
+
+      struct bot_addr *bi = (struct bot_addr *) my_calloc(1, sizeof(struct bot_addr));
+
+      bi->address = strdup(address.c_str());
+      bi->telnet_port = bi->relay_port = port;
+      bi->hublevel = hublevel;
+      if (conf.bot->hub && (!bi->hublevel) && (!strcasecmp(handle.c_str(), conf.bot->nick))) {
+        bi->hublevel = 99;
       }
-      if (ln && (ln = strchr(ln, ' ')))
-        *ln++ = 0;
+      bi->uplink = (char *) my_calloc(1, 1);
+      set_user(&USERENTRY_BOTADDR, u, bi);
+      /* set_user(&USERENTRY_PASS, get_user_by_handle(userlist, handle.c_str()), SALT2); */
     }
   }
 

+ 63 - 0
src/cmds.c

@@ -3250,6 +3250,68 @@ static void cmd_newleaf(int idx, char *par)
   }
 }
 
+static void cmd_newhub(int idx, char *par)
+{
+  bd::Array<bd::String> params(bd::String(par).split(' '));
+  if (!par[0] || params.length() < 3) {
+    dprintf(idx, "Usage: newhub <handle> <address> <port> [hublevel]\n");
+    return;
+  }
+
+  putlog(LOG_CMDS, "*", "#%s# newhub %s", dcc[idx].nick, par);
+
+  bd::String handle(params[0]);
+
+  if (handle.length() > HANDLEN) {
+    handle.resize(HANDLEN);
+  }
+  if (get_user_by_handle(userlist, const_cast<char*>(handle.c_str()))) {
+    dprintf(idx, "Already got a %s hub\n", handle.c_str());
+    return;
+  } else if (strchr(BADHANDCHARS, handle[0]) != NULL) {
+    dprintf(idx, "You can't start a botnick with '%c'.\n", static_cast<char>(handle[0]));
+    return;
+  }
+
+  bd::String address(params[1]);
+  port_t port = atoi(static_cast<bd::String>(params[2]).c_str());
+
+  unsigned short hublevel = 0;
+  // Was a hublevel passed in?
+  if (params.length() == 4) {
+    hublevel = atoi(static_cast<bd::String>(params[3]).c_str());
+  } else {
+    // Find next available hublevel
+    for (struct userrec *u = userlist; u; u = u->next) {
+      unsigned short u_bot_hublevel = bot_hublevel(u);
+      if (u->bot && u_bot_hublevel < 999 && u_bot_hublevel > hublevel) {
+        hublevel = u_bot_hublevel;
+      }
+    }
+    ++hublevel;
+  }
+
+  struct bot_addr *bi = NULL;
+  struct userrec *u1 = NULL;
+  char tmp[81] = "";
+
+  userlist = adduser(userlist, handle.c_str(), "none", "-", USER_OP, 1);
+  u1 = get_user_by_handle(userlist, const_cast<char*>(handle.c_str()));
+  bi = (struct bot_addr *) my_calloc(1, sizeof(struct bot_addr));
+  bi->uplink = (char *) my_calloc(1, 1);
+  bi->address = strdup(address.c_str());
+  bi->telnet_port = port;
+  bi->relay_port = port;
+  bi->hublevel = hublevel;
+
+  set_user(&USERENTRY_BOTADDR, u1, bi);
+  simple_snprintf(tmp, sizeof(tmp), "%li %s", (long) now, dcc[idx].nick);
+  set_user(&USERENTRY_ADDED, u1, tmp);
+  dprintf(idx, "Added new hub: %s\n", handle.c_str());
+
+  write_userfile(idx);
+}
+
 static void cmd_nopass(int idx, char *par)
 {
   int cnt = 0;
@@ -4615,6 +4677,7 @@ cmd_t C_dcc[] =
   {"me",		"",	(Function) cmd_me,		NULL, 0},
   {"motd",		"",	(Function) cmd_motd,		NULL, 0},
   {"newleaf",		"n",	(Function) cmd_newleaf,		NULL, HUB},
+  {"newhub",		"a",	(Function) cmd_newhub,		NULL, HUB},
   {"nopass",		"m",	(Function) cmd_nopass,		NULL, HUB},
   {"newpass",		"",	(Function) cmd_newpass,		NULL, 0},
   {"secpass",		"",	(Function) cmd_secpass,		NULL, 0},

+ 83 - 1
src/conf.c

@@ -322,6 +322,12 @@ init_conf()
 //  conf.bots->next = NULL;
   conf.bots = NULL;
   conf.bot = NULL;
+  // If conf_hubs is blank, revert to pack hubs
+  if (strlen(settings.conf_hubs)) {
+    conf.hubs = bd::String(settings.conf_hubs).split(',');
+  } else {
+    conf.hubs = bd::String(settings.hubs).split(',');
+  }
 
   conf.localhub = NULL;
   conf.autocron = 1;
@@ -465,7 +471,7 @@ conf_addbot(const char *nick, const char *ip, const char *host, const char *ip6)
 
 //  bot->pid = checkpid(nick, bot);
 
-  if (settings.hubs && is_hub(bot->nick))
+  if (is_hub(bot->nick))
     bot->hub = 1;
 
   /* not a hub 
@@ -536,6 +542,7 @@ free_conf()
     free(conf.datadir);
   if (conf.homedir)
     free(conf.homedir);
+  conf.hubs.clear();
   init_conf();
 }
 
@@ -602,9 +609,11 @@ readconf(const char *fname, int bits)
     fatal(STR("Cannot read config"), 0);
   }
 
+  conf.hubs.clear();
   free_conf_bots(conf.bots);
 
   bd::String line, option;
+  unsigned short hublevel = 0;
 
   while (stream->tell() < stream->length()) {
     line = stream->getline().chomp().trim();
@@ -654,6 +663,11 @@ readconf(const char *fname, int bits)
         if (str_isdigit(line.c_str()))
           conf.uid = atoi(line.c_str());
 
+      } else if (option == STR("hub")) {
+        if (line.split(' ').length() == 3) {
+          conf.hubs << bd::String::printf("%s %d", line.c_str(), ++hublevel);
+        }
+
       } else {
         putlog(LOG_MISC, "*", STR("Unrecognized config option '%s'"), option.c_str());
 
@@ -687,6 +701,20 @@ readconf(const char *fname, int bits)
 
 char s1_9[3] = "",s1_5[3] = "",s1_1[3] = "";
 
+bool hubSort (bd::String hub1, bd::String hub2) {
+  bd::Array<bd::String> hub1params(static_cast<bd::String>(hub1).split(' '));
+  bd::Array<bd::String> hub2params(static_cast<bd::String>(hub2).split(' '));
+
+  unsigned short hub1level = 99, hub2level = 99;
+  if (hub1params.length() == 4) {
+    hub1level = atoi(static_cast<bd::String>(hub1params[3]).c_str());
+  }
+  if (hub2params.length() == 4) {
+    hub2level = atoi(static_cast<bd::String>(hub2params[3]).c_str());
+  }
+  return hub1level < hub2level;
+}
+
 int
 writeconf(char *filename, int fd, int bits)
 {
@@ -774,6 +802,21 @@ writeconf(char *filename, int fd, int bits)
     comment("");
   }
 
+  if (conf.hubs.length()) {
+    comment("# Hubs this bot will connect to");
+    // Sort hub list by hublevel
+    bd::Array<bd::String> sortedhubs(conf.hubs);
+    std::sort(sortedhubs.begin(), sortedhubs.end(), hubSort);
+
+    for (size_t idx = 0; idx < sortedhubs.length(); ++idx) {
+      bd::Array<bd::String> hubparams(static_cast<bd::String>(sortedhubs[idx]).split(' '));
+      bd::String hubnick(hubparams[0]), address(hubparams[1]);
+      port_t port = atoi(static_cast<bd::String>(hubparams[2]).c_str());
+      *stream << bd::String::printf(STR("! hub %s %s %d\n"), hubnick.c_str(), address.c_str(), port);
+    }
+    comment("");
+  }
+
   comment("# '|' means OR, [] means the enclosed is optional");
   comment("# A '+' in front of HOST means the HOST is ipv6");
   comment("# A '/' in front of BOT will disable that bot.");
@@ -980,6 +1023,45 @@ bin_to_conf(bool error)
   tellconf();
 }
 
+void conf_update_hubs(struct userrec* list) {
+  if (!conf.bot->hub && !conf.bot->localhub) {
+    return;
+  }
+
+  bd::Array<bd::String> hubUsers, oldhubs(conf.hubs);
+
+  // Count how many hubs there are
+  for (struct userrec *u = list; u; u = u->next) {
+    if (bot_hublevel(u) < 999) {
+      hubUsers << u->handle;
+    }
+  }
+
+  conf.hubs.clear();
+  conf.hubs.Reserve(hubUsers.length());
+  for (size_t idx = 0; idx < hubUsers.length(); ++idx) {
+    struct userrec *u = get_user_by_handle(list, const_cast<char*>(static_cast<bd::String>(hubUsers[idx]).c_str()));
+    struct bot_addr *bi = (struct bot_addr *) get_user(&USERENTRY_BOTADDR, u);
+    conf.hubs << bd::String::printf("%s %s %d %d", u->handle, bi->address, bi->telnet_port, bi->hublevel);
+  }
+
+  // Only rewrite binary / notify bots if the hubs changed
+  if (conf.hubs == oldhubs) {
+    return;
+  }
+
+  if (conf.bot->hub || conf.bot->localhub) {
+    /* rewrite our binary */
+    conf_to_bin(&conf, 0, -1);
+
+    /* Now signal all of the old bots with SIGUSR1
+     * They will auto die and determine new localhub, etc..
+     */
+    conf_checkpids(conf.bots);
+    conf_killbot(conf.bots, conf.bot->nick, NULL, SIGUSR1, 1); /* Don't kill me. */
+  }
+}
+
 void conf_add_userlist_bots()
 {
   conf_bot *bot = NULL;

+ 3 - 0
src/conf.h

@@ -6,6 +6,7 @@
 #include "types.h"
 #include "eggdrop.h"
 #include "settings.h"
+#include <bdlib/src/Array.h>
 
 typedef struct conf_net_b {
   char *host;
@@ -28,6 +29,7 @@ typedef struct conf_bot_b {
 } conf_bot;
 
 typedef struct conf_b {
+  bd::Array<bd::String> hubs;
   conf_bot *bots;       /* the list of bots */
   conf_bot *bot;        /* single bot (me) */
   int features;		/* Pack features (take, mdop, beta... etc..) */
@@ -73,5 +75,6 @@ void deluser_removed_bots(conf_bot *, conf_bot *);
 conf_bot *conf_getlocalhub(conf_bot *);
 void conf_setmypid(pid_t);
 void conf_bot_dup(conf_bot *dest, conf_bot *src);
+void conf_update_hubs(struct userrec* list);
 
 #endif /* !_CONF_H */

+ 1 - 2
src/debug.c

@@ -211,8 +211,7 @@ static void got_term(int) __attribute__ ((noreturn));
 
 static void got_term(int z)
 {
-  if (conf.bot->hub)
-    write_userfile(-1);
+  write_userfile(-1);
   fatal("Received SIGTERM", 0);
   exit(1);		/* for GCC noreturn */
 }

+ 10 - 1
src/main.c

@@ -430,7 +430,16 @@ static void dtx_arg(int& argc, char *argv[])
         printf(STR("BuildOS: %s%s%s BuildArch: %s%s%s\n"), BOLD(-1), BUILD_OS, BOLD_END(-1), BOLD(-1), BUILD_ARCH, BOLD_END(-1));
         printf(STR("- http://wraith.botpack.net -\n"));
 #ifdef DEBUG
-	printf(STR("pack: %zu conf: %zu settings_t: %zu prefix: %zu pad: %zu needed padding: %zu\n"), SIZE_PACK, SIZE_CONF, sizeof(settings_t), PREFIXLEN, SIZE_PAD, (16 - ((sizeof(settings_t) - sizeof(settings.padding)) % 16)) % 16);
+	printf(STR("pack: %zu conf: %zu settings_t: %zu prefix: %zu pad: %zu/%zu needed padding: %zu/%zu\n"),
+            SIZE_PACK,
+            SIZE_CONF,
+            SIZE_SETTINGS,
+            PREFIXLEN,
+            SIZE_PAD_PACK,
+            SIZE_PAD,
+            (SIZE_PAD_ALIGN - ((SIZE_PACK - SIZE_PAD_PACK) % SIZE_PAD_ALIGN)) % SIZE_PAD_ALIGN,
+            (SIZE_PAD_ALIGN - ((SIZE_SETTINGS - SIZE_PAD) % SIZE_PAD_ALIGN)) % SIZE_PAD_ALIGN
+        );
 #endif
         if (settings.dynamic_initialized[0]) {
           bin_to_conf();

+ 3 - 5
src/misc.c

@@ -609,9 +609,8 @@ int str_isdigit(const char *str)
  */
 void kill_bot(char *s1, char *s2)
 {
-  if (conf.bot->hub)
-    write_userfile(-1);
-  else
+  write_userfile(-1);
+  if (!conf.bot->hub)
     server_die();
   chatout("*** %s\n", s1);
   botnet_send_chat(-1, conf.bot->nick, s1);
@@ -818,8 +817,7 @@ restart(int idx)
 
   socks->my_close();
 
-  if (conf.bot->hub)
-    write_userfile(idx);
+  write_userfile(idx);
 /*
   if (server_online) {
     do_chanset(NULL, NULL, STR("+inactive"), DO_LOCAL);

+ 8 - 0
src/mod/share.mod/share.c

@@ -533,6 +533,7 @@ share_newuser(int idx, char *par)
       noshare = 0;
       if (conf.bot->hub)
         putlog(LOG_CMDS, "@", "%s: newuser %s %s", dcc[idx].nick, nick, s);
+      write_userfile(-1);
     }
   }
 }
@@ -551,6 +552,7 @@ share_killuser(int idx, char *par)
       shareout_but(idx, "k %s\n", par);
       if (conf.bot->hub)
         putlog(LOG_CMDS, "@", "%s: killuser %s", dcc[idx].nick, par);
+      write_userfile(-1);
     }
     noshare = 0;
   }
@@ -670,6 +672,10 @@ share_change(int idx, char *par)
       }
       noshare = 0;
     }
+
+    if (uet == &USERENTRY_BOTADDR) {
+      write_userfile(-1);
+    }
   }
 }
 
@@ -1444,6 +1450,8 @@ static void share_read_stream(int idx, bd::Stream& stream) {
     Auth::FillUsers();
   }
 
+  write_userfile(-1);
+
   cmdpass_free(old_cmdpass);
 
   checkchans(1);                /* remove marked channels */

+ 8 - 4
src/settings.h

@@ -6,7 +6,7 @@
 #define PREFIXLEN sizeof(SETTINGS_HEADER)
 
 /* !!! THIS MUST BE CHANGED WHEN CHANGING THE PACK STRUCT OR ALGORITHMS !!! */
-#define SETTINGS_VER 2
+#define SETTINGS_VER 3
 
 #define DYNAMIC_HEADER dynamic_initialized
 
@@ -23,8 +23,10 @@ typedef struct settings_struct {
   char salt2[17];
   char dcc_prefix[17];
   char features[17];
+  char pack_padding[7];
   /* -- DYNAMIC -- */
   char dynamic_initialized[17];
+  char conf_hubs[513];
   char bots[1025];
   char uid[17];
   char autocron[17];         /* should the bot auto crontab itself? */
@@ -34,7 +36,7 @@ typedef struct settings_struct {
   char portmin[17];       /* for hubs, the reserved port range for incoming connections */
   char portmax[17];       /* for hubs, the reserved port range for incoming connections */
   /* -- PADDING -- */
-  char padding[14];        // (16 - (sizeof(settings_t) % 16)) % 16]
+  char padding[6];        // (16 - (sizeof(settings_t) % 16)) % 16]
 } settings_t;
 
 #define SALT1 {s1_1[0],s1_1[1],s1_5[0],s1_5[1],s1_8[0],s1_8[1],s1_4[0],s1_9[1],s1_2[0],s1_13[0],s1_6[0],s1_6[1],s1_7[0],s1_7[1],s1_3[0],s1_13[1],s1_16[1],s1_4[1],s1_15[0],s1_10[1],s1_14[0],s1_14[1],s1_12[0],s1_12[1],s1_2[1],s1_3[1],s1_11[0],s1_11[1],s1_10[0],s1_15[1],s1_16[0],s1_9[0],'\0'}
@@ -47,14 +49,16 @@ extern char s1_3[3],s1_2[3],s1_1[3],s2_7[3],s1_9[3],s1_13[3],s1_14[3],s2_2[3],s1
 
 #define SIZE_PACK sizeof(settings.hash) + sizeof(settings.packname) + sizeof(settings.shellhash) + \
 sizeof(settings.owners) + sizeof(settings.hubs) + \
-sizeof(settings.salt1) + sizeof(settings.salt2) + sizeof(settings.dcc_prefix) + sizeof(settings.features)
+sizeof(settings.salt1) + sizeof(settings.salt2) + sizeof(settings.dcc_prefix) + sizeof(settings.features) + sizeof(settings.pack_padding)
 
-#define SIZE_CONF sizeof(settings.dynamic_initialized) + sizeof(settings.bots) + sizeof(settings.uid) + \
+#define SIZE_CONF sizeof(settings.dynamic_initialized) + sizeof(settings.conf_hubs) + sizeof(settings.bots) + sizeof(settings.uid) + \
 sizeof(settings.autocron) + \
 sizeof(settings.username) + sizeof(settings.homedir) + \
 sizeof(settings.portmin) + sizeof(settings.portmin) + sizeof(settings.datadir)
 
+#define SIZE_PAD_ALIGN PREFIXLEN
 #define SIZE_PAD sizeof(settings.padding)
+#define SIZE_PAD_PACK sizeof(settings.pack_padding)
 
 #define SIZE_SETTINGS sizeof(settings_t)
 

+ 8 - 2
src/userrec.c

@@ -476,7 +476,13 @@ void stream_writeuserfile(bd::Stream& stream, const struct userrec *bu, bool old
  */
 int write_userfile(int idx)
 {
-  if (userlist == NULL || !conf.bot->hub)
+  if (!userlist) {
+    return 1;
+  }
+
+  conf_update_hubs(userlist);
+
+  if (!conf.bot->hub)
     return 1;			/* No point in saving userfile */
 
   if (idx >= 0)
@@ -539,7 +545,7 @@ int change_handle(struct userrec *u, char *newh)
   return 1;
 }
 
-struct userrec *adduser(struct userrec *bu, char *handle, char *host, char *pass, flag_t flags, int bot)
+struct userrec *adduser(struct userrec *bu, const char *handle, char *host, char *pass, flag_t flags, int bot)
 {
   struct userrec *u = NULL, *x = NULL;
   int oldshare = noshare;

+ 1 - 1
src/userrec.h

@@ -5,7 +5,7 @@ namespace bd {
   class Stream;
 }
 
-struct userrec *adduser(struct userrec *, char *, char *, char *, flag_t, int);
+struct userrec *adduser(struct userrec *, const char *, char *, char *, flag_t, int);
 void addhost_by_handle(char *, char *);
 void clear_masks(struct maskrec *);
 void clear_userlist(struct userrec *);