commands/vrehash.cc
changeset 0 6f7a81934006
child 2 b3afb9f1e801
--- /dev/null	Thu Jan 01 00:00:00 1970 +0000
+++ b/commands/vrehash.cc	Wed Jan 16 22:39:43 2008 +0100
@@ -0,0 +1,231 @@
+
+// Copyright (C) 1999,2000 Bruce Guenter <bruceg@em.ca>
+//
+// 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., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
+
+#include <config.h>
+#include <stdio.h>
+#include <string.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include "misc/autodelete.h"
+#include "fdbuf/fdbuf.h"
+#include "mystring/mystring.h"
+#include "misc/maildir.h"
+#include "config/configrc.h"
+#include "vpwentry/vpwentry.h"
+#include "vcommand.h"
+#include "misc/stat_fns.h"
+#include "cli/cli.h"
+#include "cdb++/cdb++.h"
+
+const char* cli_program = "vrehash";
+const char* cli_help_prefix = "Reorganize users directory\n"
+"Renames user directories in a virtual domain to match the current\n"
+"partitioning scheme\n";
+const char* cli_help_suffix = "";
+const char* cli_args_usage = "";
+const int cli_args_min = 0;
+const int cli_args_max = 0;
+
+// This program is designed to be run after the sysadmin has changed the
+// C<user-dir-bits> or C<user-dir-slices> configuration variables.
+// It creates a new users directory called C<new.users>, where C<users>
+// is the configured name of the user directory.
+// It then traverses the password table, creates a new user directory name
+// for each user, and moves the user's mail directory to the new
+// directory name, creating any necessary directories as it goes.
+// Any alias entries in the password table are copied as-is.
+
+cli_option cli_options[] = {
+  {0}
+};
+
+// RETURN VALUE
+//
+// Returns 1 if any part of the process fails; 0 otherwise.
+
+// NOTES
+//
+// When the process is completed, a the old users directory will have
+// been moved to C<backup.users>.
+// If no errors occurred, you should be able to safely delete this
+// directory and all its subdirectories.
+// Check this directory first, though, to ensure that no important files
+// remain.
+
+// WARNINGS
+//
+// This program is not particularly careful to clean up after itself if
+// an error occurs.
+// If an error occurs, you will have to check the status of the current
+// directory, the virtual password file, and all the virtual users
+// subdirectories in both C<users> and C<new.users>.
+
+static cdb_reader* in = 0;
+static cdb_writer* out = 0;
+static mystring cdbfilename;
+static mystring tmpfilename;
+static mystring newuserdir;
+static mystring backupdir;
+
+mystring lock_dir()
+{
+  struct stat buf;
+  if(stat(".", &buf))
+    return "Error stat'ing the current directory ?!?";
+  if(buf.st_mode & S_ISVTX)
+    return "Directory is already locked";
+  if(chmod(".", buf.st_mode | S_ISVTX))
+    return "Can't lock directory";
+  cdbfilename = password_file + ".cdb";
+  tmpfilename = cdbfilename + ".tmp";
+  in = new cdb_reader(cdbfilename);
+  if(!*in)
+    return "Could not open virtual password table";
+  out = new cdb_writer(tmpfilename, 0600);
+  if(!*out)
+    return "Could not open temporary table exclusively";
+  return "";
+}
+
+mystring unlock_dir()
+{
+  struct stat buf;
+  if(stat(".", &buf))
+    return "Error stat'ing the current directory ?!?";
+  if(!(buf.st_mode & S_ISVTX))
+    return "Directory was not locked";
+  if(chmod(".", buf.st_mode & ~S_ISVTX))
+    return "Can't unlock directory";
+  if(out) {
+    if(out->end(cdbfilename)) {
+      delete out;
+      return "Error completing the virtual password table";
+    }
+    delete out;
+  }
+  delete in;
+  return "";
+}
+
+bool getvpwent(vpwentry& vpw)
+{
+  autodelete<datum> d = in->nextrec();
+  if(!d)
+    return false;
+  vpw.from_record(d->key, d->data);
+  return true;
+}
+
+mystring user_dir;
+
+mystring translate_one(vpwentry& vpw)
+{
+  mystring tmp = user_dir;
+  mystring newdir = "./" + newuserdir +
+		  domain.userdir(vpw.name).right(user_dir.length());
+  mystring vpwdir = "./" + domain.userdir(vpw.name);
+  mystring tmpdir = newdir.left(newdir.find_last('/'));
+  if(mkdirp(tmpdir.c_str(), 0755))
+    return "Error creating a user subdirectory: " + tmpdir;
+  if(rename(vpw.mailbox.c_str(), newdir.c_str()))
+    return "Error moving a user subdirectory." + vpw.mailbox;
+  vpw.mailbox = vpwdir;
+  return "";
+}
+
+mystring translate()
+{
+  vpwentry vpw;
+  unsigned errors = 0;
+  while(getvpwent(vpw)) {
+    if(!!vpw.mailbox) {
+      mystring response = translate_one(vpw);
+      if(!!response) {
+	ferr << "vrehash: " << response;
+	++errors;
+      }
+    }
+    if(!out->put(vpw.name, vpw.to_record())) {
+      return "vrehash: failed to add entry to table";
+      ++errors;
+    }
+  }
+  return "";
+}
+
+mystring renamedirs()
+{
+  if(rename(user_dir.c_str(), backupdir.c_str()) == -1)
+    return "Unable to make backup copy of user directory";
+  if(rename(newuserdir.c_str(), user_dir.c_str()) == -1)
+    return "Unable to rename new user directory";
+#if 0
+  if(rmdir(backupdir.c_str()) == -1)
+    return "Unable to remove backup user directory";
+#endif
+  return "";
+}
+
+bool check_ok(const char* msg, mystring (*fn)())
+{
+  fout << "vrehash: " << msg << ": ";
+  mystring status = fn();
+  if(!!status) {
+    fout << "failed:\n  " << status << endl;
+    return false;
+  }
+  else {
+    fout << "OK.\n";
+    return true;
+  }
+}
+
+int cli_main(int, char*[])
+{
+  if(!go_home())
+    return 1;
+
+  user_dir = config->user_dir();
+  
+  newuserdir = "new." + user_dir;
+  backupdir = "backup." + user_dir;
+
+  if(is_dir(newuserdir.c_str())) {
+    ferr << "vrehash: error: new users directory '" << newuserdir
+	 << "' already exists.\n";
+    return 1;
+  }
+  if(is_dir(backupdir.c_str())) {
+    ferr << "vrehash: error: backup directory '" << backupdir
+	 << "' already exists.\n";
+    return 1;
+  }
+  
+  if(!check_ok("locking directory", lock_dir))
+    return 1;
+
+  if(!check_ok("translating paths and making directories", translate))
+    return 1;
+
+  if(!check_ok("removing old user directory", renamedirs))
+    return 1;
+  
+  if(!check_ok("unlocking directory", unlock_dir))
+    return 1;
+  
+  return 0;
+}