package fs; # $Id$ use diagnostics; use strict; use MDK::Common::System; use MDK::Common::Various; use common; use log; use devices; use partition_table qw(:types); use run_program; use swap; use detect_devices; use modules; use fsedit; use loopback; sub read_fstab { my ($prefix, $file, $all_options) = @_; my %comments; my $comment; my @l = grep { if (/^\s*#/) { $comment .= chomp_($_) . "\n"; 0; } else { $comments{$_} = $comment if $comment; $comment = ''; 1; } } cat_("$prefix$file"); #- attach comments at the end of fstab to the previous line $comments{$l[-1]} = $comment if $comment; map { my ($dev, $mntpoint, $type, $options, $freq, $passno) = split; my $comment = $comments{$_}; $options = 'defaults' if $options eq 'rw'; # clean-up for mtab read $type = fs2type($type); if ($type eq 'supermount') { # normalize this bloody supermount $options = join(",", 'supermount', grep { if (/fs=(.*)/) { $type = $1; 0; } elsif (/dev=(.*)/) { $dev = $1; 0; } elsif ($_ eq '--') { 0; } else { 1; } } split(',', $options)); } elsif ($type eq 'smb') { # prefering type "smbfs" over "smb" $type = 'smbfs'; } $mntpoint =~ s/\\040/ /g; $dev =~ s/\\040/ /g; my $h = { device => $dev, mntpoint => $mntpoint, type => $type, options => $options, comment => $comment, if_($all_options, freq => $freq, passno => $passno), }; ($h->{major}, $h->{minor}) = unmakedev((stat "$prefix$dev")[6]); if ($dev =~ m,/(tmp|dev)/,) { my $symlink = readlink("$prefix$dev"); $dev =~ s,/(tmp|dev)/,,; if ($symlink =~ m|^[^/]+$|) { $h->{device_alias} = $dev; $h->{device} = $symlink; } else { $h->{device} = $dev; } } if ($h->{options} =~ /credentials=/) { require network::smb; #- remove credentials=file with username=foo,password=bar,domain=zoo #- the other way is done in fstab_to_string my ($options, $unknown) = mount_options_unpack($h); my $file = delete $options->{'credentials='}; my $credentials = network::smb::read_credentials_raw($file); if ($credentials->{username}) { $options->{"$_="} = $credentials->{$_} foreach qw(username password domain); mount_options_pack($h, $options, $unknown); } } $h; } @l; } sub merge_fstabs { my ($loose, $fstab, @l) = @_; foreach my $p (@$fstab) { my ($p2) = grep { fsedit::is_same_hd($_, $p) } @l or next; @l = grep { !fsedit::is_same_hd($_, $p) } @l; $p->{mntpoint} = $p2->{mntpoint} if delete $p->{unsafeMntpoint}; $p->{type} = $p2->{type} if $p2->{type} && !$loose; $p->{options} = $p2->{options} if $p2->{options} && !$loose; #- important to get isMounted property else DrakX may try to mount already mounted partitions :-( add2hash($p, $p2); $p->{device_alias} ||= $p2->{device_alias} || $p2->{device} if $p->{device} ne $p2->{device} && $p2->{device} !~ m|/|; $p->{type} && $p2->{type} && $p->{type} ne $p2->{type} && type2fs($p) ne type2fs($p2) && $p->{type} ne 'auto' && $p2->{type} ne 'auto' and log::l("err, fstab and partition table do not agree for $p->{device} type: " . (type2fs($p) || type2name($p->{type})) . " vs ", (type2fs($p2) || type2name($p2->{type}))); } @l; } sub add2all_hds { my ($all_hds, @l) = @_; @l = merge_fstabs('', [ fsedit::get_really_all_fstab($all_hds) ], @l); foreach (@l) { my $s = isThisFs('nfs', $_) ? 'nfss' : isThisFs('smbfs', $_) ? 'smbs' : 'special'; push @{$all_hds->{$s}}, $_; } } sub get_major_minor { eval { my (undef, $major, $minor) = devices::entry($_->{device}); ($_->{major}, $_->{minor}) = ($major, $minor); } foreach @_; } sub merge_info_from_mtab { my ($fstab) = @_; my @l1 = map { my $l = $_; my %l = (type => fs2type('swap')); $l{$_} = $l->{$_} foreach qw(device major minor); \%l; } read_fstab('', '/proc/swaps'); my @l2 = map { read_fstab('', $_) } '/etc/mtab', '/proc/mounts'; foreach (@l1, @l2) { log::l("found mounted partition on $_->{device} with $_->{mntpoint}"); if ($::isInstall && $_->{mntpoint} eq '/tmp/hdimage') { log::l("found hdimage on $_->{device}"); $_->{real_mntpoint} = delete $_->{mntpoint}; $_->{mntpoint} = common::usingRamdisk() && "/mnt/hd"; #- remap for hd install. } $_->{isMounted} = $_->{isFormatted} = 1; delete $_->{options}; } merge_fstabs('loose', $fstab, @l1, @l2); } # - when using "$loose", it does not merge in type&options from the fstab sub merge_info_from_fstab { my ($fstab, $prefix, $uniq, $loose) = @_; my @l = grep { if ($uniq) { my $part = fsedit::mntpoint2part($_->{mntpoint}, $fstab); !$part || fsedit::is_same_hd($part, $_); #- keep it only if it is the mountpoint AND the same device } else { 1; } } read_fstab($prefix, "/etc/fstab", 'all_options'); merge_fstabs($loose, $fstab, @l); } sub prepare_write_fstab { my ($all_hds, $prefix, $keep_smb_credentials) = @_; $prefix ||= ''; my @l1 = (fsedit::get_really_all_fstab($all_hds), @{$all_hds->{special}}); my @l2 = read_fstab($prefix, "/etc/fstab", 'all_options'); { #- remove entries from @l2 that are given by @l1 #- this is needed to allow to unset a mount point my %new; $new{$_->{device}} = 1 foreach @l1; delete $new{none}; #- special case for device "none" which can be _mounted_ more than once @l2 = grep { !$new{$_->{device}} } @l2; } my %new; my @smb_credentials; my @l = map { my $device = $_->{device} eq 'none' || member($_->{type}, qw(nfs smbfs)) ? $_->{device} : isLoopback($_) ? ($_->{mntpoint} eq '/' ? "/initrd/loopfs" : "$_->{loopback_device}{mntpoint}") . $_->{loopback_file} : do { my $dir = $_->{device} =~ m|^/| ? '' : '/dev/'; eval { devices::make("$prefix$dir$_->{device}") }; "$dir$_->{device}"; }; my $real_mntpoint = $_->{mntpoint} || ${{ '/tmp/hdimage' => '/mnt/hd' }}{$_->{real_mntpoint}}; mkdir("$prefix$real_mntpoint", 0755) if $real_mntpoint =~ m|^/|; my $mntpoint = loopback::carryRootLoopback($_) ? '/initrd/loopfs' : $real_mntpoint; my ($freq, $passno) = exists $_->{freq} ? ($_->{freq}, $_->{passno}) : isTrueFS($_) && $_->{options} !~ /encryption=/ ? (1, $_->{mntpoint} eq '/' ? 1 : loopback::carryRootLoopback($_) ? 0 : 2) : (0, 0); if (($device eq 'none' || !$new{$device}) && ($mntpoint eq 'swap' || !$new{$mntpoint})) { #- keep in mind the new line for fstab. $new{$device} = 1; $new{$mntpoint} = 1; my $options = $_->{options}; if (isThisFs('smbfs', $_) && $options =~ /password=/ && !$keep_smb_credentials) { require network::smb; if (my ($opts, $smb_credentials) = network::smb::fstab_entry_to_credentials($_)) { $options = $opts; push @smb_credentials, $smb_credentials; } } my $type = type2fs($_); my $dev = $_->{device_alias} ? "/dev/$_->{device_alias}" : $device; $mntpoint =~ s/ /\\040/g; $dev =~ s/ /\\040/g; # handle bloody supermount special case if ($options =~ /supermount/) { my @l = grep { $_ ne 'supermount' } split(',', $options); my @l1 = grep { member($_, 'ro', 'exec') } @l; my @l2 = difference2(\@l, \@l1); $options = join(",", "dev=$dev", "fs=$type", @l1, if_(@l2, '--', @l2)); ($dev, $type) = ('none', 'supermount'); } [ $mntpoint, $_->{comment} . join(' ', $dev, $mntpoint, $type, $options || 'defaults', $freq, $passno) . "\n" ]; } else { () } } grep { $_->{device} && ($_->{mntpoint} || $_->{real_mntpoint}) && $_->{type} } (@l1, @l2); join('', map { $_->[1] } sort { $a->[0] cmp $b->[0] } @l), \@smb_credentials; } sub fstab_to_string { my ($all_hds, $prefix) = @_; my ($s, undef) = prepare_write_fstab($all_hds, $prefix, 'keep_smb_credentials'); $s; } sub write_fstab { my ($all_hds, $prefix) = @_; log::l("writing $prefix/etc/fstab"); my ($s, $smb_credentials) = prepare_write_fstab($all_hds, $prefix, ''); output("$prefix/etc/fstab", $s); network::smb::save_credentials($_) foreach @$smb_credentials; } sub auto_fs() { grep { chop; $_ && !/nodev/ } cat_("/etc/filesystems"); } sub mount_options { my %non_defaults = ( sync => 'async', noatime => 'atime', noauto => 'auto', ro => 'rw', user => 'nouser', nodev => 'dev', noexec => 'exec', nosuid => 'suid', ); my @user_implies = qw(noexec nodev nosuid); \%non_defaults, \@user_implies; } # simple function # use mount_options_unpack + mount_options_pack for advanced stuff sub add_options(\$@) { my ($option, @options) = @_; my %l; @l{split(',', $$option), @options} = (); delete $l{defaults}; $$option = join(',', keys %l) || "defaults"; } sub mount_options_unpack { my ($part) = @_; my $packed_options = $part->{options}; my ($non_defaults, $user_implies) = mount_options(); my @auto_fs = auto_fs(); my %per_fs = ( iso9660 => [ qw(unhide) ], vfat => [ qw(umask=0) ], nfs => [ qw(rsize=8192 wsize=8192) ], smbfs => [ qw(username= password=) ], reiserfs => [ 'notail' ], ); push @{$per_fs{$_}}, 'usrquota', 'grpquota' foreach 'ext2', 'ext3', 'xfs'; while (my ($fs, $l) = each %per_fs) { isThisFs($fs, $part) || $part->{type} eq 'auto' && member($fs, @auto_fs) or next; $non_defaults->{$_} = 1 foreach @$l; } $non_defaults->{encrypted} = 1 if !$part->{isFormatted} || isSwap($part); $non_defaults->{supermount} = 1 if member(type2fs($part), 'auto', @auto_fs); my $defaults = { reverse %$non_defaults }; my %options = map { $_ => '' } keys %$non_defaults; my @unknown; foreach (split(",", $packed_options)) { if ($_ eq 'user') { $options{$_} = 1 foreach ('user', @$user_implies); } elsif (exists $non_defaults->{$_}) { $options{$_} = 1; } elsif ($defaults->{$_}) { $options{$defaults->{$_}} = 0; } elsif (/(.*?=)(.*)/) { $options{$1} = $2; } else { push @unknown, $_; } } # merge those, for cleaner help $options{'rsize=8192,wsize=8192'} = delete $options{'rsize=8192'} && delete $options{'wsize=8192'} if exists $options{'rsize=8192'}; my $unknown = join(",", @unknown); \%options, $unknown; } sub mount_options_pack_ { my ($part, $options, $unknown) = @_; my ($non_defaults, $user_implies) = mount_options(); my @l; if (delete $options->{user}) { push @l, 'user'; foreach (@$user_implies) { if (!delete $options->{$_}) { # overriding $options->{$non_defaults->{$_}} = 1; } } } push @l, map_each { if_($::b, $::a =~ /=$/ ? "$::a$::b" : $::a) } %$options; push @l, $unknown; join(",", uniq(grep { $_ } @l)); } sub mount_options_pack { my ($part, $options, $unknown) = @_; $part->{options} = mount_options_pack_($part, $options, $unknown); noreturn(); } sub mount_options_help { my %help = map { $_ => '' } @_; my %short = map { if_(/(.*?)=/, "$1=" => $_) } keys %help; foreach (split(':', $ENV{LANGUAGE}), '') { my $manpage = "/usr/share/man/$_/man8/mount.8.bz2"; -e $manpage or next; my ($tp, $option); foreach (`bzip2 -dc $manpage`) { my $prev_tp = $tp; $tp = /^\.(TP|RE)/; my ($s) = /^\.B (.*)/; if ($prev_tp && $s eq '\-o' .. /X^/) { if (my $v = $prev_tp && $s =~ /^[a-z]/i .. $tp) { if ($v == 1) { $s = $short{$s} || $s; $option = exists $help{$s} && !$help{$s} ? $s : ''; } elsif ($v !~ 'E0') { s/\\//g; s/\s*"(.*?)"\s*/$1/g if s/^\.BR\s+//; s/^\.B\s+//; $help{$option} .= $_ if $option; } } } } } %help; } sub set_default_options { my ($part, $is_removable, $useSupermount, $security, $iocharset, $codepage) = @_; my ($options, $unknown) = mount_options_unpack($part); if ($is_removable) { $options->{supermount} = $useSupermount; $part->{type} = 'auto'; # if supermount, code below will handle choosing the right type } my $is_auto = isThisFs('auto', $part); if ($part->{media_type} eq 'cdrom') { $options->{ro} = 1; } if ($part->{media_type} eq 'fd') { # slow device so don't loose time, write now! $options->{sync} = 1; } if (isTrueFS($part)) { #- noatime on laptops (do not wake up the hd) #- Do not update inode access times on this #- file system (e.g, for faster access on the #- news spool to speed up news servers). $options->{noatime} = detect_devices::isLaptop(); } if (isThisFs('nfs', $part)) { put_in_hash($options, { nosuid => 1, 'rsize=8192,wsize=8192' => 1, soft => 1, }); } if (isFat($part) || $is_auto) { put_in_hash($options, { user => 1, noexec => 0, }) if !exists $part->{rootDevice}; # partition means no removable media put_in_hash($options, { 'umask=0' => $security < 3, 'iocharset=' => $iocharset, 'codepage=' => $codepage, }); } if (isThisFs('ntfs', $part) || $is_auto) { put_in_hash($options, { 'iocharset=' => $iocharset }); } if (isThisFs('iso9660', $part) || $is_auto) { put_in_hash($options, { user => 1, noexec => 0, 'iocharset=' => $iocharset }); } if (isThisFs('reiserfs', $part)) { $options->{notail} = 1; } if (isLoopback($part) && !isSwap($part)) { #- no need for loop option for swap files $options->{loop} = 1; } # rationalize: no need for user if ($options->{autofs} || $options->{supermount}) { $options->{user} = 0; } # have noauto when we have user $options->{noauto} = 1 if $options->{user}; if ($options->{user}) { # ensure security (user_implies - noexec as noexec is not a security matter) $options->{$_} = 1 foreach 'nodev', 'nosuid'; } mount_options_pack($part, $options, $unknown); } sub set_all_default_options { my ($all_hds, $useSupermount, $security, $iocharset, $codepage) = @_; my @removables = @{$all_hds->{raw_hds}}; foreach my $part (fsedit::get_really_all_fstab($all_hds)) { set_default_options($part, member($part, @removables), $useSupermount, $security, $iocharset, $codepage); } } sub set_removable_mntpoints { my ($all_hds) = @_; my %names; foreach (@{$all_hds->{raw_hds}}) { my $name = $_->{media_type}; if (member($name, 'hd', 'fd')) { if (detect_devices::isZipDrive($_)) { $name = 'zip'; } elsif ($name eq 'fd') { $name = 'floppy'; } else { log::l("set_removable_mntpoints: don't know what to with hd $_->{device}"); next; } } if ($name) { my $s = ++$names{$name}; $_->{mntpoint} ||= "/mnt/$name" . ($s == 1 ? '' : $s); } } } sub get_raw_hds { my ($prefix, $all_hds) = @_; $all_hds->{raw_hds} = [ detect_devices::floppies(), detect_devices::cdroms__faking_ide_scsi(), detect_devices::zips__faking_ide_scsi(), ]; get_major_minor(@{$all_hds->{raw_hds}}); my @fstab = read_fstab($prefix, "/etc/fstab", 'all_options'); $all_hds->{nfss} = [ grep { isThisFs('nfs', $_) } @fstab ]; $all_hds->{smbs} = [ grep { isThisFs('smbfs', $_) } @fstab ]; $all_hds->{special} = [ (grep { isThisFs('tmpfs', $_) } @fstab), { device => 'none', mntpoint => '/proc', type => 'proc' }, { device => 'none', mntpoint => '/dev/pts', type => 'devpts', options => 'mode=0620' }, ]; } ################################################################################ # formatting functions ################################################################################ sub disable_forced_fsck { my ($dev) = @_; run_program::run("tune2fs", "-c0", "-i0", devices::make($dev)); } sub format_ext2($@) { #- mke2fs -b (1024|2048|4096) -c -i(1024 > 262144) -N (1 > 100000000) -m (0-100%) -L volume-label #- tune2fs my ($dev, @options) = @_; $dev =~ m,(rd|ida|cciss)/, and push @options, qw(-b 4096 -R stride=16); #- For RAID only. push @options, qw(-b 1024 -O none) if arch() =~ /alpha/; run_program::run('mke2fs', '-F', @options, devices::make($dev)) or die _("%s formatting of %s failed", "ext2", $dev); } sub format_ext3 { my ($dev, @options) = @_; format_ext2($dev, "-j", @options); disable_forced_fsck($dev); } sub format_reiserfs { my ($dev, @options) = @_; #TODO add -h tea run_program::run("mkreiserfs", "-ff", @options, devices::make($dev)) or die _("%s formatting of %s failed", "reiserfs", $dev); } sub format_xfs { my ($dev, @options) = @_; run_program::run("mkfs.xfs", "-f", "-q", @options, devices::make($dev)) or die _("%s formatting of %s failed", "xfs", $dev); } sub format_jfs { my ($dev, @options) = @_; run_program::run("mkfs.jfs", "-f", @options, devices::make($dev)) or die _("%s formatting of %s failed", "jfs", $dev); } sub format_dos { my ($dev, @options) = @_; run_program::run("mkdosfs", @options, devices::make($dev)) or die _("%s formatting of %s failed", "dos", $dev); } sub format_hfs { my ($dev, @options) = @_; run_program::run("hformat", @options, devices::make($dev)) or die _("%s formatting of %s failed", "HFS", $dev); } sub real_format_part { my ($part) = @_; $part->{isFormatted} and return; my $dev = $part->{real_device} || $part->{device}; my @options = $part->{toFormatCheck} ? "-c" : (); log::l("formatting device $dev (type ", type2name($part->{type}), ")"); if (isExt2($part)) { push @options, "-F" if isLoopback($part); push @options, "-m", "0" if $part->{mntpoint} =~ m|^/home|; format_ext2($dev, @options); } elsif (isThisFs("ext3", $part)) { push @options, "-m", "0" if $part->{mntpoint} =~ m|^/home|; format_ext3($dev, @options); } elsif (isThisFs("reiserfs", $part)) { format_reiserfs($dev, @options, if_(c::kernel_version() =~ /^\Q2.2/, "-v", "1")); } elsif (isThisFs("xfs", $part)) { format_xfs($dev, @options); } elsif (isThisFs("jfs", $part)) { format_jfs($dev, @options); } elsif (isDos($part)) { format_dos($dev, @options); } elsif (isWin($part)) { format_dos($dev, @options, '-F', 32); } elsif (isThisFs('hfs', $part)) { format_hfs($dev, @options, '-l', "Untitled"); } elsif (isAppleBootstrap($part)) { format_hfs($dev, @options, '-l', "bootstrap"); } elsif (isSwap($part)) { my $check_blocks = grep { /^-c$/ } @options; swap::make($dev, $check_blocks); } else { die _("I don't know how to format %s in type %s", $part->{device}, type2name($part->{type})); } $part->{isFormatted} = 1; } sub format_part { my ($raids, $part, $prefix) = @_; if (isRAID($part)) { require raid; raid::format_part($raids, $part); } elsif (isLoopback($part)) { loopback::format_part($part, $prefix); } else { real_format_part($part); } } ################################################################################ # mounting functions ################################################################################ sub set_loop { my ($part) = @_; if (!$part->{real_device}) { eval { modules::load('loop') }; $part->{real_device} = devices::set_loop(devices::make($part->{device}), $part->{encrypt_key}, $part->{options} =~ /encryption=(\w+)/); } } sub formatMount_part { my ($part, $raids, $fstab, $prefix, $callback) = @_; if (isLoopback($part)) { formatMount_part($part->{loopback_device}, $raids, $fstab, $prefix, $callback); } if (my $p = up_mount_point($part->{mntpoint}, $fstab)) { formatMount_part($p, $raids, $fstab, $prefix, $callback) unless loopback::carryRootLoopback($part); } if ($part->{encrypt_key}) { set_loop($part); } if ($part->{toFormat}) { $callback->($part) if $callback; format_part($raids, $part, $prefix); } mount_part($part, $prefix); } sub formatMount_all { my ($raids, $fstab, $prefix, $callback) = @_; formatMount_part($_, $raids, $fstab, $prefix, $callback) foreach sort { isLoopback($a) ? 1 : isSwap($a) ? -1 : 0 } grep { $_->{mntpoint} } @$fstab; #- ensure the link is there loopback::carryRootCreateSymlink($_, $prefix) foreach @$fstab; #- for fun :) #- that way, when install exits via ctrl-c, it gives hand to partition eval { local $SIG{__DIE__} = 'ignore'; my ($type, $major, $minor) = devices::entry(fsedit::get_root($fstab)->{device}); output "/proc/sys/kernel/real-root-dev", makedev($major, $minor); }; } sub mount { my ($dev, $where, $fs, $rdonly, $options) = @_; log::l("mounting $dev on $where as type $fs, options $options"); -d $where or mkdir_p($where); my @fs_modules = qw(vfat hfs romfs ufs reiserfs xfs jfs ext3); if (member($fs, 'smb', 'smbfs', 'nfs', 'ntfs') && $::isStandalone) { system('mount', '-t', $fs, $dev, $where, '-o', $options) == 0 or die _("mounting partition %s in directory %s failed", $dev, $where); return; #- do not update mtab, already done by mount(8) } elsif (member($fs, 'ext2', 'proc', 'usbdevfs', 'iso9660', @fs_modules)) { $dev = devices::make($dev) if $fs ne 'proc' && $fs ne 'usbdevfs'; $where =~ s|/$||; my $flag = c::MS_MGC_VAL(); $flag |= c::MS_RDONLY() if $rdonly; my $mount_opt = ""; if ($fs eq 'vfat') { $mount_opt = 'check=relaxed'; } elsif ($fs eq 'reiserfs') { #- could be better if we knew if there is a /boot or not #- without knowing it, / is forced to be mounted with notail # if $where =~ m|/(boot)?$|; $mount_opt = 'notail'; #- notail in any case } elsif ($fs eq 'jfs' && !$rdonly) { #- needed if the system is dirty otherwise mounting read-write simply fails run_program::run("fsck.jfs", $dev) or die "fsck.jfs failed"; } elsif ($fs eq 'ext2') { foreach ('-a', '-y') { run_program::run("fsck.ext2", $_, $dev); my $err = $?; if ($err & 0x0100) { log::l("fsck corrected partition $dev") } if ($err & 0xfeff) { my $txt = sprintf("fsck failed on %s with exit code %d or signal %d", $dev, $err >> 8, $err & 255); $_ eq '-y' ? die($txt) : cdie($txt); } else { last; } } } if (member($fs, @fs_modules)) { eval { modules::load($fs) }; } elsif ($fs eq 'iso9660') { eval { modules::load('isofs') }; } if ($fs eq 'ext3' && $::isInstall) { # ext3 mount to use the journal syscall_('mount', $dev, $where, $fs, $flag, $mount_opt) or die _("mounting partition %s in directory %s failed", $dev, $where) . " ($!)"; syscall_('umount', $where); # really mount as ext2 during install for speed up $fs = 'ext2'; } log::l("calling mount($dev, $where, $fs, $flag, $mount_opt)"); syscall_('mount', $dev, $where, $fs, $flag, $mount_opt) or die _("mounting partition %s in directory %s failed", $dev, $where) . " ($!)"; } else { log::l("skipping mounting $fs partition"); return; } local *F; open F, ">>/etc/mtab" or return; #- fail silently, /etc must be read-only print F "$dev $where $fs defaults 0 0\n"; } #- takes the mount point to umount (can also be the device) sub umount { my ($mntpoint) = @_; $mntpoint =~ s|/$||; log::l("calling umount($mntpoint)"); syscall_('umount', $mntpoint) or die _("error unmounting %s: %s", $mntpoint, "$!"); substInFile { $_ = '' if /(^|\s)$mntpoint\s/ } '/etc/mtab'; #- don't care about error, if we can't read, we won't manage to write... (and mess mtab) } sub mount_part { my ($part, $prefix, $rdonly) = @_; #- root carrier's link can't be mounted loopback::carryRootCreateSymlink($part, $prefix); log::l("isMounted=$part->{isMounted}, real_mntpoint=$part->{real_mntpoint}, mntpoint=$part->{mntpoint}"); if ($part->{isMounted} && $part->{real_mntpoint} && $part->{mntpoint}) { log::l("remounting partition on $prefix$part->{mntpoint} instead of $part->{real_mntpoint}"); if ($::isInstall) { #- ensure partition will not be busy. require install_any; install_any::getFile('XXX'); } eval { umount($part->{real_mntpoint}); rmdir $part->{real_mntpoint}; symlinkf "$prefix$part->{mntpoint}", $part->{real_mntpoint}; delete $part->{real_mntpoint}; $part->{isMounted} = 0; }; } return if $part->{isMounted}; unless ($::testing) { if (isSwap($part)) { swap::swapon($part->{device}); } else { $part->{mntpoint} or die "missing mount point for partition $part->{device}"; my $mntpoint = ($prefix || '') . $part->{mntpoint}; if (isLoopback($part) || $part->{encrypt_key}) { set_loop($part); } elsif (loopback::carryRootLoopback($part)) { $mntpoint = "/initrd/loopfs"; } my $dev = $part->{real_device} || $part->{device}; mount($dev, $mntpoint, type2fs($part), $rdonly, $part->{options}); rmdir "$mntpoint/lost+found"; } } $part->{isMounted} = $part->{isFormatted} = 1; #- assume that if mount works, partition is formatted } sub umount_part { my ($part, $prefix) = @_; $part->{isMounted} || $part->{real_mntpoint} or return; unless ($::testing) { if (isSwap($part)) { swap::swapoff($part->{device}); } elsif (loopback::carryRootLoopback($part)) { umount("/initrd/loopfs"); } else { umount(($prefix || '') . $part->{mntpoint} || devices::make($part->{device})); devices::del_loop(delete $part->{real_device}) if $part->{real_device}; } } $part->{isMounted} = 0; } sub mount_all($;$$) { my ($fstab, $prefix) = @_; log::l("mounting all filesystems"); #- order mount by alphabetical order, that way / < /home < /home/httpd... foreach (sort { $a->{mntpoint} cmp $b->{mntpoint} } grep { isSwap($_) || $_->{mntpoint} && isTrueFS($_) } @$fstab) { mount_part($_, $prefix); } } sub umount_all($;$) { my ($fstab, $prefix) = @_; log::l("unmounting all filesystems"); foreach (sort { $b->{mntpoint} cmp $a->{mntpoint} } @$fstab) { $_->{mntpoint} and umount_part($_, $prefix); } } ################################################################################ # various functions ################################################################################ sub df { my ($part, $prefix) = @_; my $dir = "/tmp/tmp_fs_df"; return $part->{free} if exists $part->{free}; if ($part->{isMounted}) { $dir = ($prefix || '') . $part->{mntpoint}; } elsif ($part->{notFormatted} && !$part->{isFormatted}) { return; #- won't even try! } else { mkdir $dir; eval { mount($part->{device}, $dir, type2fs($part), 'readonly') }; if ($@) { $part->{notFormatted} = 1; $part->{isFormatted} = 0; unlink $dir; return; } } my (undef, $free) = MDK::Common::System::df($dir); if (!$part->{isMounted}) { umount($dir); unlink($dir) } $part->{free} = 2 * $free if defined $free; $part->{free}; } sub up_mount_point { my ($mntpoint, $fstab) = @_; while (1) { $mntpoint = dirname($mntpoint); $mntpoint ne "." or return; $_->{mntpoint} eq $mntpoint and return $_ foreach @$fstab; } } 1; f='#n617'>617 618 619 620 621 622 623 624 625 626 627 628 629 630 631 632 633 634 635 636 637 638 639 640 641 642 643 644 645 646 647 648 649 650 651 652 653 654 655 656 657 658 659 660 661 662 663 664 665 666 667 668 669 670 671 672 673 674 675 676 677 678 679 680 681 682 683 684 685 686 687 688 689 690 691 692 693 694 695 696 697 698 699 700 701 702 703 704 705 706 707 708 709 710 711 712 713 714 715 716 717 718 719 720 721 722 723 724 725 726 727 728 729 730 731 732 733 734 735 736 737 738 739 740 741 742 743 744 745 746 747 748 749 750 751 752 753 754 755 756 757 758 759 760 761 762 763 764 765 766 767 768 769 770
package detect_devices; # $Id$
use diagnostics;
use strict;
use vars qw($pcitable_addons $usbtable_addons);
#-######################################################################################
#- misc imports
#-######################################################################################
use log;
use common;
use devices;
use run_program;
use c;
#-#####################################################################################
#- Globals
#-#####################################################################################
my @netdevices = map { my $l = $_; map { "$l$_" } (0..3) } qw(eth tr fddi plip);
my %serialprobe;
#-######################################################################################
#- Functions
#-######################################################################################
sub dev_is_devfs { -e "/dev/.devfsd" } #- no $::prefix, returns false during install and that's nice :)
sub get {
#- Detect the default BIOS boot harddrive is kind of tricky. We may have IDE,
#- SCSI and RAID devices on the same machine. From what I see so far, the default
#- BIOS boot harddrive will be
#- 1. The first IDE device if IDE exists. Or
#- 2. The first SCSI device if SCSI exists. Or
#- 3. The first RAID device if RAID exists.
getIDE(), getSCSI(), getDAC960(), getCompaqSmartArray(), getATARAID();
}
sub hds { grep { $_->{media_type} eq 'hd' && !isRemovableDrive($_) } get() }
sub tapes { grep { $_->{media_type} eq 'tape' } get() }
sub cdroms { grep { $_->{media_type} eq 'cdrom' } get() }
sub burners { grep { isBurner($_) } cdroms() }
sub dvdroms { grep { isDvdDrive($_) } cdroms() }
sub raw_zips { grep { member($_->{media_type}, 'fd', 'hd') && isZipDrive($_) } get() }
#-sub jazzs { grep { member($_->{media_type}, 'fd', 'hd') && isJazzDrive($_) } get() }
sub ls120s { grep { member($_->{media_type}, 'fd', 'hd') && isLS120Drive($_) } get() }
sub zips {
map {
$_->{device} .= 4;
$_->{devfs_device} = $_->{devfs_prefix} . '/part4';
$_;
} raw_zips();
}
sub cdroms__faking_ide_scsi {
my @l = cdroms();
return @l if $::isStandalone;
if (my @l_ide = grep { $_->{bus} eq 'ide' && isBurner($_) } @l) {
require modules;
modules::add_probeall('scsi_hostadapter', 'ide-scsi');
my $nb = 1 + max(-1, map { $_->{device} =~ /scd(\d+)/ } @l);
foreach my $e (@l_ide) {
log::l("IDEBurner: $e->{device}");
$e->{device} = "scd" . $nb++;
}
}
@l;
}
sub zips__faking_ide_scsi {
my @l = raw_zips();
if (my @l_ide = grep { $_->{bus} eq 'ide' && $::isInstall } @l) {
require modules;
modules::add_probeall('scsi_hostadapter', 'ide-scsi');
my $nb = 1 + max(-1, map { if_($_->{device} =~ /sd(\w+)/, ord($1) - ord('a')) } getSCSI());
foreach my $e (@l_ide) {
my $faked = "sd" . chr(ord('a') + $nb++);
log::l("IDE Zip: $e->{device} => $faked");
$e->{device} = $faked;
}
}
map { $_->{device} .= 4; $_ } @l;
}
sub floppies() {
require modules;
eval { modules::load("floppy") };
my @fds = $@ ? () : map {
my $info = (!dev_is_devfs() || -e "/dev/fd$_") && c::floppy_info(devices::make("fd$_"));
if_($info && $info ne '(null)', { device => "fd$_", devfs_device => "floppy/$_", media_type => 'fd', info => $info })
} qw(0 1);
my @ide = ls120s() and eval { modules::load("ide-floppy") };
eval { modules::load("usb-storage") } if usbStorage();
my @scsi = grep { $_->{media_type} eq 'fd' } getSCSI();
@ide, @scsi, @fds;
}
sub floppies_dev() { map { $_->{device} } floppies() }
sub floppy { first(floppies_dev()) }
#- example ls120, model = "LS-120 SLIM 02 UHD Floppy"
sub removables {
floppies(), cdroms__faking_ide_scsi(), zips__faking_ide_scsi();
}
sub get_sys_cdrom_info {
my (@drives) = @_;
my @drives_order;
foreach (cat_("/proc/sys/dev/cdrom/info")) {
my ($t, $l) = split ':';
my @l = split ' ', $l;
if ($t eq 'drive name') {
@drives_order = map {
s/^sr/scd/;
my $dev = $_;
first(grep { $_->{device} eq $dev } @drives);
} @l;
} else {
my $capacity;
if ($t eq 'Can write CD-R') {
$capacity = 'burner';
} elsif ($t eq 'Can read DVD') {
$capacity = 'DVD';
}
if ($capacity) {
each_index {
($drives_order[$::i] || {})->{capacity} .= "$capacity " if $_;
} @l;
}
}
}
}
sub get_usb_storage_info {
my (@l) = @_;
my %usbs = map {
my $s = cat_(glob_("$_/*"));
my ($host) = $s =~ /^\s*Host scsi(\d+):/m; #-#
my ($vendor_name) = $s =~ /^\s*Vendor: (.*)/m;
my ($vendor, $id) = $s =~ /^\s*GUID: (....)(....)/m;
if_(defined $host, $host => { vendor_name => $vendor_name, usb_vendor => hex $vendor, usb_id => hex $id });
} glob_('/proc/scsi/usb-storage-*') or return;
#- only the entries matching the following conditions can be usb-storage devices
@l = grep { $_->{channel} == 0 && $_->{id} == 0 && $_->{lun} == 0 } @l;
my %l; push @{$l{$_->{host}}}, $_ foreach @l;
my @informed;
foreach my $host (keys %usbs) {
my @choices = @{$l{$host} || []} or log::l("weird, host$host from /proc/scsi/usb-storage-*/* is not in /proc/scsi/scsi"), next;
if (@choices > 1) {
@choices = grep { $_->{info} =~ /^\Q$usbs{$host}{vendor_name}/ } @choices;
@choices or log::l("weird, can't find the good entry host$host from /proc/scsi/usb-storage-*/* in /proc/scsi/scsi"), next;
@choices == 1 or log::l("argh, can't determine the good entry host$host from /proc/scsi/usb-storage-*/* in /proc/scsi/scsi"), next;
}
add2hash($choices[0], $usbs{$host});
push @informed, $choices[0];
}
@informed or return;
foreach my $usb (usb_probe()) {
if (my ($e) = grep { $_->{usb_vendor} == $usb->{vendor} && $_->{usb_id} == $usb->{id} } @informed) {
$e->{"usb_$_"} = $usb->{$_} foreach keys %$usb;
}
}
}
sub get_devfs_devices {
my (@l) = @_;
my %h = (cdrom => 'cd', hd => 'disc');
foreach (@l) {
my $t = $h{$_->{media_type}} or next;
$_->{devfs_device} = $_->{devfs_prefix} . '/' . $t;
}
}
sub isBurner {
my ($e) = @_;
$e->{capacity} =~ /burner/ and return 1;
#- do not work for SCSI
my $f = tryOpen($e->{device}); #- SCSI burner are not detected this way.
$f && c::isBurner(fileno($f));
}
sub isDvdDrive {
my ($e) = @_;
$e->{capacity} =~ /DVD/ || $e->{info} =~ /DVD/ and return 1;
#- do not work for SCSI
my $f = tryOpen($e->{device});
$f && c::isDvdDrive(fileno($f));
}
sub isZipDrive { $_[0]{info} =~ /ZIP\s+\d+/ } #- accept ZIP 100, untested for bigger ZIP drive.
sub isJazzDrive { $_[0]{info} =~ /\bJAZZ?\b/i } #- accept "iomega jaz 1GB"
sub isLS120Drive { $_[0]{info} =~ /LS-?120|144MB/ }
sub isRemovableUsb { index($_[0]{usb_media_type}, 'Mass Storage|') == 0 && usb2removable($_[0]) }
sub isFloppyUsb { $_[0]{usb_driver} eq 'Removable:floppy' }
sub isRemovableDrive {
my ($e) = @_;
isZipDrive($e) || isLS120Drive($e) || $e->{media_type} eq 'fd' || isRemovableUsb($e) || index($e->{usb_media_type}, 'Mass Storage|Floppy (UFI)') == 0;
}
sub getSCSI() {
my $err = sub { log::l("ERROR: unexpected line in /proc/scsi/scsi: $_[0]") };
my ($first, @l) = common::join_lines(cat_("/proc/scsi/scsi")) or return;
$first =~ /^Attached devices:/ or $err->($first);
@l = map_index {
my ($host, $channel, $id, $lun) = m/^Host: scsi(\d+) Channel: (\d+) Id: (\d+) Lun: (\d+)/ or $err->($_);
my ($vendor, $model) = /^\s*Vendor:\s*(.*?)\s+Model:\s*(.*?)\s+Rev:/m or $err->($_);
my ($type) = /^\s*Type:\s*(.*)/m or $err->($_);
{ info => "$vendor $model", host => $host, channel => $channel, id => $id, lun => $lun,
device => "sg$::i", devfs_prefix => sprintf('scsi/host%d/bus%d/target%d/lun%d', $host, $channel, $id, $lun),
raw_type => $type, bus => 'SCSI' };
} @l;
get_usb_storage_info(@l);
each_index {
my $dev = "sd" . chr($::i + ord('a'));
put_in_hash $_, { device => $dev, media_type => isFloppyUsb($_) ? 'fd' : 'hd' };
} grep { $_->{raw_type} =~ /Direct-Access|Optical Device/ } @l;
each_index {
put_in_hash $_, { device => "st$::i", media_type => 'tape' };
} grep { $_->{raw_type} =~ /Sequential-Access/ } @l;
each_index {
put_in_hash $_, { device => "scd$::i", media_type => 'cdrom' };
} grep { $_->{raw_type} =~ /CD-ROM|WORM/ } @l;
# Old hp scanners report themselves as "Processor"s
# (see linux/include/scsi/scsi.h and sans-find-scanner.1)
each_index {
put_in_hash $_, { media_type => 'scanner' };
} grep { $_->{raw_type} =~ /Scanner/ || $_->{raw_type} =~ /Processor /} @l;
get_devfs_devices(@l);
get_sys_cdrom_info(@l);
@l;
}
my %eide_hds = (
"ASUS" => "Asus",
"CD-ROM CDU" => "Sony",
"CD-ROM Drive/F5D" => "ASUSTeK",
"Compaq" => "Compaq",
"CONNER" => "Conner Peripherals",
"IBM" => "IBM",
"FUJITSU" => "Fujitsu",
"HITACHI" => "Hitachi",
"Lite-On" => "Lite-On Technology Corp.",
"LTN" => "Lite-On Technology Corp.",
"IOMEGA" => "Iomega",
"MAXTOR" => "Maxtor",
"Maxtor" => "Maxtor",
"Micropolis" => "Micropolis",
"PLEXTOR" => "Plextor",
"QUANTUM" => "Quantum",
"SAMSUNG" => "Samsung",
"Seagate " => "Seagate Technology",
"ST3" => "Seagate Technology",
"TEAC" => "Teac",
"TOSHIBA" => "Toshiba",
"TEAC" => "Teac",
"TOSHIBA" => "Toshiba",
"WDC" => "Western Digital Corp.",
);
sub getIDE() {
my @idi;
#- what about a system with absolutely no IDE on it, like some sparc machine.
-e "/proc/ide" or return ();
#- Great. 2.2 kernel, things are much easier and less error prone.
foreach my $d (sort @{[glob_('/proc/ide/hd*')]}) {
cat_("$d/driver") =~ /ide-scsi/ and next; #- already appears in /proc/scsi/scsi
my $t = chomp_(cat_("$d/media"));
my $type = $ {{disk => 'hd', cdrom => 'cdrom', tape => 'tape', floppy => 'fd'}}{$t} or next;
my $info = chomp_(cat_("$d/model")) || "(none)";
my $num = ord (($d =~ /(.)$/)[0]) - ord 'a';
my ($vendor, $model) = map {
if_($info =~ /^$_\b(-|\s*)(.*)/, $eide_hds{$_}, $2);
} keys %eide_hds;
my ($channel, $id) = ($num / 2, $num % 2);
my $devfs_prefix = sprintf('ide/host0/bus%d/target%d/lun0', $channel, $id);
push @idi, { media_type => $type, device => basename($d),
devfs_prefix => $devfs_prefix,
info => $info, channel => $channel, id => $id, bus => 'ide',
Vendor => $vendor, Model => $model };
}
get_devfs_devices(@idi);
get_sys_cdrom_info(@idi);
@idi;
}
sub getCompaqSmartArray() {
my (@idi, $f);
foreach ('array/ida', 'cpqarray/ida', 'cciss/cciss') {
my $prefix = "/proc/driver/$_"; #- kernel 2.4 places it here
$prefix = "/proc/$_" if !-e "${prefix}0"; #- kernel 2.2
my ($name) = m|/(.*)|;
for (my $i = 0; -r ($f = "${prefix}$i"); $i++) {
foreach (cat_($f)) {
if (my ($device) = m|^\s*($name/.*?):|) {
push @idi, { device => $device, prefix => $device . 'p', info => "Compaq RAID logical disk",
media_type => 'hd', bus => 'ida' };
}
}
}
}
@idi;
}
sub getDAC960() {
my %idi;
#- We are looking for lines of this format:DAC960#0:
#- /dev/rd/c0d0: RAID-7, Online, 17928192 blocks, Write Thru0123456790123456789012
foreach (syslog()) {
my ($device, $info) = m|/dev/(rd/.*?): (.*?),| or next;
$idi{$device} = { info => $info, media_type => 'hd', device => $device, prefix => $device . 'p', bus => 'dac960' };
}
values %idi;
}
sub getATARAID {
my %l;
foreach (syslog()) {
my ($device) = m|^\s*(ataraid/d\d+):| or next;
$l{$device} = { info => 'ATARAID block device', media_type => 'hd', device => $device, prefix => $device . 'p', bus => 'ataraid' };
log::l("ATARAID: $device");
}
values %l;
}
sub getNet() {
grep { !(($::isStandalone || $::live) && /plip/) && c::hasNetDevice($_) } @netdevices;
}
#sub getISDN() {
# mapgrep(sub {member (($_[0] =~ /\s*(\w*):/), @netdevices), $1 }, split(/\n/, cat_("/proc/net/dev")));
#}
$pcitable_addons = <<'EOF';
# add here lines conforming the pcitable format (0xXXXX\t0xXXXX\t"\w+"\t".*")
EOF
$usbtable_addons = <<'EOF';
# add here lines conforming the usbtable format (0xXXXX\t0xXXXX\t"\w+"\t".*")
EOF
sub add_addons {
my ($addons, @l) = @_;
foreach (split "\n", $addons) {
/^\s/ and die "bad detect_devices::probeall_addons line \"$_\"";
s/^#.*//;
s/"(.*?)"/$1/g;
next if /^$/;
my ($vendor, $id, $driver, $description) = split("\t", $_, 4) or die "bad detect_devices::probeall_addons line \"$_\"";
foreach (@l) {
$_->{vendor} == hex $vendor && $_->{id} == hex $id or next;
put_in_hash($_, { driver => $driver, description => $description });
}
}
@l;
}
sub pci_probe {
my ($probe_type) = @_;
log::l("full pci_probe") if $probe_type;
add_addons($pcitable_addons, map {
my %l;
@l{qw(vendor id subvendor subid pci_bus pci_device pci_function media_type driver description)} = split "\t";
$l{$_} = hex $l{$_} foreach qw(vendor id subvendor subid);
$l{bus} = 'PCI';
\%l
} c::pci_probe($probe_type || 0));
}
sub usb_probe {
-e "/proc/bus/usb/devices" or return;
add_addons($usbtable_addons, map {
my %l;
@l{qw(vendor id media_type driver description pci_bus pci_device)} = split "\t";
$l{$_} = hex $l{$_} foreach qw(vendor id);
$l{bus} = 'USB';
\%l
} c::usb_probe());
}
sub pcmcia_probe {
-e '/var/run/stab' || -e '/var/lib/pcmcia/stab' or return ();
my (@devs, $desc);
foreach (cat_('/var/run/stab'), cat_('/var/lib/pcmcia/stab')) {
if (/^Socket\s+\d+:\s+(.*)/) {
$desc = $1;
} else {
my (undef, $type, $module, undef, $device) = split;
push @devs, { description => $desc, driver => $module, type => $type, device => $device };
}
}
@devs;
}
# pci_probe with $probe_type is unsafe for pci! (bug in kernel&hardware)
# pcmcia_probe provides field "device", used in network.pm
# => probeall with $probe_type is unsafe
sub probeall {
my ($probe_type) = @_;
return if $::noauto;
require sbus_probing::main;
pci_probe($probe_type), usb_probe(), pcmcia_probe(), sbus_probing::main::probe();
}
sub matching_desc {
my ($regexp) = @_;
grep { $_->{description} =~ /$regexp/i } probeall();
}
sub stringlist {
map {
sprintf("%-16s: %s%s%s",
$_->{driver} ? $_->{driver} : 'unknown',
$_->{description} eq '(null)' ? sprintf("Vendor=0x%04x Device=0x%04x", $_->{vendor}, $_->{id}) : $_->{description},
$_->{media_type} ? sprintf(" [%s]", $_->{media_type}) : '',
$_->{subid} && $_->{subid} != 0xffff ? sprintf(" SubVendor=0x%04x SubDevice=0x%04x", $_->{subvendor}, $_->{subid}) : '',
);
} probeall(@_);
}
sub tryOpen($) {
local *F;
sysopen F, devices::make($_[0]), c::O_NONBLOCK() and *F;
}
sub tryWrite($) {
local *F;
sysopen F, devices::make($_[0]), 1 | c::O_NONBLOCK() and *F;
}
sub syslog {
-r "/tmp/syslog" and return map { /<\d+>(.*)/ } cat_("/tmp/syslog");
`$ENV{LD_LOADER} /bin/dmesg`;
}
sub get_mac_model() {
my $mac_model = cat_("/proc/device-tree/model") || die "Can't open /proc/device-tree/model";
log::l("Mac model: $mac_model");
$mac_model;
}
sub get_mac_generation() {
my $generation = cat_("/proc/cpuinfo") || die "Can't open /proc/cpuinfo";
my @genarray = split(/\n/, $generation);
my $count = 0;
while ($count <= @genarray) {
if ($genarray[$count] =~ /pmac-generation/) {
@genarray = split(/:/, $genarray[$count]);
return $genarray[1];
}
$count++;
}
return "Unknown Generation";
}
sub hasSMP { c::detectSMP() }
sub hasPCMCIA { $::o->{pcmcia} } #- because /proc/pcmcia seems not to be present on 2.4 at least (or use /var/run/stab)
#- try to detect a laptop, we assume pcmcia service is an indication of a laptop or
#- the following regexp to match graphics card apparently only used for such systems.
sub isLaptop {
hasPCMCIA() || (matching_desc('C&T.*655[45]\d') || matching_desc('C&T.*68554') ||
matching_desc('Neomagic.*Magic(Media|Graph)') ||
matching_desc('ViRGE.MX') || matching_desc('S3.*Savage.*[IM]X') ||
matching_desc('ATI.*(Mobility|LT)'));
}
sub hasUltra66 {
die "hasUltra66 deprecated";
#- keep it BUT DO NOT USE IT as now included in kernel.
cat_("/proc/cmdline") =~ /(ide2=(\S+)(\s+ide3=(\S+))?)/ and return $1;
my @l = map { $_->{verbatim} } matching_desc('HPT|Ultra66') or return;
my $ide = sprintf "ide2=0x%x,0x%x ide3=0x%x,0x%x",
@l == 2 ?
(map_index { hex($_) + (odd($::i) ? 1 : -1) } map { (split ' ')[3..4] } @l) :
(map_index { hex($_) + (odd($::i) ? 1 : -1) } map { (split ' ')[3..6] } @l);
log::l("HPT|Ultra66: found $ide");
$ide;
}
sub whatParport() {
my @res;
foreach (0..3) {
my $elem = {};
local *F;
open F, "/proc/parport/$_/autoprobe" or open F, "/proc/sys/dev/parport/parport$_/autoprobe" or next;
{
local $_;
while (<F>) {
if (/(.*):(.*);/) { #-#
$elem->{$1} = $2;
$elem->{$1} =~ s/Hewlett[-\s_]Packard/HP/;
$elem->{$1} =~ s/HEWLETT[-\s_]PACKARD/HP/;
}
}
}
push @res, { port => "/dev/lp$_", val => $elem };
}
@res;
}
sub usbMice { grep { $_->{media_type} =~ /\|Mouse/ && $_->{driver} !~ /Tablet:wacom/ ||
$_->{driver} =~ /Mouse:USB/ } usb_probe() }
sub usbWacom { grep { $_->{driver} =~ /Tablet:wacom/ } usb_probe() }
sub usbKeyboards { grep { $_->{media_type} =~ /\|Keyboard/ } usb_probe() }
sub usbStorage { grep { $_->{media_type} =~ /Mass Storage\|/ } usb_probe() }
sub usbKeyboard2country_code {
my ($usb_kbd) = @_;
local *F;
my $tmp;
sysopen(F, sprintf("/proc/bus/usb/%03d/%03d", $usb_kbd->{pci_bus}, $usb_kbd->{pci_device}), 0) and
sysseek F, 0x28, 0 and
sysread F, $tmp, 1 and
unpack("C", $tmp);
}
sub whatUsbport() {
# The printer manufacturer and model names obtained with the usb_probe()
# function were very messy, once there was a lot of noise around the
# manufacturers name ("Inc.", "SA", "International", ...) and second,
# all Epson inkjets answered with the name "Epson Stylus Color 760" which
# lead many newbies to install their Epson Stylus Photo XXX as an Epson
# Stylus Color 760 ...
#
# This routine based on an ioctl request gives very clean and correct
# manufacturer and model names, so that they are easily matched to the
# printer entries in the Foomatic database
my $i;
my @res;
foreach $i (0..15) {
my $port = "/dev/usb/lp$i";
my $realport = devices::make($port);
next if (!$realport);
next if (! -r $realport);
open PORT, $realport or do next;
my $idstr = "";
# Calculation of IOCTL function 0x84005001 (to get device ID
# string):
# len = 1024
# IOCNR_GET_DEVICE_ID = 1
# LPIOC_GET_DEVICE_ID(len) =
# _IOC(_IOC_READ, 'P', IOCNR_GET_DEVICE_ID, len)
# _IOC(), _IOC_READ as defined in /usr/include/asm/ioctl.h
# Use "eval" so that program does not stop when IOCTL fails
eval {
my $output = "\0" x 1024;
ioctl(PORT, 0x84005001, $output);
$idstr = $output;
} or do {
close PORT;
next;
};
close PORT;
# Remove non-printable characters
$idstr =~ tr/[\x00-\x1f]/\./;
# Extract the printer data from the ID string
my ($manufacturer, $model, $serialnumber, $description) =
("", "", "", "");
if (($idstr =~ /MFG:([^;]+);/) ||
($idstr =~ /MANUFACTURER:([^;]+);/)) {
$manufacturer = $1;
$manufacturer =~ s/Hewlett[-\s_]Packard/HP/;
$manufacturer =~ s/HEWLETT[-\s_]PACKARD/HP/;
}
# For HP's multi-function devices the real model name is in the "SKU"
# field. So use this field with priority for $model when it exists.
if (($idstr =~ /MDL:([^;]+);/) ||
($idstr =~ /MODEL:([^;]+);/)) {
$model = $1 if !$model;
}
if ($idstr =~ /SKU:([^;]+);/) {
$model = $1;
}
if (($idstr =~ /DES:([^;]+);/) ||
($idstr =~ /DESCRIPTION:([^;]+);/)) {
$description = $1;
$description =~ s/Hewlett[-\s_]Packard/HP/;
$description =~ s/HEWLETT[-\s_]PACKARD/HP/;
}
if ($idstr =~ /SE*R*N:([^;]+);/) {
$serialnumber = $1;
}
# Was there a manufacturer and a model in the string?
if (($manufacturer eq "") || ($model eq "")) {
next;
}
# No description field? Make one out of manufacturer and model.
if ($description eq "") {
$description = "$manufacturer $model";
}
# Store this auto-detection result in the data structure
push @res, { port => $port, val =>
{ CLASS => 'PRINTER',
MODEL => $model,
MANUFACTURER => $manufacturer,
DESCRIPTION => $description,
SERIALNUMBER => $serialnumber
}};
}
@res;
}
#-CLASS:PRINTER;
#-MODEL:HP LaserJet 1100;
#-MANUFACTURER:Hewlett-Packard;
#-DESCRIPTION:HP LaserJet 1100 Printer;
#-COMMAND SET:MLC,PCL,PJL;
sub whatPrinter {
my @res = (whatParport(), whatUsbport());
grep { $_->{val}{CLASS} eq "PRINTER"} @res;
}
sub whatPrinterPort() {
grep { tryWrite($_) } qw(/dev/lp0 /dev/lp1 /dev/lp2 /dev/usb/lp0 /dev/usb/lp1 /dev/usb/lp2 /dev/usb/lp3 /dev/usb/lp4 /dev/usb/lp5 /dev/usb/lp6 /dev/usb/lp7 /dev/usb/lp8 /dev/usb/lp9);
}
sub probeSerialDevices {
foreach (0..3) {
#- make sure the device are created before probing,
devices::make("/dev/ttyS$_");
#- and make sure the device is a real terminal (major is 4).
int((stat "/dev/ttyS$_")[6]/256) == 4 or $serialprobe{"/dev/ttyS$_"} = undef;
}
#- for device already probed, we can safely (assuming device are
#- not moved during install :-)
#- include /dev/mouse device if using an X server.
mkdir_p("/var/lock");
-l "/dev/mouse" and $serialprobe{"/dev/" . readlink "/dev/mouse"} = undef;
foreach (keys %serialprobe) { m|^/dev/(.*)| and touch "/var/lock/LCK..$1" }
print STDERR "Please wait while probing serial ports...\n";
#- start probing all serial ports... really faster than before ...
#- ... but still take some time :-)
local *F; open F, "$ENV{LD_LOADER} serial_probe |";
local $_;
my %current; while (<F>) {
$serialprobe{$current{DEVICE}} = { %current } and %current = () if /^\s*$/ && $current{DEVICE};
$current{$1} = $2 if /^([^=]+)=(.*?)\s*$/;
}
close F;
foreach (values %serialprobe) {
$_->{DESCRIPTION} =~ /modem/i and $_->{CLASS} = 'MODEM'; #- hack to make sure a modem is detected.
$_->{DESCRIPTION} =~ /olitec/i and $_->{CLASS} = 'MODEM'; #- hack to make sure such modem gets detected.
log::l("probed $_->{DESCRIPTION} of class $_->{CLASS} on device $_->{DEVICE}");
}
}
sub probeSerial($) { $serialprobe{$_[0]} }
sub hasModem($) {
$serialprobe{$_[0]} and $serialprobe{$_[0]}{CLASS} eq 'MODEM' and $serialprobe{$_[0]}{DESCRIPTION};
}
sub hasMousePS2 {
my $t; sysread(tryOpen($_[0]) || return, $t, 256) != 1 || $t ne "\xFE";
}
sub raidAutoStartIoctl {
local *F;
sysopen F, devices::make("md0"), 2 or return;
ioctl F, 2324, 0;
}
sub raidAutoStartRaidtab {
my (@parts) = @_;
require raid;
#- faking a raidtab, it seems to be working :-)))
#- (choosing any inactive md)
raid::inactivate_all();
foreach (@parts) {
my ($nb) = grep { !raid::is_active("md$_") } 0..7;
output("/etc/raidtab", "raiddev /dev/md$nb\n device " . devices::make($_->{device}) . "\n");
run_program::run('raidstart', devices::make("md$nb"));
}
}
sub raidAutoStart {
my (@parts) = @_;
log::l("raidAutoStart");
eval { modules::load('md') };
my %personalities = ('1' => 'linear', '2' => 'raid0', '3' => 'raid1', '4' => 'raid5');
raidAutoStartIoctl() or raidAutoStartRaidtab(@parts);
if (my @needed_perso = map {
if_(/^kmod: failed.*md-personality-(.)/ ||
/^md: personality (.) is not loaded/, $personalities{$1}) } syslog()) {
eval { modules::load(@needed_perso) };
raidAutoStartIoctl() or raidAutoStartRaidtab(@parts);
}
}
sub is_a_recent_computer {
my ($frequence) = map { /cpu MHz\s*:\s*(.*)/ } cat_("/proc/cpuinfo");
$frequence > 600;
}
sub usb_description2removable {
local ($_) = @_;
return 'camera' if /\bcamera\b/i;
return 'memory_card' if /\bmemory\s?stick\b/i || /\bcompact\s?flash\b/i || /\bsmart\s?media\b/i;
return 'memory_card' if /DiskOnKey/i || /IBM-DMDM/i;
return 'zip' if /\bzip\s?(100|250|750)/i;
return 'floppy' if /\bLS-?120\b/i;
return;
}
sub usb2removable {
my ($e) = @_;
$e->{usb_driver} or return;
if ($e->{usb_driver} =~ /Removable:(.*)/) {
return $1;
} elsif (my $name = usb_description2removable($e->{usb_description})) {
return $name;
}
undef;
}
sub suggest_mount_point {
my ($e) = @_;
my $name = $e->{media_type};
if (member($name, 'hd', 'fd')) {
if (exists $e->{usb_driver}) {
return usb2removable($e) || 'removable';
}
if (isZipDrive($e)) {
$name = 'zip';
} elsif ($name eq 'fd') {
$name = 'floppy';
} else {
log::l("set_removable_mntpoints: don't know what to with hd $e->{device}");
}
}
$name;
}
1;