]> arthur.barton.de Git - netatalk.git/blob - bin/cnid/cnid_maint.in
Change all references to db3/DB3 to BDB. We now support Berkeley DB
[netatalk.git] / bin / cnid / cnid_maint.in
1 #!@PERL@
2
3 #
4 # cnid_maint: A script to maintain the consistency of CNID databases.
5 #
6 # $Id: cnid_maint.in,v 1.13 2003-01-04 21:41:48 jmarcus Exp $
7 #
8
9 use strict;
10 use Getopt::Std;
11 use vars qw(
12     $APPLE_VOLUMES_FILE
13     $STOP_CMD
14     $START_CMD
15     $PS_CMD
16     $GREP
17     $DB_STAT
18     $DB_RECOVER
19     $DB_VERIFY
20     $VERSION
21     $START_NETATALK
22     $LOCK_FILE
23     $HOLDING_LOCK
24 );
25
26 ## Edit ME
27 $STOP_CMD  = '/usr/local/etc/rc.d/netatalk.sh stop';
28 $START_CMD = '/usr/local/etc/rc.d/netatalk.sh start';
29
30 # This ps command needs to output the following fields in the following order:
31 # USER,PID,PPID,COMMAND
32 # Below is the example of a BSD ps.  A SYSV example is:
33 # /bin/ps -eflouid,pid,ppid,comm
34 $PS_CMD             = '@PS@ -axouser,pid,ppid,command';
35 $DB_STAT            = '@BDB_PATH@bin/db_stat';
36 $DB_RECOVER         = '@BDB_PATH@bin/db_recover';
37 $DB_VERIFY          = '@BDB_PATH@bin/db_verify';
38 $APPLE_VOLUMES_FILE = '@PKGCONFDIR@/AppleVolumes.default';
39 ## End edit section
40
41 $VERSION        = '1.0';
42 $GREP           = '@GREP@';
43 $START_NETATALK = 0;
44 $LOCK_FILE      = tmpdir() . '/cnid_maint.LOCK';
45 $HOLDING_LOCK   = 0;
46
47 sub LOCK_SH { 1 }
48 sub LOCK_EX { 2 }
49 sub LOCK_NB { 4 }
50 sub LOCK_UN { 8 }
51
52 my $opts        = {};
53 my $extra_safe  = 0;
54 my $do_verify   = 0;
55 my $remove_logs = 0;
56
57 getopts('hsvVl', $opts);
58
59 if ($opts->{'v'}) {
60         version();
61         exit(0);
62 }
63 if ($opts->{'h'}) {
64         help();
65         exit(0);
66 }
67 if ($opts->{'s'}) {
68         $extra_safe = 1;
69 }
70 if ($opts->{'V'}) {
71         $do_verify = 1;
72 }
73 if ($opts->{'l'}) {
74         $remove_logs = 1;
75 }
76
77 if ($< != 0) {
78         die "You must be root to run this script.\n";
79 }
80
81 print "Beginning run of CNID DB Maintanence script at "
82     . scalar(localtime) . ".\n\n";
83
84 if (-f $LOCK_FILE) {
85         error(1, "Lock file $LOCK_FILE exists.");
86         end();
87 }
88
89 unless (open(LOCK, ">" . $LOCK_FILE)) {
90         error(2, "Unable to create $LOCK_FILE: $!");
91 }
92 flock(LOCK, LOCK_EX);
93 $HOLDING_LOCK = 1;
94
95 # Check to see if the AppleVolumes.default file exists.  We will use this file
96 # to get a list of database environments to recover.  We will ignore users'
97 # home directories since that could be a monumental under taking.
98 if (!-f $APPLE_VOLUMES_FILE) {
99         error(2, "Unable to locate $APPLE_VOLUMES_FILE");
100 }
101
102 # Use ps to get a list of running afpds.  We will track all afpd PIDs that are
103 # running as root.
104 unless (open(PS, $PS_CMD . " | $GREP afpd | $GREP -v grep |")) {
105         error(2, "Unable to open a pipe to ps: $!");
106 }
107
108 my $children  = 0;
109 my $processes = 0;
110 while (<PS>) {
111         chomp;
112         $processes++;
113         my ($user, $pid, $ppid, $command) = split (/\s+/);
114         if (($user eq "root" && $ppid != 1) || ($user ne "root")) {
115                 $children++;
116         }
117 }
118
119 close(PS);
120
121 if ($children) {
122
123         # We have some children.  We cannot run recovery.
124         error(1,
125                 "Clients are still connected.  Database recovery will not be run at this time."
126         );
127         end();
128 }
129
130 if ($processes) {
131
132         # Shutdown the running afpds.
133         $START_NETATALK = 1;
134         error(0, "Shutting down afpd process...");
135         error(2, "Failed to shutdown afpd")
136             if system($STOP_CMD . ">/dev/null 2>&1");
137 }
138
139 # Now, we parse AppleVolumes.default to get a list of volumes to run recovery
140 # on.
141 unless (open(VOLS, $APPLE_VOLUMES_FILE)) {
142         error(2, "Unable to open $APPLE_VOLUMES_FILE: $!");
143 }
144 flock(VOLS, LOCK_SH);
145
146 my @paths = ();
147 while (<VOLS>) {
148         s/#.*//;
149         s/^\s+//;
150         s/\s+$//;
151         next unless length;
152         my ($path, @options) = split (/\s+/, $_);
153         next if ($path =~ /^~/);
154         my $option = "";
155         foreach $option (@options) {
156
157                 # We need to check for the dbpath option on each volume.  If 
158                 # that option is present, we should use its path instead of 
159                 # the actual volume path.
160                 if ($option =~ /^dbpath:/) {
161                         push @paths, $';
162                 } else {
163                         push @paths, $path;
164                 }
165         }
166 }
167
168 close(VOLS);
169
170 my $path = "";
171 foreach $path (@paths) {
172         my $dbpath = $path . "/.AppleDB";
173         if (!-d $dbpath) {
174                 error(1, "Database environment $dbpath does not exist");
175                 next;
176         }
177         if ($extra_safe) {
178                 error(0,
179                         "Checking database environment $dbpath for open connections..."
180                 );
181                 unless (open(STAT, $DB_STAT . " -h $dbpath -e |")) {
182                         error(1, "Failed to open a pipe to $DB_STAT: $!");
183                         next;
184                 }
185
186                 # Now, check each DB environment for any open connections 
187                 # (db_stat calls them as references).  If a volume has no 
188                 # references, we can do recovery on it.  Only check this option
189                 # if the user wants to play things extra safe.
190                 my $connections = 0;
191                 while (<STAT>) {
192                         chomp;
193                         s/\s//g;
194                         if (/References\.$/) {
195                                 $connections = $`;
196                                 last;
197                         }
198                 }
199
200                 close(STAT);
201
202                 # Print out two different skip messages.  This is just for 
203                 # anality.
204                 if ($connections == 1) {
205                         error(1,
206                                 "Skipping $dbpath since it has one active connection"
207                         );
208                         next;
209                 }
210
211                 if ($connections > 0) {
212                         error(1,
213                                 "Skipping $dbpath since it has $connections active connections"
214                         );
215                         next;
216                 }
217         }
218
219         # Run the db_recover command on the environment.
220         error(0, "Running db_recover on $dbpath");
221         if (system($DB_RECOVER . " -h $dbpath >/dev/null 2>&1")) {
222                 error(1, "Failed to run db_recover on $dbpath");
223                 next;
224         }
225
226         if ($do_verify) {
227                 error(0, "Verifying $dbpath/cnid.db");
228                 if (system($DB_VERIFY . " -q -h $dbpath cnid.db")) {
229                         error(1, "Verification of $dbpath/cnid.db failed");
230                         next;
231                 }
232
233                 error(0, "Verifying $dbpath/devino.db");
234                 if (system($DB_VERIFY . " -q -h $dbpath devino.db")) {
235                         error(1, "Verification of $dbpath/devino.db failed");
236                         next;
237                 }
238
239                 error(0, "Verifying $dbpath/didname.db");
240                 if (system($DB_VERIFY . " -q -h $dbpath didname.db")) {
241                         error(1, "Verification of $dbpath/didname.db failed");
242                         next;
243                 }
244                 if (-f "$dbpath/mangle.db") {
245                         error(0, "Verifying $dbpath/mangle.db");
246                         if (system($DB_VERIFY . " -q -h $dbpath mangle.db")) {
247                                 error(1,
248                                         "Verification of $dbpath/mangle.db failed"
249                                 );
250                                 next;
251                         }
252                 }
253         }
254
255         if ($remove_logs) {
256
257                 # Remove the log files if told to do so.
258                 unless (opendir(DIR, $dbpath)) {
259                         error(1, "Failed to open $dbpath for browsing: $!");
260                         next;
261                 }
262
263                 my $file = "";
264                 while (defined($file = readdir(DIR))) {
265                         if ($file =~ /^log\.\d+$/) {
266                                 error(0, "Removing $dbpath/$file");
267                                 unless (unlink($dbpath . "/" . $file)) {
268                                         error(1,
269                                                 "Failed to remove $dbpath/$file: $!"
270                                         );
271                                         next;
272                                 }
273                         }
274                 }
275
276                 closedir(DIR);
277         }
278
279 }
280
281 end();
282
283 sub tmpdir {
284         my $tmpdir;
285
286         foreach ($ENV{TMPDIR}, "/tmp") {
287                 next unless defined && -d && -w _;
288                 $tmpdir = $_;
289                 last;
290         }
291         $tmpdir = '' unless defined $tmpdir;
292         return $tmpdir;
293 }
294
295 sub error {
296         my ($code, $msg) = @_;
297
298         my $err_types = {
299                 0 => "INFO",
300                 1 => "WARNING",
301                 2 => "ERROR",
302         };
303
304         print $err_types->{$code} . ": " . $msg . "\n";
305
306         end() if ($code == 2);
307 }
308
309 sub end {
310         if ($START_NETATALK) {
311                 error(0, "Restarting Netatalk");
312                 if (system($START_CMD . " >/dev/null 2>&1")) {
313                         print "ERROR: Failed to restart Netatalk\n";
314                 }
315         }
316         if ($HOLDING_LOCK) {
317                 close(LOCK);
318                 unlink($LOCK_FILE);
319         }
320         print "\nRun of CNID DB Maintenance script ended at "
321             . scalar(localtime) . ".\n";
322         exit(0);
323 }
324
325 sub version {
326         print "$0 version $VERSION\n";
327 }
328
329 sub help {
330         print "usage: $0 [-hlsvV]\n";
331         print "\t-h   view this message\n";
332         print "\t-l   remove transaction logs after running recovery\n";
333         print
334             "\t-s   be extra safe in verifying there are no open DB connections\n";
335         print "\t-v   print version and exit\n";
336         print
337             "\t-V   run a verification on all database files after recovery\n";
338 }