Commit | Line | Data |
---|---|---|
805e021f CE |
1 | #!/usr/bin/perl -w |
2 | # real Perl code begins here | |
3 | # | |
4 | # Adapted from Apple's uninstall-devtools.pl (Version 7 for Xcode Tools 1.2) | |
5 | # | |
6 | # BSD License: c.f. <http://www.opensource.org/licenses/bsd-license> | |
7 | # | |
8 | ||
9 | use strict; | |
10 | use File::Basename; | |
11 | ||
12 | use vars qw ($do_nothing $print_donothing_removals $receipts_dir $verbose $noisy_warnings); | |
13 | use vars qw ($suppress_spin $spin_counter $spin_state $spin_slower_downer); | |
14 | use vars qw (%exception_list $gen_dirs @gen_files @rmfiles @rmdirs @rmpkg); | |
15 | ||
16 | #---------------------------------------------------------------------------------------- | |
17 | ||
18 | $do_nothing = 0; | |
19 | $print_donothing_removals = 1; | |
20 | $verbose = 1; | |
21 | $noisy_warnings = 0; | |
22 | ||
23 | # One of rm -rf in this script uses $receipts_dir -- change with care. | |
24 | $receipts_dir = "/Library/Receipts"; | |
25 | ||
26 | %exception_list = ( | |
27 | # '/usr/bin/aklog', '', | |
28 | ); | |
29 | ||
30 | $gen_dirs = { }; | |
31 | #$gen_dirs->{"/"}->{"Library"}->{"OpenAFS"}->{"Tools"} = {}; | |
32 | ||
33 | @gen_files = ( | |
34 | "/var/db/openafs/etc/cacheinfo", | |
35 | "/var/db/openafs/etc/ThisCell", | |
36 | "/var/db/openafs/etc/config/afsd.options", | |
37 | "/var/db/openafs/etc/config/afs.conf", | |
38 | "/var/db/openafs/etc/CellServDB.save", | |
39 | "/var/db/openafs/etc/CellServDB.master.last", | |
40 | "/var/db/openafs/etc/CellServDB", | |
41 | "/var/db/openafs/etc/config/settings.plist", | |
42 | ); | |
43 | ||
44 | #---------------------------------------------------------------------------------------- | |
45 | ||
46 | $| = 1; | |
47 | #if ($do_nothing == 0 && $< != 0) | |
48 | # { | |
49 | # die "ERROR: Must be run with root permissions--prefix command with 'sudo'.\n"; | |
50 | # } | |
51 | ||
52 | sub main | |
53 | { | |
54 | # commandline args: | |
55 | # 0: dir of packages to remove | |
56 | # 1: flag indicating whether to keep package receipts | |
57 | # 2: flag indicating whether to supress spin indicator | |
58 | ||
59 | if (!@ARGV) { | |
60 | use FindBin qw($Bin); | |
61 | @ARGV = ("$Bin/..", 0, 0); | |
62 | } | |
63 | $suppress_spin = defined ($ARGV[2]) && $ARGV[2]; | |
64 | ||
65 | $spin_counter = 0; | |
66 | $spin_state = 0; | |
67 | spin_rate_slow (); | |
68 | ||
69 | pre_print (); | |
70 | print "Uninstalling OpenAFS package:\n\n"; | |
71 | ||
72 | remove_generated_files (); | |
73 | remove_main_packages (); | |
74 | remove_generated_directories (); | |
75 | ||
76 | if ($do_nothing == 0) { | |
77 | my @rmcmd = ('osascript', '-e', "do shell script \"/bin/rm -f @rmfiles; /bin/rmdir @rmdirs; /bin/rm -rf @rmpkg\" with administrator privileges"); | |
78 | system @rmcmd; | |
79 | my $retcode = $? >> 8; | |
80 | if ($retcode != 0) { | |
81 | print_warning ("Warning: There may have been a problem uninstalling\n"); | |
82 | } | |
83 | } | |
84 | ||
85 | pre_print (); | |
86 | print "\nFinished uninstalling.\n"; | |
87 | } | |
88 | ||
89 | sub remove_main_packages | |
90 | { | |
91 | # opendir (DIR, $ARGV[0]) or die "ERROR: package directory $ARGV[0] not found\n"; | |
92 | # my @pkglist = grep /\.pkg$/, readdir DIR; | |
93 | # closedir DIR; | |
94 | my @pkglist = ("org.openafs.OpenAFS-debug", | |
95 | "org.openafs.OpenAFS", | |
96 | "OpenAFS", | |
97 | "OpenAFS-debug-extension", | |
98 | ); | |
99 | ||
100 | foreach (@pkglist) | |
101 | { | |
102 | s/\.pkg$//; | |
103 | my $pkgname = $_; | |
104 | my $bompath = undef; | |
105 | my $infopath = undef; | |
106 | my $pkgname_plist_newpath = "/var/db/receipts/$pkgname.pkg.plist"; | |
107 | my $pkgname_bom_newpath = "/var/db/receipts/$pkgname.pkg.bom"; | |
108 | my $pkgname_plist_fullpath = "/Library/Receipts/$pkgname.pkg/Contents/Info.plist"; | |
109 | my $pkgname_bom_fullpath = "/Library/Receipts/$pkgname.pkg/Contents/Archive.bom"; | |
110 | ||
111 | if (-f $pkgname_plist_newpath && -r $pkgname_plist_newpath && -f $pkgname_bom_newpath && -r $pkgname_bom_newpath) { | |
112 | print_verbose ("Removing package $pkgname.pkg\n"); | |
113 | $bompath = $pkgname_bom_newpath; | |
114 | $infopath = $pkgname_plist_newpath; | |
115 | } else { | |
116 | if (-f $pkgname_plist_fullpath && -r $pkgname_plist_fullpath && -f $pkgname_bom_fullpath && -r $pkgname_bom_fullpath) { | |
117 | print_verbose ("Removing package $pkgname.pkg\n"); | |
118 | $bompath = $pkgname_bom_fullpath; | |
119 | $infopath = $pkgname_plist_fullpath; | |
120 | } else { | |
121 | next; | |
122 | } | |
123 | } | |
124 | ||
125 | next if (!defined ($bompath)); | |
126 | ||
127 | my $bomroot = ""; | |
128 | open (INFO, "$infopath") or next; | |
129 | while (<INFO>) { | |
130 | m/IFPkgFlagDefaultLocation/ or next; | |
131 | $bomroot = <INFO>; | |
132 | } | |
133 | close (INFO); | |
134 | ||
135 | $bomroot =~ />(.*)</; | |
136 | $bomroot = $1; | |
137 | $bomroot =~ s/^\/$//; | |
138 | ||
139 | spin_rate_slow (); | |
140 | ||
141 | open (LSBOM, "/usr/bin/lsbom -l -f -p f '$bompath' |") or next; | |
142 | while (<LSBOM>) | |
143 | { | |
144 | chomp; | |
145 | m#^\.(/.*)$#; | |
146 | next if (!defined ($1) || $1 eq ""); | |
147 | my $filename = $bomroot . $1; | |
148 | ||
149 | remove_a_file ($filename); | |
150 | } | |
151 | close (LSBOM); | |
152 | ||
153 | my $rooth = { }; | |
154 | ||
155 | open (LSBOM, "/usr/bin/lsbom -d -p f '$bompath' |") or next; | |
156 | while (<LSBOM>) | |
157 | { | |
158 | chomp; | |
159 | m#^\.(/.*)$#; | |
160 | next if (!defined ($1) || $1 eq ""); | |
161 | my $directory = $bomroot . $1; | |
162 | if (-d $directory) | |
163 | { | |
164 | $rooth = add_directory_to_tree ($directory, $rooth); | |
165 | } | |
166 | else | |
167 | { | |
168 | if ($noisy_warnings) | |
169 | { | |
170 | print_warning ("Warning: \"$directory\" listed in BOM but not present on system.\n"); | |
171 | } | |
172 | } | |
173 | } | |
174 | close (LSBOM); | |
175 | ||
176 | spin_rate_fast (); | |
177 | remove_empty_directories ($rooth, "/"); | |
178 | ||
179 | if (-d "$receipts_dir/$pkgname.pkg" ) { | |
180 | remove_package_receipts ("$pkgname.pkg") if (!defined ($ARGV[1]) || !$ARGV[1]) ; | |
181 | } else { | |
182 | unshift(@rmfiles, "$bompath"); | |
183 | unshift(@rmfiles, "$infopath"); | |
184 | } | |
185 | } | |
186 | } | |
187 | ||
188 | sub remove_generated_files | |
189 | { | |
190 | foreach (@gen_files) | |
191 | { | |
192 | remove_a_file ($_); | |
193 | } | |
194 | } | |
195 | ||
196 | sub remove_generated_directories | |
197 | { | |
198 | remove_empty_directories ($gen_dirs, "/"); | |
199 | } | |
200 | ||
201 | sub add_directory_to_tree | |
202 | { | |
203 | my $dir = shift; | |
204 | my $rooth = shift; | |
205 | my $p = $rooth; | |
206 | ||
207 | my @pathcomp = split /\//, $dir; | |
208 | ||
209 | progress_point (); | |
210 | foreach (@pathcomp) | |
211 | { | |
212 | my $cur_name = $_; | |
213 | if ($cur_name eq "" || !defined ($cur_name)) | |
214 | { | |
215 | $cur_name = "/"; | |
216 | } | |
217 | if (!defined ($p->{"$cur_name"})) | |
218 | { | |
219 | $p->{$cur_name} = { }; | |
220 | } | |
221 | $p = $p->{$cur_name}; | |
222 | } | |
223 | return $rooth; | |
224 | } | |
225 | ||
226 | sub remove_empty_directories | |
227 | { | |
228 | my $rooth = shift; | |
229 | my $path = shift; | |
230 | my $children = (scalar (keys %{$rooth})); | |
231 | my $dirs_remain = 0; | |
232 | ||
233 | if ($children > 0) | |
234 | { | |
235 | foreach my $dirname (sort keys %{$rooth}) | |
236 | { | |
237 | my $printpath; | |
238 | $printpath = "$path/$dirname"; | |
239 | $printpath =~ s#^/*#/#; | |
240 | remove_empty_directories ($rooth->{$dirname}, "$printpath"); | |
241 | $dirs_remain = 1 if (-d "$printpath"); | |
242 | } | |
243 | } | |
244 | ||
245 | if ($dirs_remain == 0) | |
246 | { | |
247 | maybe_remove_ds_store ("$path"); | |
248 | } | |
249 | ||
250 | remove_a_dir ("$path"); | |
251 | } | |
252 | ||
253 | sub remove_a_file | |
254 | { | |
255 | my $fn = shift; | |
256 | my $dirname = dirname ($fn); | |
257 | my $basename = basename ($fn); | |
258 | my $ufs_rsrc_file = "$dirname/._$basename"; | |
259 | ||
260 | progress_point (); | |
261 | return if (!defined ($fn) || $fn eq ""); | |
262 | ||
263 | # Leave any files that are shared between packages alone. | |
264 | if (defined($exception_list{$fn})) | |
265 | { | |
266 | if ($noisy_warnings) | |
267 | { | |
268 | print_warning ("Warning: file \"$fn\" intentionally not removed, even though it's in the BOM.\n"); | |
269 | } | |
270 | return; | |
271 | } | |
272 | ||
273 | if (! -f $fn && ! -l $fn) | |
274 | { | |
275 | if ($noisy_warnings) | |
276 | { | |
277 | print_warning ("Warning: file \"$fn\" present in BOM but not found on disc.\n"); | |
278 | } | |
279 | return; | |
280 | } | |
281 | ||
282 | if ($do_nothing == 1) | |
283 | { | |
284 | print_donothing ("rm $fn\n"); | |
285 | print_donothing ("rm $ufs_rsrc_file\n") if ( -f $ufs_rsrc_file); | |
286 | } | |
287 | else | |
288 | { | |
289 | unshift(@rmfiles, "$fn"); | |
290 | unshift(@rmfiles, "$ufs_rsrc_file") if ( -f $ufs_rsrc_file); | |
291 | } | |
292 | } | |
293 | ||
294 | sub remove_a_dir | |
295 | { | |
296 | my $dir = shift; | |
297 | ||
298 | progress_point (); | |
299 | return if (!defined ($dir) || $dir eq "" || $dir eq "/" || $dir eq "/usr"); | |
300 | if (! -d $dir) | |
301 | { | |
302 | if ($noisy_warnings) | |
303 | { | |
304 | print_warning ("Warning: directory \"$dir\" present in BOM but not found on disc.\n"); | |
305 | } | |
306 | return; | |
307 | } | |
308 | ||
309 | if ($do_nothing == 1) | |
310 | { | |
311 | print_donothing ("rmdir $dir\n"); | |
312 | } | |
313 | else | |
314 | { | |
315 | push(@rmdirs, "$dir"); | |
316 | } | |
317 | } | |
318 | ||
319 | sub remove_package_receipts | |
320 | { | |
321 | my $pkgname = shift; | |
322 | $pkgname =~ s#/##g; # There shouldn't be any path seps in the pkg name... | |
323 | return if (!defined ($pkgname) || $pkgname eq "" | |
324 | || $pkgname eq "." || $pkgname eq ".."); | |
325 | ||
326 | my $pkgdir = "$receipts_dir/$pkgname"; | |
327 | return if (!defined ($pkgdir) || $pkgdir eq "" || ! -d $pkgdir); | |
328 | ||
329 | push(@rmpkg, "$pkgdir"); | |
330 | } | |
331 | ||
332 | ||
333 | sub maybe_remove_ds_store | |
334 | { | |
335 | my $path = shift; | |
336 | my $filecount = 0; | |
337 | return if (!defined ($path) || $path eq "" || $path eq "/" || $path eq "/usr"); | |
338 | return if (! -f "$path/.DS_Store"); | |
339 | ||
340 | open (LS, "/bin/ls -a '$path' |"); | |
341 | while (<LS>) | |
342 | { | |
343 | chomp; | |
344 | next if (m#^\.$# || m#^\.\.$#); | |
345 | $filecount++; | |
346 | } | |
347 | close (LS); | |
348 | ||
349 | if ($filecount == 1 && -f "$path/.DS_Store") | |
350 | { | |
351 | remove_a_file ("$path/.DS_Store"); | |
352 | } | |
353 | } | |
354 | ||
355 | sub print_donothing | |
356 | { | |
357 | my $msg = shift; | |
358 | return if ($print_donothing_removals != 1); | |
359 | pre_print (); | |
360 | print $msg; | |
361 | } | |
362 | ||
363 | sub print_verbose | |
364 | { | |
365 | my $msg = shift; | |
366 | return if ($verbose != 1); | |
367 | pre_print (); | |
368 | print $msg; | |
369 | } | |
370 | ||
371 | sub print_warning | |
372 | { | |
373 | my $msg = shift; | |
374 | pre_print (); | |
375 | print STDERR $msg; | |
376 | } | |
377 | ||
378 | sub print_error | |
379 | { | |
380 | my $msg = shift; | |
381 | pre_print (); | |
382 | print STDERR $msg; | |
383 | } | |
384 | ||
385 | sub pre_print | |
386 | { | |
387 | print " \b" unless ($suppress_spin); | |
388 | } | |
389 | ||
390 | sub spin_rate_slow | |
391 | { | |
392 | $spin_slower_downer = 150; | |
393 | } | |
394 | ||
395 | sub spin_rate_fast | |
396 | { | |
397 | $spin_slower_downer = 75; | |
398 | } | |
399 | ||
400 | sub progress_point | |
401 | { | |
402 | return if ($suppress_spin); | |
403 | $spin_counter++; | |
404 | if (($spin_counter % $spin_slower_downer) == 0) | |
405 | { | |
406 | my $spin_chars = "|/-\\"; | |
407 | my $c = substr ($spin_chars, $spin_state % 4, 1); | |
408 | $spin_state++; | |
409 | print "\e[7m$c\e[m\b"; | |
410 | } | |
411 | } | |
412 | ||
413 | main (); | |
414 | ||
415 | #---------------------------------------------------------------------------------------- |