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