forked from jasontibbitts/majordomo
-
Notifications
You must be signed in to change notification settings - Fork 1
/
convertdb.pl
245 lines (195 loc) · 7.11 KB
/
convertdb.pl
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
#!/usr/bin/perl -w
use File::Find;
use DB_File;
use strict;
use Getopt::Std;
my ($cnts, $cntf, %opts, @btrees, @hashes, $dir, $doingwhat);
getopts('dtpc', \%opts);
$cnts = $cntf = 0; # skipped and processed file counter
$| = 1;
# this part is scary because it may be out of synch with Mj2 source code:
@btrees = qw(parser posts register subscribers);
@hashes = qw(aliases bounce dup_id dup_partial dup_sum latchkeys tokens);
$dir = $ARGV[0];
&usage("directory containing list data is required") unless (defined $dir and length $dir);
&usage("$dir is not a directory") unless (-d $dir);
# better stroking for 'just print what needs done' mode
$doingwhat = "Converting";
if ($opts{'p'}) {
$doingwhat = "Would convert";
}
if ($opts{'c'}) {
&usage("only one option switch is allowed") if($opts{'t'} || $opts{'d'});
find(\&cleanup, $dir);
}
elsif ($opts{'t'}) {
&usage("only one option switch is allowed") if($opts{'c'} || $opts{'d'});
find(\&db_to_text, $dir);
}
elsif ($opts{'d'}) {
&usage("only one option switch is allowed") if($opts{'t'} || $opts{'c'});
find(\&text_to_db, $dir);
}
else {
&usage("one option switch of -t|-d|-c is required");
}
print "Done: $cntf files were changed.\n";
print "ERROR: $cnts files were skipped.\n" if($cnts);
exit 0;
########################################################################
# end of main
########################################################################
sub addrcompare {
reverse($_[0]) cmp reverse($_[1]);
}
sub cleanup {
# Only want files starting with _ or X (for auxiliary lists) and ending
# with .T.old or .D.old
return unless $_ =~ /^[X_].*\.[DT]\.old$/;
# ignore permissions and ownership for the file
# ($mode, $uid, $gid) = (stat("$type$name.$ext"))[2,4,5];
if ($opts{'p'}) {
print "rm $File::Find::name\n";
}
else {
print "unlink $File::Find::name\n";
# NOTE: stroking and unlink args are different on purpose!
unlink($_) or die("Cannot unlink $File::Find::name: $!");
}
$cntf++;
} # end of cleanup
sub text_to_db {
my (%db, $db, $dbinfo, $gid, $key, $line, $mode, $name, $status, $type,
$uid, $val);
# Only want files starting with _ or X (for auxiliary lists) and ending
# with .T
return unless $_ =~ /^([X_])(.*)\.T$/;
$type = $1;
$name = $2;
# Get permissions and ownership for the text file
($mode, $uid, $gid) = (stat("$type$name.T"))[2,4,5];
# Use BTree and comparison function if the file is appropriately named
if ($type eq 'X' || grep { $_ eq $name } @btrees) {
print "$doingwhat text->btree $File::Find::name \n";
return if ($opts{'p'});
$dbinfo = new DB_File::BTREEINFO;
$dbinfo->{compare} = \&addrcompare;
}
# Otherwise use a simple hash
elsif (grep { $_ eq $name } @hashes) {
print "$doingwhat text->hash $File::Find::name \n";
return if ($opts{'p'});
$dbinfo = new DB_File::HASHINFO;
}
else {
print "Skipping $File::Find::name \n";
$cnts++;
return;
}
# Create the new database
rename "$type$name.D", "$type$name.D.old";
$db = tie %db, 'DB_File', "$type$name.D", O_RDWR|O_CREAT, 0666, $dbinfo;
die "Can't open database: $!" unless $db;
open TEXT, "<$type$name.T";
# Do the copy
while (defined($line = <TEXT>)) {
chomp $line;
($key, $val) = split("\001", $line, 2);
$status = $db->put($key, $val);
warn "\nProblem! Status = $status" unless $status == 0;
}
close TEXT;
# Move the old database out of the way
rename "$type$name.T", "$type$name.T.old";
# Set the permissions and mode correctly on the new database
chmod($mode, "$type$name.D");
chown($uid, $gid, "$type$name.D");
undef $db; untie %db; undef $dbinfo;
$cntf++;
} # end of text_to_db
sub db_to_text {
my (%db, $db, $dbinfo, $gid, $key, $line, $mode, $name, $status, $type,
$uid, $val);
# Only want files starting with _ or X (for auxiliary lists) and ending
# with .D
return unless $_ =~ /^([X_])(.*)\.D$/;
$type = $1;
$name = $2;
# Get permissions and ownership for the text file
($mode, $uid, $gid) = (stat("$type$name.D"))[2,4,5];
# Use BTree and comparison function if the file is appropriately named
if ($type eq 'X' || grep { $_ eq $name } @btrees) {
print "$doingwhat btree->text $File::Find::name \n";
return if ($opts{'p'});
$dbinfo = new DB_File::BTREEINFO;
$dbinfo->{compare} = \&addrcompare;
}
# Otherwise use a simple hash
elsif (grep { $_ eq $name } @hashes) {
print "$doingwhat hash->text $File::Find::name \n";
return if ($opts{'p'});
$dbinfo = new DB_File::HASHINFO;
}
else {
print "Skipping $File::Find::name \n";
$cnts++;
return;
}
# Open the old database
$db = tie %db, 'DB_File', "$type$name.D", O_RDONLY, 0666, $dbinfo;
die "Can't open database: $!" unless $db;
# Create the new database
rename ("$type$name.T", "$type$name.T.old");
open (TEXT, ">$type$name.T") or
die ("Cannot open $type$name.T: $!");
# Do the copy
while (($key, $val) = each %db)
{
print TEXT "$key\001$val\n" ;
}
close TEXT;
# Move the old database out of the way
rename "$type$name.D", "$type$name.D.old";
# Set the permissions and mode correctly on the new database
chmod($mode, "$type$name.T");
chown($uid, $gid, "$type$name.T");
undef $db; untie %db; undef $dbinfo;
$cntf++;
} # end of db_to_text
sub usage {
print <<EOM;
-----------------------------------------------------------------
convertdb.pl - $cntf files (ERROR: $_[0])
-----------------------------------------------------------------
Usage:
convertdb.pl -t|-d|-c [-p] DIRECTORY
Called as:
$0 @ARGV
If the "-p" switch is used, nothing will happen but a list of files
that WOULD BE converted or deleted (without -p) will be printed.
Exactly one of the switches -t, -d, or -c is required.
This script will change the format of Majordomo 2 databases. It only
operates on files starting with "X" or "_" and ending with ".T", ".D",
".T.old", or ".D.old". The directory given should be the top-level
directory where your Majordomo 2 domains are installed (e.g. the dir
that contains ALIASES, LIB, QUEUE, SITE, and virtual domain dirs).
If the "-t" switch is used, the databases will be converted from
DB_File format to Text format. With the "-d" switch the databases
will be converted from Text format to DB_File format.
If the "-c" switch is used, no conversion will happen but all the old
files left over from either type of conversion will be cleaned up. In
other words, "-c" will delete all [X_]*.[TD].old files in the tree.
If both "-c" and "-p" switches are specified, a list of "rm" commands
will be printed but no action will be taken.
Text files have filename.T extensions, DB_File files have filename.D
extensions. Regardless of which direction you are going (T->D or D->T)
the input files will be renamed with ".old" extensions appended onto
the original file name. Pre-existing files with the same name as an
output file will also be renamed with ".old" extensions. No warnings
are issued about existing files.
This script should be run as root or as the majordomo user.
Back up the target DIRECTORY first or risk total annihilation.
EOM
exit 1;
}
# end of file convertdb.pl