|
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 } |