1#
2# This is not a runnable script, it is a Perl module, a collection of variables, subroutines, etc.
3# To get help about exported variables and subroutines, execute the following command:
4#
5#     perldoc Uname.pm
6#
7# or see POD (Plain Old Documentation) embedded to the source...
8#
9#
10#//===----------------------------------------------------------------------===//
11#//
12#// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
13#// See https://llvm.org/LICENSE.txt for license information.
14#// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
15#//
16#//===----------------------------------------------------------------------===//
17#
18
19package Uname;
20
21use strict;
22use warnings;
23use warnings::register;
24use Exporter;
25
26use POSIX;
27use File::Glob ":glob";
28use Net::Domain qw{};
29
30# Following code does not work with Perl 5.6 on Linux* OS and Windows* OS:
31#
32#     use if $^O eq "darwin", tools => qw{};
33#
34# The workaround for Perl 5.6:
35#
36BEGIN {
37    if ( $^O eq "darwin" or $^O eq "linux" ) {
38	require tools;
39        import tools;
40    }; # if
41    if ( $^O eq "MSWin32" ) {
42        require Win32;
43    }; # if
44}; # BEGIN
45
46my $mswin = qr{\A(?:MSWin32|Windows_NT)\z};
47
48my @posix = qw{ kernel_name fqdn kernel_release kernel_version machine };
49    # Properties supported by POSIX::uname().
50my @linux =
51    qw{ processor hardware_platform operating_system };
52    # Properties reported by uname in Linux* OS.
53my @base = ( @posix, @linux );
54    # Base properties.
55my @aux =
56    (
57        qw{ host_name domain_name },
58        map( "operating_system_$_", qw{ name release codename description } )
59    );
60    # Auxiliary properties.
61my @all = ( @base, @aux );
62    # All the properties.
63my @meta = qw{ base_names all_names value };
64    # Meta functions.
65
66our $VERSION     = "0.07";
67our @ISA         = qw{ Exporter };
68our @EXPORT      = qw{};
69our @EXPORT_OK   = ( @all, @meta );
70our %EXPORT_TAGS =
71    (
72        base => [ @base ],
73        all  => [ @all  ],
74        meta => [ @meta ],
75    );
76
77my %values;
78    # Hash of values. Some values are strings, some may be references to code which should be
79    # evaluated to get real value. This trick is implemented because call to Net::Domain::hostfqdn()
80    # is relatively slow.
81
82# Get values from POSIX::uname().
83@values{ @posix } = POSIX::uname();
84
85# On some systems POSIX::uname() returns "short" node name (without domain name). To be consistent
86# on all systems, we will get node name from alternative source.
87if ( $^O =~ m/cygwin/i ) {
88    # Function from Net::Domain module works well, but on Cygwin it prints to
89    # stderr "domainname: not found". So we will use environment variables for now.
90    $values{ fqdn } = lc( $ENV{ COMPUTERNAME } . "." . $ENV{ USERDNSDOMAIN } );
91} else {
92    # On systems other than Cygwin, let us use Net::Domain::hostfqdn(), but do it only node name
93    # is really requested.
94    $values{ fqdn } =
95        sub {
96            my $fqdn = Net::Domain::hostfqdn(); # "fqdn" stands for "fully qualified domain name".
97            # On some systems POSIX::uname() and Net::Domain::hostfqdn() reports different names.
98            # Let us issue a warning if they significantly different. Names are insignificantly
99            # different if POSIX::uname() matches the beginning of Net::Domain::hostfqdn().
100            if (
101                $fqdn eq substr( $fqdn, 0, length( $fqdn ) )
102                &&
103                (
104                    length( $fqdn ) == length( $fqdn )
105                    ||
106                    substr( $fqdn, length( $fqdn ), 1 ) eq "."
107                )
108            ) {
109                # Ok.
110            } else {
111                warnings::warnif(
112                    "POSIX::uname() and Net::Domain::hostfqdn() reported different names: " .
113                        "\"$values{ fqdn }\" and \"$fqdn\" respectively\n"
114                );
115            }; # if
116            return $fqdn;
117        }; # sub
118}; # if
119
120if ( $^O =~ $mswin ) {
121    if (
122        $values{ machine } =~ m{\A(?:x86|[56]86)\z}
123        and
124        exists( $ENV{ PROCESSOR_ARCHITECTURE } ) and $ENV{ PROCESSOR_ARCHITECTURE } eq "x86"
125        and
126        exists( $ENV{ PROCESSOR_ARCHITEW6432 } )
127    ) {
128        if ( $ENV{ PROCESSOR_ARCHITEW6432 } eq "AMD64" ) {
129            $values{ machine } = "x86_64";
130        }; # if
131    }; # if
132}; # if
133
134# Some values are not returned by POSIX::uname(), let us compute them.
135
136# processor.
137$values{ processor } = $values{ machine };
138
139# hardware_platform.
140if ( 0 ) {
141} elsif ( $^O eq "linux" or $^O eq "freebsd" or $^O eq "netbsd" ) {
142    if ( 0 ) {
143    } elsif ( $values{ machine } =~ m{\Ai[3456]86\z} ) {
144        $values{ hardware_platform } = "i386";
145    } elsif ( $values{ machine } =~ m{\A(x86_64|amd64)\z} ) {
146        $values{ hardware_platform } = "x86_64";
147    } elsif ( $values{ machine } =~ m{\Aarmv7\D*\z} ) {
148        $values{ hardware_platform } = "arm";
149    } elsif ( $values{ machine } =~ m{\Appc64le\z} ) {
150        $values{ hardware_platform } = "ppc64le";
151    } elsif ( $values{ machine } =~ m{\Appc64\z} ) {
152        $values{ hardware_platform } = "ppc64";
153    } elsif ( $values{ machine } =~ m{\Aaarch64\z} ) {
154        $values{ hardware_platform } = "aarch64";
155    } elsif ( $values{ machine } =~ m{\Amips64\z} ) {
156        $values{ hardware_platform } = "mips64";
157    } elsif ( $values{ machine } =~ m{\Amips\z} ) {
158        $values{ hardware_platform } = "mips";
159    } elsif ( $values{ machine } =~ m{\Ariscv64\z} ) {
160        $values{ hardware_platform } = "riscv64";
161    } else {
162        die "Unsupported machine (\"$values{ machine }\") returned by POSIX::uname(); stopped";
163    }; # if
164} elsif ( $^O eq "darwin" ) {
165    if ( 0 ) {
166    } elsif ( $values{ machine } eq "x86" or $values{ machine } eq "i386" ) {
167        $values{ hardware_platform } =
168            sub {
169                my $platform = "i386";
170                # Some OSes on Intel(R) 64 still reports "i386" machine. Verify it by using
171                # the value returned by 'sysctl -n hw.optional.x86_64'. On Intel(R) 64-bit systems the
172                # value == 1; on 32-bit systems the 'hw.optional.x86_64' property either does not exist
173                # or the value == 0. The path variable does not contain a path to sysctl when
174                # started by crontab.
175                my $sysctl = ( which( "sysctl" ) or "/usr/sbin/sysctl" );
176                my $output;
177                debug( "Executing $sysctl..." );
178                execute( [ $sysctl, "-n", "hw.optional.x86_64" ], -stdout => \$output, -stderr => undef );
179                chomp( $output );
180                if ( 0 ) {
181                } elsif ( "$output" eq "" or "$output" eq "0" ) {
182                    $platform = "i386";
183                } elsif ( "$output" eq "1" ) {
184                    $platform = "x86_64";
185                } else {
186                    die "Unsupported value (\"$output\") returned by \"$sysctl -n hw.optional.x86_64\"; stopped";
187                }; # if
188                return $platform;
189            }; # sub {
190    } elsif ( $values{ machine } eq "x86_64" ) {
191	# Some OS X* versions report "x86_64".
192	$values{ hardware_platform } = "x86_64";
193    } else {
194        die "Unsupported machine (\"$values{ machine }\") returned by POSIX::uname(); stopped";
195    }; # if
196} elsif ( $^O =~ $mswin ) {
197    if ( 0 ) {
198    } elsif ( $values{ machine } =~ m{\A(?:x86|[56]86)\z} ) {
199        $values{ hardware_platform } = "i386";
200    } elsif ( $values{ machine } eq "x86_64" or $values{ machine } eq "amd64" ) {
201        # ActivePerl for IA-32 architecture returns "x86_64", while ActivePerl for Intel(R) 64 returns "amd64".
202        $values{ hardware_platform } = "x86_64";
203    } else {
204        die "Unsupported machine (\"$values{ machine }\") returned by POSIX::uname(); stopped";
205    }; # if
206} elsif ( $^O eq "cygwin" ) {
207    if ( 0 ) {
208    } elsif ( $values{ machine } =~ m{\Ai[3456]86\z} ) {
209        $values{ hardware_platform } = "i386";
210    } elsif ( $values{ machine } eq "x86_64" ) {
211        $values{ hardware_platform } = "x86_64";
212    } else {
213        die "Unsupported machine (\"$values{ machine }\") returned by POSIX::uname(); stopped";
214    }; # if
215} else {
216    die "Unsupported OS (\"$^O\"); stopped";
217}; # if
218
219# operating_system.
220if ( 0 ) {
221} elsif ( $values{ kernel_name } eq "Linux" ) {
222    $values{ operating_system } = "GNU/Linux";
223    my $release;    # Name of chosen "*-release" file.
224    my $bulk;       # Content of release file.
225    # On Ubuntu, lsb-release is quite informative, e. g.:
226    #     DISTRIB_ID=Ubuntu
227    #     DISTRIB_RELEASE=9.04
228    #     DISTRIB_CODENAME=jaunty
229    #     DISTRIB_DESCRIPTION="Ubuntu 9.04"
230    # Try lsb-release first. But on some older systems lsb-release is not informative.
231    # It may contain just one line:
232    #     LSB_VERSION="1.3"
233    $release = "/etc/lsb-release";
234    if ( -e $release ) {
235        $bulk = read_file( $release );
236    } else {
237        $bulk = "";
238    }; # if
239    if ( $bulk =~ m{^DISTRIB_} ) {
240        # Ok, this lsb-release is informative.
241        $bulk =~ m{^DISTRIB_ID\s*=\s*(.*?)\s*$}m
242            or runtime_error( "$release: There is no DISTRIB_ID:", $bulk, "(eof)" );
243        $values{ operating_system_name } = $1;
244        $bulk =~ m{^DISTRIB_RELEASE\s*=\s*(.*?)\s*$}m
245            or runtime_error( "$release: There is no DISTRIB_RELEASE:", $bulk, "(eof)" );
246        $values{ operating_system_release } = $1;
247        $bulk =~ m{^DISTRIB_CODENAME\s*=\s*(.*?)\s*$}m
248            or runtime_error( "$release: There is no DISTRIB_CODENAME:", $bulk, "(eof)" );
249        $values{ operating_system_codename } = $1;
250        $bulk =~ m{^DISTRIB_DESCRIPTION\s*="?\s*(.*?)"?\s*$}m
251            or runtime_error( "$release: There is no DISTRIB_DESCRIPTION:", $bulk, "(eof)" );
252        $values{ operating_system_description } = $1;
253    } else {
254        # Oops. lsb-release is missed or not informative. Try other *-release files.
255        $release = "/etc/system-release";
256        if ( not -e $release ) {    # Use /etc/system-release" if such file exists.
257            # Otherwise try other "/etc/*-release" files, but ignore "/etc/lsb-release".
258            my @releases = grep( $_ ne "/etc/lsb-release", bsd_glob( "/etc/*-release" ) );
259            # On some Fedora systems there are two files: fedora-release and redhat-release
260            # with identical content. If fedora-release present, ignore redjat-release.
261            if ( grep( $_ eq "/etc/fedora-release", @releases ) ) {
262                @releases = grep( $_ ne "/etc/redhat-release", @releases );
263            }; # if
264            if ( @releases == 1 ) {
265                $release = $releases[ 0 ];
266            } else {
267                if ( @releases == 0 ) {
268                    # No *-release files found, try debian_version.
269                    $release = "/etc/debian_version";
270                    if ( not -e $release ) {
271                        $release = undef;
272                        warning( "No release files found in \"/etc/\" directory." );
273                    }; # if
274                } else {
275                    $release = undef;
276                    warning( "More than one release files found in \"/etc/\" directory:", @releases );
277                }; # if
278            }; # if
279        }; # if
280        if ( defined( $release ) ) {
281            $bulk = read_file( $release );
282            if ( $release =~ m{system|redhat|fedora} ) {
283                # Red Hat or Fedora. Parse the first line of file.
284                # Typical values of *-release (one of):
285                #     Red Hat Enterprise Linux* OS Server release 5.2 (Tikanga)
286                #     Red Hat Enterprise Linux* OS AS release 3 (Taroon Update 4)
287                #     Fedora release 10 (Cambridge)
288                $bulk =~ m{\A(.*)$}m
289                    or runtime_error( "$release: Cannot find the first line:", $bulk, "(eof)" );
290                my $first_line = $1;
291                $values{ operating_system_description } = $first_line;
292                $first_line =~ m{\A(.*?)\s+release\s+(.*?)(?:\s+\((.*?)(?:\s+Update\s+(.*?))?\))?\s*$}
293                    or runtime_error( "$release:1: Cannot parse line:", $first_line );
294                $values{ operating_system_name    }  = $1;
295                $values{ operating_system_release }  = $2 . ( defined( $4 ) ? ".$4" : "" );
296                $values{ operating_system_codename } = $3;
297            } elsif ( $release =~ m{SuSE} ) {
298                # Typical SuSE-release:
299                #     SUSE Linux* OS Enterprise Server 10 (x86_64)
300                #     VERSION = 10
301                #     PATCHLEVEL = 2
302                $bulk =~ m{\A(.*)$}m
303                    or runtime_error( "$release: Cannot find the first line:", $bulk, "(eof)" );
304                my $first_line = $1;
305                $values{ operating_system_description } = $first_line;
306                $first_line =~ m{^(.*?)\s*(\d+)\s*\(.*?\)\s*$}
307                    or runtime_error( "$release:1: Cannot parse line:", $first_line );
308                $values{ operating_system_name } = $1;
309                $bulk =~ m{^VERSION\s*=\s*(.*)\s*$}m
310                    or runtime_error( "$release: There is no VERSION:", $bulk, "(eof)" );
311                $values{ operating_system_release } = $1;
312                if ( $bulk =~ m{^PATCHLEVEL\s*=\s*(.*)\s*$}m ) {
313                    $values{ operating_system_release } .= ".$1";
314                }; # if
315            } elsif ( $release =~ m{debian_version} ) {
316                # Debian. The file debian_version contains just version number, nothing more:
317                #     4.0
318                my $name = "Debian";
319                $bulk =~ m{\A(.*)$}m
320                    or runtime_error( "$release: Cannot find the first line:", $bulk, "(eof)" );
321                my $version = $1;
322                $values{ operating_system_name        } = $name;
323                $values{ operating_system_release     } = $version;
324                $values{ operating_system_codename    } = "unknown";
325                $values{ operating_system_description } = sprintf( "%s %s", $name, $version );
326            }; # if
327        }; # if
328    }; # if
329    if ( not defined( $values{ operating_system_name } ) ) {
330        $values{ operating_system_name } = "GNU/Linux";
331    }; # if
332} elsif ( $values{ kernel_name } eq "Darwin" ) {
333    my %codenames = (
334        10.4 => "Tiger",
335        10.5 => "Leopard",
336        10.6 => "Snow Leopard",
337    );
338   my $darwin;
339   my $get_os_info =
340       sub {
341           my ( $name ) = @_;
342           if ( not defined $darwin ) {
343               $darwin->{ operating_system } = "Darwin";
344               # sw_vers prints OS X* version to stdout:
345               #     ProductName:       OS X*
346               #     ProductVersion:    10.4.11
347               #     BuildVersion:      8S2167
348               # It does not print codename, so we code OS X* codenames here.
349               my $sw_vers = which( "sw_vers" ) || "/usr/bin/sw_vers";
350               my $output;
351               debug( "Executing $sw_vers..." );
352               execute( [ $sw_vers ], -stdout => \$output, -stderr => undef );
353               $output =~ m{^ProductName:\s*(.*)\s*$}m
354                   or runtime_error( "There is no ProductName in sw_vers output:", $output, "(eof)" );
355               my $name = $1;
356               $output =~ m{^ProductVersion:\s*(.*)\s*$}m
357                   or runtime_error( "There is no ProductVersion in sw_vers output:", $output, "(eof)" );
358               my $release = $1;
359               # Sometimes release reported as "10.4.11" (3 components), sometimes as "10.6".
360               # Handle both variants.
361               $release =~ m{^(\d+.\d+)(?:\.\d+)?(?=\s|$)}
362                   or runtime_error( "Cannot parse OS X* version: $release" );
363               my $version = $1;
364               my $codename = ( $codenames{ $version } or "unknown" );
365               $darwin->{ operating_system_name        } = $name;
366               $darwin->{ operating_system_release     } = $release;
367               $darwin->{ operating_system_codename    } = $codename;
368               $darwin->{ operating_system_description } = sprintf( "%s %s (%s)", $name, $release, $codename );
369           }; # if
370           return $darwin->{ $name };
371       }; # sub
372    $values{ operating_system             } = sub { $get_os_info->( "operating_system"             ); };
373    $values{ operating_system_name        } = sub { $get_os_info->( "operating_system_name"        ); };
374    $values{ operating_system_release     } = sub { $get_os_info->( "operating_system_release"     ); };
375    $values{ operating_system_codename    } = sub { $get_os_info->( "operating_system_codename"    ); };
376    $values{ operating_system_description } = sub { $get_os_info->( "operating_system_description" ); };
377} elsif ( $values{ kernel_name } =~ m{\AWindows[ _]NT\z} ) {
378    $values{ operating_system } = "MS Windows";
379    # my @os_name = Win32::GetOSName();
380    # $values{ operating_system_release } = $os_name[ 0 ];
381    # $values{ operating_system_update  } = $os_name[ 1 ];
382} elsif ( $values{ kernel_name } =~ m{\ACYGWIN_NT-} ) {
383    $values{ operating_system } = "MS Windows";
384} elsif ( $values{ kernel_name } =~ m{\AFreeBSD} ) {
385    $values{ operating_system } = "FreeBSD";
386} elsif ( $values{ kernel_name } =~ m{\ANetBSD} ) {
387    $values{ operating_system } = "NetBSD";
388} else {
389    die "Unsupported kernel_name (\"$values{ kernel_name }\") returned by POSIX::uname(); stopped";
390}; # if
391
392# host_name and domain_name
393$values{ host_name } =
394    sub {
395        my $fqdn = value( "fqdn" );
396        $fqdn =~ m{\A([^.]*)(?:\.(.*))?\z};
397        my $host_name = $1;
398        if ( not defined( $host_name ) or $host_name eq "" ) {
399            die "Unexpected error: undefined or empty host name; stopped";
400        }; # if
401        return $host_name;
402    };
403$values{ domain_name } =
404    sub {
405        my $fqdn = value( "fqdn" );
406        $fqdn =~ m{\A([^.]*)(?:\.(.*))?\z};
407        my $domain_name = $2;
408        if ( not defined( $domain_name ) or $domain_name eq "" ) {
409            die "Unexpected error: undefined or empty domain name; stopped";
410        }; # if
411        return $domain_name;
412    };
413
414# Replace undefined values with "unknown".
415foreach my $name ( @all ) {
416    if ( not defined( $values{ $name } ) ) {
417        $values{ $name } = "unknown";
418    }; # if
419}; # foreach $name
420
421# Export functions reporting properties.
422foreach my $name ( @all ) {
423    no strict "refs";
424    *$name = sub { return value( $name ); };
425}; # foreach $name
426
427# This function returns base names.
428sub base_names {
429    return @base;
430}; # sub base_names
431
432# This function returns all the names.
433sub all_names {
434    return @all;
435}; # sub all_names
436
437# This function returns value by the specified name.
438sub value($) {
439    my $name = shift( @_ );
440    if ( ref( $values{ $name } ) ) {
441        my $value = $values{ $name }->();
442        $values{ $name } = $value;
443    }; # if
444    return $values{ $name };
445}; # sub value
446
447return 1;
448
449__END__
450
451=pod
452
453=head1 NAME
454
455B<Uname.pm> -- A few subroutines to get system information usually provided by
456C</bin/uname> and C<POSIX::uname()>.
457
458=head1 SYNOPSIS
459
460    use Uname;
461
462    # Base property functions.
463    $kernel_name       = Uname::kernel_name();
464    $fqdn              = Uname::fqdn();
465    $kernel_release    = Uname::kernel_release();
466    $kernel_version    = Uname::kernel_version();
467    $machine           = Uname::machine();
468    $processor         = Uname::processor();
469    $hardware_platform = Uname::hardware_platform();
470    $operating_system  = Uname::operating_system();
471
472    # Auxiliary property functions.
473    $host_name         = Uname::host_name();
474    $domain_name       = Uname::domain_name();
475    $os_name           = Uname::operating_system_name();
476    $os_release        = Uname::operating_system_release();
477    $os_codename       = Uname::operating_system_codename();
478    $os_description    = Uname::operating_system_description();
479
480    # Meta functions.
481    @base_names  = Uname::base_names();
482    @all_names   = Uname::all_names();
483    $kernel_name = Uname::value( "kernel_name" );
484
485=head1 DESCRIPTION
486
487B<Uname.pm> resembles functionality found in C<POSIX::uname()> function or in C<uname> program.
488However, both C<POSIX::uname()> and C</bin/uname> have some disadvantages:
489
490=over
491
492=item *
493
494C<uname> may be not available in some environments, for example, in Windows* OS
495(C<uname> may be found in some third-party software packages, like MKS Toolkit or Cygwin, but it is
496not a part of OS).
497
498=item *
499
500There are many different versions of C<uname>. For example, C<uname> on OS X* does not
501recognize options C<-i>, C<-o>, and any long options.
502
503=item *
504
505Different versions of C<uname> may report the same property differently. For example,
506C<uname> on Linux* OS reports machine as C<i686>, while C<uname> on OS X* reports the same machine as
507C<x86>.
508
509=item *
510
511C<POSIX::uname()> returns list of values. I cannot recall what is the fourth element of the list.
512
513=back
514
515=head2 Base Functions
516
517Base property functions provide the information as C<uname> program.
518
519=over
520
521=item B<kernel_name()>
522
523Returns the kernel name, as reported by C<POSIX::uname()>.
524
525=item B<fqdn()>
526
527Returns the FQDN, fully qualified domain name. On some systems C<POSIX::uname()> reports short node
528name (with no domain name), on others C<POSIX::uname()> reports full node name. This
529function strive to return FQDN always (by refining C<POSIX::uname()> with
530C<Net::Domain::hostfqdn()>).
531
532=item B<kernel_release()>
533
534Returns the kernel release string, as reported by C<POSIX::uname()>. Usually the string consists of
535several numbers, separated by dots and dashes, but may also include some non-numeric substrings like
536"smp".
537
538=item B<kernel_version()>
539
540Returns the kernel version string, as reported by C<POSIX::uname()>. It is B<not> several
541dot-separated numbers but much longer string describing the kernel.
542For example, on Linux* OS it includes build date.
543If you look for something identifying the kernel, look at L<kernel_release>.
544
545=item B<machine()>
546
547Returns the machine hardware name, as reported by POSIX::uname(). Not reliable. Different OSes may
548report the same machine hardware name differently. For example, Linux* OS reports C<i686>, while OS X*
549reports C<x86> on the same machine.
550
551=item B<processor()>
552
553Returns the processor type. Not reliable. Usually the same as C<machine>.
554
555=item B<hardware_platform()>
556
557One of: C<i386> or C<x86_64>.
558
559=item B<operating_system()>
560
561One of: C<GNU/Linux>, C<OS X*>, or C<MS Windows>.
562
563=back
564
565=head2 Auxiliary Functions
566
567Auxiliary functions extends base functions with information not reported by C<uname> program.
568
569Auxiliary functions collect information from different sources. For example, on OS X*, they may
570call C<sw_vers> program to find out OS release; on Linux* OS they may parse C</etc/redhat-release> file,
571etc.
572
573=over
574
575=item B<host_name()>
576
577Returns host name (FQDN with dropped domain part).
578
579=item B<domain_name()>
580
581Returns domain name (FQDN with dropped host part).
582
583=item B<operating_system_name>
584
585Name of operating system or name of Linux* OS distribution, like "Fedora" or
586"Red Hat Enterprise Linux* OS Server".
587
588=item B<operating_system_release>
589
590Release (version) of operating system or Linux* OS distribution. Usually it is a series of
591dot-separated numbers.
592
593=item B<operating_system_codename>
594
595Codename of operating system release or Linux* OS distribution. For example, Fedora 10 is "Cambridge"
596while OS X* 10.4 is "Tiger".
597
598=item B<operating_system_description>
599
600Longer string. Usually it includes all the operating system properting mentioned above -- name,
601release, codename in parentheses.
602
603=back
604
605=head2 Meta Functions
606
607=over
608
609=item B<base_names()>
610
611This function returns the list of base property names.
612
613=item B<all_names()>
614
615This function returns the list of all property names.
616
617=item B<value(> I<name> B<)>
618
619This function returns the value of the property specified by I<name>.
620
621=back
622
623=head1 EXAMPLES
624
625    use Uname;
626
627    print( Uname::string(), "\n" );
628
629    foreach my $name ( Uname::all_names() ) {
630        print( "$name=\"" . Uname::value( $name ) . "\"\n" );
631    }; # foreach $name
632
633=head1 SEE ALSO
634
635L<POSIX::uname>, L<uname>.
636
637=cut
638
639# end of file #
640
641