commands/vrehash.cc
changeset 0 6f7a81934006
child 2 b3afb9f1e801
equal deleted inserted replaced
-1:000000000000 0:6f7a81934006
       
     1 
       
     2 // Copyright (C) 1999,2000 Bruce Guenter <bruceg@em.ca>
       
     3 //
       
     4 // This program is free software; you can redistribute it and/or modify
       
     5 // it under the terms of the GNU General Public License as published by
       
     6 // the Free Software Foundation; either version 2 of the License, or
       
     7 // (at your option) any later version.
       
     8 //
       
     9 // This program is distributed in the hope that it will be useful,
       
    10 // but WITHOUT ANY WARRANTY; without even the implied warranty of
       
    11 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
       
    12 // GNU General Public License for more details.
       
    13 //
       
    14 // You should have received a copy of the GNU General Public License
       
    15 // along with this program; if not, write to the Free Software
       
    16 // Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
       
    17 
       
    18 #include <config.h>
       
    19 #include <stdio.h>
       
    20 #include <string.h>
       
    21 #include <sys/stat.h>
       
    22 #include <unistd.h>
       
    23 #include "misc/autodelete.h"
       
    24 #include "fdbuf/fdbuf.h"
       
    25 #include "mystring/mystring.h"
       
    26 #include "misc/maildir.h"
       
    27 #include "config/configrc.h"
       
    28 #include "vpwentry/vpwentry.h"
       
    29 #include "vcommand.h"
       
    30 #include "misc/stat_fns.h"
       
    31 #include "cli/cli.h"
       
    32 #include "cdb++/cdb++.h"
       
    33 
       
    34 const char* cli_program = "vrehash";
       
    35 const char* cli_help_prefix = "Reorganize users directory\n"
       
    36 "Renames user directories in a virtual domain to match the current\n"
       
    37 "partitioning scheme\n";
       
    38 const char* cli_help_suffix = "";
       
    39 const char* cli_args_usage = "";
       
    40 const int cli_args_min = 0;
       
    41 const int cli_args_max = 0;
       
    42 
       
    43 // This program is designed to be run after the sysadmin has changed the
       
    44 // C<user-dir-bits> or C<user-dir-slices> configuration variables.
       
    45 // It creates a new users directory called C<new.users>, where C<users>
       
    46 // is the configured name of the user directory.
       
    47 // It then traverses the password table, creates a new user directory name
       
    48 // for each user, and moves the user's mail directory to the new
       
    49 // directory name, creating any necessary directories as it goes.
       
    50 // Any alias entries in the password table are copied as-is.
       
    51 
       
    52 cli_option cli_options[] = {
       
    53   {0}
       
    54 };
       
    55 
       
    56 // RETURN VALUE
       
    57 //
       
    58 // Returns 1 if any part of the process fails; 0 otherwise.
       
    59 
       
    60 // NOTES
       
    61 //
       
    62 // When the process is completed, a the old users directory will have
       
    63 // been moved to C<backup.users>.
       
    64 // If no errors occurred, you should be able to safely delete this
       
    65 // directory and all its subdirectories.
       
    66 // Check this directory first, though, to ensure that no important files
       
    67 // remain.
       
    68 
       
    69 // WARNINGS
       
    70 //
       
    71 // This program is not particularly careful to clean up after itself if
       
    72 // an error occurs.
       
    73 // If an error occurs, you will have to check the status of the current
       
    74 // directory, the virtual password file, and all the virtual users
       
    75 // subdirectories in both C<users> and C<new.users>.
       
    76 
       
    77 static cdb_reader* in = 0;
       
    78 static cdb_writer* out = 0;
       
    79 static mystring cdbfilename;
       
    80 static mystring tmpfilename;
       
    81 static mystring newuserdir;
       
    82 static mystring backupdir;
       
    83 
       
    84 mystring lock_dir()
       
    85 {
       
    86   struct stat buf;
       
    87   if(stat(".", &buf))
       
    88     return "Error stat'ing the current directory ?!?";
       
    89   if(buf.st_mode & S_ISVTX)
       
    90     return "Directory is already locked";
       
    91   if(chmod(".", buf.st_mode | S_ISVTX))
       
    92     return "Can't lock directory";
       
    93   cdbfilename = password_file + ".cdb";
       
    94   tmpfilename = cdbfilename + ".tmp";
       
    95   in = new cdb_reader(cdbfilename);
       
    96   if(!*in)
       
    97     return "Could not open virtual password table";
       
    98   out = new cdb_writer(tmpfilename, 0600);
       
    99   if(!*out)
       
   100     return "Could not open temporary table exclusively";
       
   101   return "";
       
   102 }
       
   103 
       
   104 mystring unlock_dir()
       
   105 {
       
   106   struct stat buf;
       
   107   if(stat(".", &buf))
       
   108     return "Error stat'ing the current directory ?!?";
       
   109   if(!(buf.st_mode & S_ISVTX))
       
   110     return "Directory was not locked";
       
   111   if(chmod(".", buf.st_mode & ~S_ISVTX))
       
   112     return "Can't unlock directory";
       
   113   if(out) {
       
   114     if(out->end(cdbfilename)) {
       
   115       delete out;
       
   116       return "Error completing the virtual password table";
       
   117     }
       
   118     delete out;
       
   119   }
       
   120   delete in;
       
   121   return "";
       
   122 }
       
   123 
       
   124 bool getvpwent(vpwentry& vpw)
       
   125 {
       
   126   autodelete<datum> d = in->nextrec();
       
   127   if(!d)
       
   128     return false;
       
   129   vpw.from_record(d->key, d->data);
       
   130   return true;
       
   131 }
       
   132 
       
   133 mystring user_dir;
       
   134 
       
   135 mystring translate_one(vpwentry& vpw)
       
   136 {
       
   137   mystring tmp = user_dir;
       
   138   mystring newdir = "./" + newuserdir +
       
   139 		  domain.userdir(vpw.name).right(user_dir.length());
       
   140   mystring vpwdir = "./" + domain.userdir(vpw.name);
       
   141   mystring tmpdir = newdir.left(newdir.find_last('/'));
       
   142   if(mkdirp(tmpdir.c_str(), 0755))
       
   143     return "Error creating a user subdirectory: " + tmpdir;
       
   144   if(rename(vpw.mailbox.c_str(), newdir.c_str()))
       
   145     return "Error moving a user subdirectory." + vpw.mailbox;
       
   146   vpw.mailbox = vpwdir;
       
   147   return "";
       
   148 }
       
   149 
       
   150 mystring translate()
       
   151 {
       
   152   vpwentry vpw;
       
   153   unsigned errors = 0;
       
   154   while(getvpwent(vpw)) {
       
   155     if(!!vpw.mailbox) {
       
   156       mystring response = translate_one(vpw);
       
   157       if(!!response) {
       
   158 	ferr << "vrehash: " << response;
       
   159 	++errors;
       
   160       }
       
   161     }
       
   162     if(!out->put(vpw.name, vpw.to_record())) {
       
   163       return "vrehash: failed to add entry to table";
       
   164       ++errors;
       
   165     }
       
   166   }
       
   167   return "";
       
   168 }
       
   169 
       
   170 mystring renamedirs()
       
   171 {
       
   172   if(rename(user_dir.c_str(), backupdir.c_str()) == -1)
       
   173     return "Unable to make backup copy of user directory";
       
   174   if(rename(newuserdir.c_str(), user_dir.c_str()) == -1)
       
   175     return "Unable to rename new user directory";
       
   176 #if 0
       
   177   if(rmdir(backupdir.c_str()) == -1)
       
   178     return "Unable to remove backup user directory";
       
   179 #endif
       
   180   return "";
       
   181 }
       
   182 
       
   183 bool check_ok(const char* msg, mystring (*fn)())
       
   184 {
       
   185   fout << "vrehash: " << msg << ": ";
       
   186   mystring status = fn();
       
   187   if(!!status) {
       
   188     fout << "failed:\n  " << status << endl;
       
   189     return false;
       
   190   }
       
   191   else {
       
   192     fout << "OK.\n";
       
   193     return true;
       
   194   }
       
   195 }
       
   196 
       
   197 int cli_main(int, char*[])
       
   198 {
       
   199   if(!go_home())
       
   200     return 1;
       
   201 
       
   202   user_dir = config->user_dir();
       
   203   
       
   204   newuserdir = "new." + user_dir;
       
   205   backupdir = "backup." + user_dir;
       
   206 
       
   207   if(is_dir(newuserdir.c_str())) {
       
   208     ferr << "vrehash: error: new users directory '" << newuserdir
       
   209 	 << "' already exists.\n";
       
   210     return 1;
       
   211   }
       
   212   if(is_dir(backupdir.c_str())) {
       
   213     ferr << "vrehash: error: backup directory '" << backupdir
       
   214 	 << "' already exists.\n";
       
   215     return 1;
       
   216   }
       
   217   
       
   218   if(!check_ok("locking directory", lock_dir))
       
   219     return 1;
       
   220 
       
   221   if(!check_ok("translating paths and making directories", translate))
       
   222     return 1;
       
   223 
       
   224   if(!check_ok("removing old user directory", renamedirs))
       
   225     return 1;
       
   226   
       
   227   if(!check_ok("unlocking directory", unlock_dir))
       
   228     return 1;
       
   229   
       
   230   return 0;
       
   231 }