macos-update-readme-20040317
[openafs.git] / src / packaging / RedHat / afsmodname
1 #!/usr/bin/perl
2 # afsmodname - return the name of the AFS module to load
3 # usage: afsmodname [path]
4 # THIS SCRIPT IS UNDER SOURCE CONTROL!
5 # The master copy is /afs/cs/misc/openafs/src/client-support/afsmodname
6
7 use Getopt::Std;
8
9 use strict;
10 use vars qw($modbase $VERSION @TrySyms @AddSyms $Prefix);
11 use vars qw($DEBUG $Mode $OutFile %SymCache);
12
13 $VERSION = '$Revision$';
14 $VERSION =~ s/^\$[a-z]*:?\s*(\S*)\s*\$$/$1/i;
15 $VERSION = 'testing' unless $VERSION;
16 $modbase = '/usr/vice/etc/modload';
17 @TrySyms = qw(__iget iget __iget4 iget4);
18 @AddSyms = qw(sock_create);
19 $Prefix = 'libafs';
20
21
22 sub vcmp {
23   my (@a, @b, @aa, @bb);
24   my ($i, $ii);
25
26   @a = split /-/, $a;
27   @b = split /-/, $b;
28   foreach $i (0 .. ((@a > @b) ? $#b : $#a)) {
29     @aa = split /\./, $a[$i];
30     @bb = split /\./, $b[$i];  
31     foreach $ii (0 .. ((@aa > @bb) ? $#bb : $#aa)) {
32       return $aa[$ii] <=> $bb[$ii] if $aa[$ii] <=> $bb[$ii]
33                                    && $aa[$ii] =~ /^\d+$/
34                                    && $bb[$ii] =~ /^\d+$/;
35       return $aa[$ii] cmp $bb[$ii] if $aa[$ii] cmp $bb[$ii];
36     }
37     return @aa <=> @bb if @aa <=> @bb;
38   }
39   return @a <=> @b;
40 }
41
42
43 sub parse_symbol ($) {
44   my($symbol) = @_;
45
46   if ($symbol =~ /^(.*)_R((?:smp)?(?:2gig)?_?[0-9a-f]{8})$/) {
47     ($1, $2);
48   } else {
49     ($symbol, '--none--');
50   }
51 }
52
53
54 sub get_ksym ($) {
55   my($req_sym) = @_;
56   my($addr, $symbol, $module, $version, @answer);
57
58
59   if (exists($SymCache{$req_sym})) {
60     print STDERR "get_ksym($req_sym) [cached]\n" if $DEBUG > 1;
61     return $SymCache{$req_sym};
62   }
63
64   print STDERR "get_ksym($req_sym)" if $DEBUG;
65   $SymCache{$req_sym} = undef;
66   open(KSYMS, '/proc/ksyms') or die "open /proc/ksyms: $!\n";
67   while (<KSYMS>) {
68     if (/^(\w+)\s+(\w+)\s+\[(.*)\]/) {
69       ($addr, $symbol, $module) = ($1, $2, $3)
70     } elsif (/^(\w+)\s+(\w+)/) {
71       ($addr, $symbol, $module) = ($1, $2, 'KERNEL')
72     } else { next }
73
74     ($symbol, $version) = parse_symbol($symbol);
75
76     if ($symbol eq $req_sym) {
77       $SymCache{$req_sym} = [$addr, $version, $module];
78       print STDERR " => [addr=$addr, vers=$version, mod=$module]\n" if $DEBUG;
79       last;
80     }
81   }
82   close(KSYMS);
83
84   print STDERR " => not found\n" if $DEBUG && !defined($SymCache{$req_sym});
85   $SymCache{$req_sym};
86 }
87
88
89 sub get_modsyms ($) {
90   my($modpath) = @_;
91   my($symbol, $version, $V);
92
93   $V = {};
94   open(NM, "nm $modpath|") or die "nm $modpath: $!\n";
95   while (<NM>) {
96     chomp;
97     next unless /^\s+U\s+/;
98     ($symbol, $version) = parse_symbol($');
99     $$V{$symbol} = $version unless $version eq '--none--';
100   }
101   close(NM);
102   $V;
103 }
104
105
106 sub get_hdrsyms ($) {
107   my($srcpath) = @_;
108   my($moddir, @hdrs, $h);
109   my($symbol, $version, $V);
110
111
112   $moddir = "$srcpath/include/linux/modules";
113   opendir(HDRS, $moddir) or die "$moddir: $!\n";
114   @hdrs = readdir(HDRS);
115   closedir(HDRS);
116
117   $V = {};
118   foreach $h (@hdrs) {
119     next unless $h =~ /\.ver$/;
120     open(HDR, "$moddir/$h") or die "$moddir/$h: $!\n";
121     while (<HDR>) {
122       chomp;
123       next unless /#define __ver_(\S+)\s+(\S+)/;
124       $$V{$1} = $2;
125     }
126     close(HDR);
127   }
128   $V;
129 }
130
131
132 sub get_cputype () {
133   my($cputype, $family, $vendor, $model);
134
135   open(CPUINFO, '/proc/cpuinfo') or die "open /proc/cpuinfo: $!\n";
136   while (<CPUINFO>) {
137     if    (/^cpu\s*\:\s*(\S+)/)       { $cputype = $1 }
138     elsif (/^cpu family\s*:\s*(\S+)/) { $family  = $1 }
139     elsif (/^vendor_id\s*:\s*(\S+)/)  { $vendor  = $1 }
140     elsif (/^model\s*:\s*(\S+)/)      { $model   = $1 }
141   }
142   close(CPUINFO);
143   if    ($vendor eq 'GenuineIntel') { $vendor = 'intel' }
144   elsif ($vendor eq 'AuthenticAMD') { $vendor = 'amd'   }
145   $cputype = "${family}86" if !defined($cputype);
146   [$cputype, $vendor, $model];
147 }
148
149
150 sub table_lookup ($@) {
151   my($cpu, @paths) = @_;
152   my($path, $symbol, $version, $mincpu, @mincpu, $module, $info, @supp);
153   my($prev_module);     # last module line we saw
154   my($match_module);    # last matching module
155   my($prev_match);      # true if last module matches so far
156
157   foreach $path (@paths) {
158     next unless -f $path;
159     $prev_match = 0;
160     open(TABLE, $path) or die "open $path: $!\n";
161     while (<TABLE>) {
162       # Skip comments
163       next if (/^\#/ || /^\s*$/);
164
165       # Check supplemental requirements
166       if (/^\s*\>/) {
167         @supp = split;
168         foreach (@supp) {
169           if (/([^=]*)=([^=]*)/) {
170             ($symbol, $version) = ($1, $2);
171             $info = get_ksym($symbol);
172             $prev_match = 0 if !$info || $version ne $$info[1];
173           }
174         }
175         next;
176       }
177
178       # This is a new module, so all supplemental requirements for the
179       # previous module have been processed.  If they all passed, then
180       # the previous module is a matching module.
181       $match_module = $prev_module if $prev_match;
182
183       # Parse the line and remember the module name
184       ($symbol, $version, $mincpu, $module) = split;
185       $prev_module = $module;
186       $prev_match  = 0;
187       if ($DEBUG) {
188         print STDERR "Try $module ($symbol=$version)",
189                      ($mincpu ne '-') ?  " mincpu = $mincpu" : "",
190                      "\n";
191       }
192
193       # Check mincpu requirement
194       if ($mincpu ne '-') {
195         @mincpu = split(/\./, $mincpu);
196         if ($mincpu[0] ne '' && $mincpu[0] >  $$cpu[0]) {  # min family
197           print STDERR " mincpu failed: $mincpu[0] > $$cpu[0]\n" if $DEBUG;
198           next;
199         }
200         if ($mincpu[1] ne '' && $mincpu[1] ne $$cpu[1]) {  # exact vendor
201           print STDERR " mincpu failed: $mincpu[1] != $$cpu[1]\n" if $DEBUG;
202           next;
203         }
204         if ($mincpu[2] ne '' && $mincpu[2] >  $$cpu[2]) {  # min model
205           print STDERR " mincpu failed: $mincpu[2] > $$cpu[2]\n" if $DEBUG;
206           next;
207         }
208       }
209
210       # Check primary symbol requirement
211       $info = get_ksym($symbol);
212       next unless $info;
213       next unless $version eq $$info[1];
214
215       # OK; it's a match so far.  There may still be some supplemental
216       # requirements that we need to check.
217       $prev_match = 1;
218     }
219     close(TABLE);
220     $match_module = $prev_module if $prev_match;
221   }
222   $match_module;
223 }
224
225
226 sub dump_versions ($) {
227   my($cpu) = @_;
228   my($version);
229
230   print STDERR "CPU Type:       ", join('.', @$cpu), "\n";
231
232   chomp($version = `uname -rv`);
233   print STDERR "Linux version:  $version\n";
234
235   if (open(RHR, "/etc/redhat-release")) {
236     chomp($version = <RHR>);
237      print STDERR "RedHat release: $version\n";
238   }
239 }
240
241
242 sub dump_syms (@) {
243   my(@syms) = @_;
244   my($sym, $info);
245
246   print STDERR "Symbol versions:\n";
247   foreach $sym (@syms) {
248     $info = get_ksym($sym);
249     printf STDERR "  %-10s %s\n", $sym, $$info[1] if $info;
250   }
251 }
252
253
254 sub gen_table (@) {
255   my(@modules) = @_;
256   my($module, $modname, $V, $sym, $count, @add);
257
258   print <<'EOF';
259 # This file describes the available AFS kernel modules and what kernel
260 # versions they work with.  Each line matches against some kernel symbol
261 # version, and specifies a module which may be used with kernels containing
262 # that version of the specified symbol.  Only lines which match the
263 # currently-running kernel are considered.
264 #
265 # In addition, each line may specify a minimum CPU model on which the module
266 # will work.  If this value is present, the actual CPU model must be greater
267 # than or equal to the version specified; otherwise, the module is assumed
268 # to work on any CPU.
269 #
270 # The last match found will be used.
271 #
272 # Symbol  Version       MinCPU  Module
273 #=======  ============  ======  ====================
274 EOF
275   foreach $module (sort vcmp @modules) {
276     ($modname = $module) =~ s/.*\///;
277     $modname =~ s/^$Prefix[-.](.*)\.o$/$1/;
278     $V = get_modsyms($module);
279     $count = 0;
280     foreach $sym (@TrySyms) {
281       next unless exists $$V{$sym};
282       $count++;
283       printf "%-8s  %-12s  %-6s  %s\n", $sym, $$V{$sym}, '-', $modname;
284       last;
285     }
286     if (!$count) {
287       print STDERR "Unable to find a suitable symbol reference in $modname!\n";
288       next;
289     }
290     @add = ();
291     foreach $sym (@AddSyms) {
292       next unless exists $$V{$sym};
293       push(@add, "$sym=$$V{$sym}");
294     }
295     print "> ", join(' ', @add), "\n" if @add;
296   }
297 }
298
299
300 sub scan_kernels (@) {
301   my(@kernels) = @_;
302   my($kernel, $kpath, $kname, $V);
303
304 eval <<"EOF";
305 format =
306 @<<<<<<<<<<<<<<<<<<<<<<<< @{[' @<<<<<<<<<<<' x scalar(@TrySyms)]}
307 \$kname, @{[join(',', map(q/$$V{'/ . $_ . q/'}/, @TrySyms))]}
308 .
309 EOF
310
311   $kname = 'Kernel';
312   $V = { map(($_ => $_), @TrySyms) };
313   write;
314
315   $kname = '=========================';
316   $V = { map(($_ => '============'), @TrySyms) };
317   write;
318
319   foreach $kernel (@kernels) {
320     if    (-d "$kernel/src/include/linux/modules") { $kpath = "$kernel/src" }
321     elsif (-d "$kernel/include/linux/modules")     { $kpath = $kernel       }
322     else { next }
323     ($kname = $kpath) =~ s#/src$##;
324     $kname =~ s/.*\///;
325
326     $V = get_hdrsyms($kpath);
327     write;
328   }
329 }
330
331
332 sub symcompare ($$) {
333   my($module, $kernel) = @_;
334   my($ksyms, $msyms, $sym, $kvers, $mvers, $info);
335
336 eval <<'EOF';
337 format =
338 @<<<<<<<<<<<<<<<<<<<<<<<<<<<<<  @<<<<<<<<<<<  @<<<<<<<<<<<
339 $sym, $kvers, $mvers
340 .
341 EOF
342
343   if (defined($kernel)) { $ksyms = get_hdrsyms($kernel) }
344   $msyms = get_modsyms($module);
345
346   print "Symbol                          Kernel        Module\n";
347   print "==============================  ============  ============\n";
348   foreach (keys %$msyms) {
349     $sym = $_;
350     $mvers = $$msyms{$sym};
351     if (defined($kernel)) {
352       $kvers = $$ksyms{$sym};
353     } else {
354       $info = get_ksym($sym);
355       $kvers = $$info[1];
356     }
357     next if $kvers eq $mvers;
358     write;
359   }
360 }
361
362
363 sub usage (;@) {
364
365   print STDERR "$00: ", @_, "\n" if @_;
366   print STDERR <<"EOF";
367 usage: $00 [opts] [modbase]                      (find module)
368        $00 [opts] -g modules ...                 (make table)
369        $00 [opts] -k kernels ...                 (scan kernels)
370        $00 [opts] -c module [kernel]             (check module)
371        $00 -h                                    (print help)
372        $00 -v                                    (print version)
373
374 options:
375   -d          enable debugging output
376   -f outfile  set output file (default stdout)
377   -P prefix   set module name prefix (default $Prefix)
378   -S syms...  symbols to try for -x, -k (default @TrySyms)
379   -A syms...  additional symbols to check for -x (default @AddSyms)
380
381   Module lists for -S and -A may be space- or comma-separated.
382   For backward-compatibility, -g is a synonym for -x.
383 EOF
384     exit(0);
385 }
386
387 sub parse_opts () {
388   my(%opts);
389
390   ($00 = $0) =~ s/.*\///;
391   getopts('dckgxf:hvP:S:A:', \%opts) or usage('invalid option(s)');
392   usage() if $opts{'h'};
393
394   if ($opts{'v'}) {
395     print "afsmodname $VERSION\n";
396     exit(0);
397   }
398
399   $opts{'g'} = 1 if $opts{'x'};
400   if ($opts{'g'} + $opts{'k'} + $opts{'c'} > 1) {
401     usage("At most one of -g, -k, -c can be used\n");
402   }
403
404   $DEBUG++                              if exists $opts{'d'};
405   $Mode = 'g'                           if exists $opts{'g'};
406   $Mode = 'k'                           if exists $opts{'k'};
407   $Mode = 'c'                           if exists $opts{'c'};
408
409   usage("Too many arguments")   if !$Mode && @ARGV > 1;
410   usage("Too many arguments")   if $Mode eq 'c' && @ARGV > 2;
411   usage("Module name required") if $Mode eq 'c' && !@ARGV;
412
413   $OutFile = $opts{'f'}                 if exists $opts{'f'};
414   $Prefix  = $opts{'p'}                 if exists $opts{'P'};
415   @TrySyms = split(/[, ]+/, $opts{'S'}) if exists $opts{'S'};
416   @AddSyms = split(/[, ]+/, $opts{'A'}) if exists $opts{'A'};
417 }
418
419
420 ## MAIN PROGRAM
421
422 my($cpu, $module);
423
424 parse_opts();
425 if ($Mode) {
426   if ($OutFile) {
427     open(STDOUT, ">$OutFile") or die "$OutFile: $!\n";
428   }
429   if ($Mode eq 'g') { gen_table(@ARGV)               }
430   if ($Mode eq 'k') { scan_kernels(@ARGV)            }
431   if ($Mode eq 'c') { symcompare($ARGV[0], $ARGV[1]) }
432   exit(0);
433 }
434
435 $modbase = $ARGV[0] if @ARGV;
436
437 $cpu = get_cputype();
438
439 $module = table_lookup($cpu, "$modbase/SymTable", "$modbase/SymTable.local");
440
441 if ($module) {
442   print "$Prefix-$module.o";
443   exit(0);
444 }
445
446 print STDERR <<'EOF';
447 Hmm...  I can't seem to find an AFS kernel module suitable for your Linux
448 kernel.  That means you will need to build or obtain a suitable module.
449 The following information may be of some use in obtaining assistance:
450 EOF
451
452 dump_versions($cpu);
453 dump_syms(sort (@TrySyms, keys %SymCache));
454
455 exit(1);