diff options
Diffstat (limited to 'perl-install/scanner.pm')
| -rw-r--r--[-rwxr-xr-x] | perl-install/scanner.pm | 292 | 
1 files changed, 238 insertions, 54 deletions
| diff --git a/perl-install/scanner.pm b/perl-install/scanner.pm index 13d867bff..777f7192f 100755..100644 --- a/perl-install/scanner.pm +++ b/perl-install/scanner.pm @@ -1,7 +1,7 @@  package scanner; -# scanner.pm $Id$ -# Yves Duret <yduret at mandrakesoft.com> -# Copyright (C) 2001-2002 MandrakeSoft +# Yves Duret <yduret at mandriva.com> +# Till Kamppeter <till at mandriva.com> +# Copyright (C) 2001-2008 Mandriva  #  # This program is free software; you can redistribute it and/or modify  # it under the terms of the GNU General Public License as published by @@ -19,7 +19,6 @@ package scanner;  #  # pbs/TODO:  # - scsi mis-configuration (should work better now) -# - devfs use dev_is_devfs()  # - with 2 scanners same manufacturer -> will overwrite previous conf -> only 1 conf !! (should work now)  # - lp: see printerdrake  # - install: prefix --> done (partially) @@ -34,8 +33,8 @@ my $scannerDBdir = "$::prefix$ENV{SHARE_PATH}/ldetect-lst";  our $scannerDB = readScannerDB("$scannerDBdir/ScannerDB");  sub confScanner { -    my ($model, $port, $vendor, $product) = @_; -    $port ||= detect_devices::dev_is_devfs() ? "$::prefix/dev/usb/scanner0" : "$::prefix/dev/scanner"; +    my ($model, $port, $vendor, $product, $firmware) = @_; +    $port ||= "/dev/scanner";      my $a = $scannerDB->{$model}{server};      #print "file:[$a]\t[$model]\t[$port]\n| ", (join "\n| ", @{$scannerDB->{$model}{lines}}),"\n";      my @driverconf = cat_("$sanedir/$a.conf"); @@ -47,11 +46,13 @@ sub confScanner {  	next if $line =~ /\$VENDOR/;  	$line =~ s/\$PRODUCT/$product/g if $product;  	next if $line =~ /\$PRODUCT/; +	$line =~ s/\$FIRMWARE/$firmware/g if $firmware; +	next if $line =~ /\$FIRMWARE/;  	my $linetype;  	if ($line =~ /^(\S*)LINE\s+(.*?)$/) { -         $linetype = $1; -         $line = $2; -     } +	    $linetype = $1; +	    $line = $2; +	}  	next if !$line;  	if (!$linetype ||  	    ($linetype eq "USB" && ($port =~ /usb/i || $vendor)) || @@ -60,6 +61,8 @@ sub confScanner {  	    ($linetype eq "SCSI" && !$vendor &&  	     $port =~ m!(/sg|scsi|/scanner)!i)) {  	    handle_configs::set_directive(\@driverconf, $line, 1); +	} elsif ($linetype eq "FIRMWARE" && $firmware) { +	    handle_configs::set_directive(\@driverconf, $line, 0);  	}      }      output("$sanedir/$a.conf", @driverconf); @@ -73,12 +76,60 @@ sub add2dll {      output("$sanedir/dll.conf", @dllconf);  } -sub configured() { +sub setfirmware { +    my ($backend, $firmwareline) = @_; +    my @driverconf = cat_("$sanedir/$backend.conf"); +    handle_configs::set_directive(\@driverconf, $firmwareline, 0); +    output("$sanedir/$backend.conf", @driverconf); +} + +sub installfirmware { +    # Install the firmware file in /usr/share/sane/firmware +    my ($firmware, $backend) = @_; +    return "" if !$firmware; +    $in ||= interactive->vnew; +    # Install firmware +    run_program::rooted($::prefix, "mkdir", "-p", +			"/usr/share/sane/firmware") or do { +			    $in->ask_warn(N("Error"), +					  N("Could not create directory /usr/share/sane/firmware!")); +			    return ""; +			}; +    # Link /usr/share/sane/firmware to /usr/share/sane/<backend name> as +    # some backends ignore the supplied absolute path to the firmware file +    # and always search their own directory +    if ($backend) { +	run_program::rooted($::prefix, "ln", "-sf", +			    "/usr/share/sane/firmware", +			    "/usr/share/sane/$backend") or do { +				$in->ask_warn(N("Error"), +					      N("Could not create link /usr/share/sane/%s!", $backend)); +				return ""; +			    }; +    } +    run_program::rooted($::prefix, "cp", "-f", $firmware, +			"/usr/share/sane/firmware") or do { +			    $in->ask_warn(N("Error"), +					  N("Could not copy firmware file %s to /usr/share/sane/firmware!", $firmware)); +			    return ""; +			}; +    $firmware =~ s!^(.*)(/[^/]+)$!/usr/share/sane/firmware$2!; +    run_program::rooted($::prefix, "chmod", "644", +			$firmware) or do { +			    $in->ask_warn(N("Error"), +					  N("Could not set permissions of firmware file %s!", $firmware)); +			    return ""; +			}; +    return $firmware; +} + +sub configured { +    my ($in) = @_;      my @res; +    my $parportscannerfound = 0;      # Run "scanimage -L", to find the scanners which are already working -    local *LIST; -    open LIST, "LC_ALL=C scanimage -L |"; -    while (my $line = <LIST>) { +    open my $LIST, "LC_ALL=C scanimage -L |"; +    while (my $line = <$LIST>) {  	if ($line =~ /^\s*device\s*`([^`']+)'\s+is\s+a\s+(\S.*)$/) {  	    # Extract port and description  	    my $port = $1; @@ -86,28 +137,109 @@ sub configured() {  	    # Remove duplicate scanners appearing through saned and the  	    # "net" backend  	    next if $port =~ /^net:(localhost|127.0.0.1):/; +	    # Is the scanner hooked to a parallel or serial port? +	    if ($port =~ /(parport|pt_drv|parallel|ttys)/i) { +		$parportscannerfound = 1; +	    } +	    # Determine which SANE backend the scanner in question uses +	    my $backend; +            $backend = $1 if $port =~ /^([^:]+):/; +	    # Does the scanner need a firmware file +	    my $firmwareline = firmwareline($backend);  	    # Store collected data  	    push @res, {   		port => $port,   		val => {   		    DESCRIPTION => $description, -		}  +		    if_($backend, BACKEND => $backend), +		    if_($firmwareline, +		     FIRMWARELINE => $firmwareline), +		}  	    };  	}      } -    close LIST; +    close $LIST; +    # We have a parallel port scanner, make it working for non-root users +    nonroot_access_for_parport($parportscannerfound, $in);      return @res;  } +sub nonroot_access_for_parport { + +    # This function configures a non-root access for parallel port +    # scanners by running saned as root, exporting the scanner to +    # localhost and letting the user's frontend use the "net" backend +    # to access the scanner through the loopback network device. + +    # See also +    # http://www.linuxprinting.org/download/digitalimage/Scanning-as-Normal-User-on-Wierd-Scanner-Mini-HOWTO.txt + +    # Desired state of this facility: 1: Enable, 0: Disable +    my ($enable, $in) = @_; +    # Is saned running? +    my $sanedrunning = services::starts_on_boot("saned.socket"); +    # Is the "net" SANE backend active +    my $netbackendactive = find { /^\s*net\s*$/ } +      cat_("/etc/sane.d/dll.conf"); +    # Set this to 1 to tell the caller that the list of locally available +    # scanners has changed (Here if the SANE client configuration has +    # changed) +    my $changed = 0; +    my $importschanged = 0; +    if ($enable) { +	# Enable non-root access +	 +	# Install/start saned +	if (!$sanedrunning) { +	    # Make sure saned is installed and running +	    if (!files_exist('/usr/sbin/saned')) { +		if (!$in->do_pkgs->install('saned')) { +		    $in->ask_warn(N("Scannerdrake"), +				  N("Could not install the packages needed to share your scanner(s).") . " " . +				  N("Your scanner(s) will not be available for non-root users.")); +		} +		return 0; +	    } +	} + +	# Read list of hosts to where to export the local scanners +	my @exports = cat_("/etc/sane.d/saned.conf"); +	# Read list of hosts from where to import scanners +	my @imports = cat_("/etc/sane.d/net.conf"); +	# Add "localhost" to the machines which saned exports +	handle_configs::set_directive(\@exports, "localhost") +	    if !member("localhost\n", @exports); +	# Add "localhost" to the machines which "net" imports +	handle_configs::set_directive(\@imports, "localhost") +	    if !member("localhost\n", @imports); +	# Write /etc/sane.d/saned.conf +	output("/etc/sane.d/saned.conf", @exports); +	# Write /etc/sane.d/net.conf +	output("/etc/sane.d/net.conf", @imports); + +	# Make sure that the "net" backend is active +	scanner::add2dll("net"); +	 +	# (Re)start saned and make sure that it gets started on +	# every boot +	services::enable("saned.socket"); +    } + +    return 1; +} +  sub detect {      my @configured = @_;      my @res;      # Run "sane-find-scanner", this also detects USB scanners which only      # work with libusb. -    local *DETECT; -    open DETECT, "LC_ALL=C sane-find-scanner -q |"; -    while (my $line = <DETECT>) { -	my ($vendorid, $productid, $make, $model, $description, $port); + +    my @devices = detect_devices::probeall(); + +    open my $DETECT, "LC_ALL=C sane-find-scanner -q |"; +    while (my $line = <$DETECT>) { +	my ($vendorid, $productid, $make, $model, $description, $port, $driver); +	my $real_device;  	if ($line =~ /^\s*found\s+USB\s+scanner/i) {  	    # Found an USB scanner  	    if ($line =~ /vendor=(0x[0-9a-f]+)[^0-9a-f\[]+[^\[]*\[([^\[\]]+)\].*prod(|uct)=(0x[0-9a-f]+)[^0-9a-f\[]+[^\[]*\[([^\[\]]+)\]/) { @@ -123,11 +255,22 @@ sub detect {  		$productid = $3;  	    }  	    if ($vendorid && $productid) { +		my ($vendor) = $vendorid =~ /0x([0-9a-f]+)/; +		my ($id) = $productid =~ /0x([0-9a-f]+)/; +		my ($device) = grep { sprintf("%04x", $_->{vendor}) eq $vendor && sprintf("%04x", $_->{id}) eq $id } @devices; + +		if ($device) { +		    $driver = $device->{driver}; +		    $real_device = $device; +		} else { +		    #warn "Failed to lookup $vendorid and $productid!\n"; +		} +                   		# We have vendor and product ID, look up the scanner in  		# the usbtable -		foreach my $entry (cat_("$scannerDBdir/usbtable")) { +		foreach my $entry (common::catMaybeCompressed("$scannerDBdir/usbtable")) {  		    if ($entry =~  -			/^\s*$vendorid\s+$productid\s+.*\"([^\"]+)\"\s*$/) { +			/^\s*$vendorid\s+$productid\s+.*"([^"]+)"\s*$/) {  			$description = $1;  			$description =~ s/Seiko\s+Epson/Epson/i;  			if ($description =~ /^([^\|]+)\|(.*)$/) { @@ -140,7 +283,7 @@ sub detect {  	    }  	} elsif ($line =~ /^\s*found\s+SCSI/i) {  	    # SCSI scanner -	    if ($line =~ /\"([^\"\s]+)\s+([^\"]+?)\s+([^\"\s]+)\"/) { +	    if ($line =~ /"([^"\s]+)\s+([^"]+?)\s+([^"\s]+)"/) {  		$make = $1;  		$model = $2;  		$description = "$make|$model"; @@ -183,10 +326,12 @@ sub detect {  		DESCRIPTION => $description,  		id => $productid,  		vendor => $vendorid, +		driver => $driver, +		drakx_device => $real_device,  	    }   	};      } -    close DETECT; +    close $DETECT;      if (@configured) {  	# Remove scanners which are already working  	foreach my $d (@res) { @@ -205,7 +350,8 @@ sub detect {  	}  	@res = grep { ! $_->{configured} } @res;      } -    return @res; +    # blacklist device that have a driver b/c of buggy sane-find-scanner: +    return grep { member($_->{val}{driver}, qw(scanner unknown usbcore)) } @res;  }  sub resolve_symlinks { @@ -223,7 +369,7 @@ sub resolve_symlinks {  	return $file;      }      while (1) { -	my $ls = `ls -l $file`; +	my $ls = `ls -l $file 2> /dev/null`;  	if ($ls =~ m!\s($file)\s*\->\s*(\S+)\s*$!) {  	    my $target = $2;  	    if ($target !~ m!^/! && $file =~ m!^(.*)/[^/]+$!) { @@ -239,21 +385,19 @@ sub resolve_symlinks {  sub get_usb_ids_for_port {      my ($port) = @_; -    local *DETECT;      if ($port =~ /^\s*libusb:(\d+):(\d+)\s*$/) {  	# Use "lsusb" to find the USB IDs -	open DETECT, "LC_ALL=C lsusb -s $1:$2 |"; -	while (my $line = <DETECT>) { +	open my $DETECT, "LC_ALL=C lsusb -s $1:$2 |"; +	while (my $line = <$DETECT>) {  	    if ($line =~ /ID\s+([0-9a-f]+):(0x[0-9a-f]+)($|\s+)/) {  		# Scanner connected via scanner.o kernel module  		return "0x$1", "0x$2"; -		last;  	    }  	}      } else {  	# Run "sane-find-scanner" on the port -	open DETECT, "LC_ALL=C sane-find-scanner -q $port |"; -	while (my $line = <DETECT>) { +	open my $DETECT, "LC_ALL=C sane-find-scanner -q $port |"; +	while (my $line = <$DETECT>) {  	    if ($line =~ /^\s*found\s+USB\s+scanner/i) {  		if ($line =~ /vendor=(0x[0-9a-f]+)[^0-9a-f]+.*prod(|uct)=(0x[0-9a-f]+)[^0-9a-f]+/) {  		    # Scanner connected via scanner.o kernel module @@ -264,18 +408,46 @@ sub get_usb_ids_for_port {      }  } +sub readconfiglinetemplates { +    # Read templates for configuration file lines +    my %configlines; +    my $backend; +    foreach my $line (cat_("$scannerDBdir/scannerconfigs")) { +	chomp $line; +	if ($line =~ /^\s*SERVER\s+(\S+)\s*$/) { +	    $backend = $1; +	} elsif ($backend) { +	    push @{$configlines{$backend}}, $line; +	} +    } +    return \%configlines; +} + +sub firmwareline { +    # Determine whether the given SANE backend supports a firmware file +    # and return the line needed in the config file +    my ($backend) = @_; +    # Read templates for configuration file lines +    my %configlines = %{readconfiglinetemplates()}; +    # Does the backend support a line for the firmware? +    my @firmwarelines = (grep { s/^FIRMWARELINE // } @{$configlines{$backend}}); +    return join("\n", @firmwarelines); +} +  sub readScannerDB {      my ($file) = @_;      my ($card, %cards);      my $F = common::openFileMaybeCompressed($file); -    my ($lineno, $cmd, $val) = 0; +    my ($cmd, $val); +    my $lineno = 0;      my $fs = {          LINE => sub { push @{$card->{lines}}, "LINE $val" },          SCSILINE => sub { push @{$card->{lines}}, "SCSILINE $val" },          USBLINE => sub { push @{$card->{lines}}, "USBLINE $val" },          PARPORTLINE => sub { push @{$card->{lines}}, "PARPORTLINE $val" }, +        FIRMWARELINE => sub { push @{$card->{lines}}, "FIRMWARELINE $val" },  	NAME => sub {  	    #$cards{$card->{type}} = $card if ($card and !$card->{flags}{unsupported});  	    $cards{$card->{type}} = $card if $card; @@ -293,7 +465,13 @@ sub readScannerDB {  	ASK => sub { $card->{ask} = $val },  	SERVER => sub { $card->{server} = $val },  	DRIVER => sub { $card->{driver} = $val }, +	KERNEL => sub { push(@{$card->{kernel}}, $val) }, +	SCSIKERNEL => sub { push(@{$card->{scsikernel}}, $val) }, +	USBKERNEL => sub { push(@{$card->{usbkernel}}, $val) }, +	PARPORTKERNEL => sub { push(@{$card->{parportkernel}}, $val) },  	UNSUPPORTED => sub { $card->{flags}{unsupported} = 1 }, +	MANUAL => sub { $card->{flags}{manual} = 1 }, +	MANUALREQUIRED => sub { $card->{flags}{manual} = 2 },  	COMMENT => sub {},      }; @@ -311,13 +489,13 @@ sub readScannerDB {  }  sub updateScannerDBfromUsbtable() { -    substInFile { s/END// } "ScannerDB"; +    substInFile { s/^END// } "ScannerDB";      my $to_add = "# generated from usbtable by scannerdrake\n";      foreach (cat_("$ENV{SHARE_PATH}/ldetect-lst/usbtable")) {  	my ($vendor_id, $product_id, $mod, $name) = chomp_(split /\s/,$_,4);  	next if $mod ne '"scanner"'; -	$name =~ s/\"(.*)\"$/$1/; -	if (member($name, keys %$scanner::scannerDB)) { +	$name =~ s/"(.*)"$/$1/; +	if ($scanner::scannerDB->{$name}) {  	    print "#[$name] already in ScannerDB!\n";  	    next;  	} @@ -330,7 +508,7 @@ sub updateScannerDBfromUsbtable() {  sub updateScannerDBfromSane {      my ($sanesrcdir) = @_; -    substInFile { s/END// } "ScannerDB"; +    substInFile { s/^END// } "ScannerDB";      my $to_add = "# generated from Sane by scannerdrake\n";      # for compat with our usbtable @@ -342,6 +520,7 @@ sub updateScannerDBfromSane {  		   "Fujitsu Computer Products of America" => "Fujitsu",  		   "HP" => sub { $_[0] =~ s/HP\s/Hewlett-Packard|/; $_[0] =~ s/HP4200/Hewlett-Packard|ScanJet 4200C/; $_[0] },  		   "Hewlett-Packard" => sub { $_[0] =~ s/HP 3200 C/Hewlett-Packard|ScanJet 3200C/ or $_[0] = "Hewlett-Packard|$_[0]"; $_[0] }, +		   "Hewlett Packard" => "Hewlett-Packard",  		   "Kodak" => "Kodak Co.",  		   "Mustek" => "Mustek Systems Inc.",  		   "NEC" => "NEC Systems", @@ -355,31 +534,34 @@ sub updateScannerDBfromSane {  		  };      # Read templates for configuration file lines -    my %configlines; -    my $backend; -    foreach my $line (cat_("$scannerDBdir/scannerconfigs")) { -	chomp $line; -	if ($line =~ /^\s*SERVER\s+(\S+)\s*$/) { -	    $backend = $1; -	} elsif ($backend) { -	    push @{$configlines{$backend}}, $line; -	} -    } +    my %configlines = %{readconfiglinetemplates()}; -    foreach my $f (glob_("$sanesrcdir/doc/descriptions/*.desc"), glob_("$sanesrcdir/doc/descriptions-external/*.desc")) { +    foreach my $ff (glob_("$sanesrcdir/doc/descriptions/*.desc"), glob_("$sanesrcdir/doc/descriptions-external/*.desc"), "UNSUPPORTED") { +	my $f = $ff; +	# unsupported.desc must be treated separately, as the list of +	# unsupported scanners in SANE is out of date. +	next if $f =~ /unsupported.desc$/; +	# Treat unsupported.desc in the end +	$f = "$sanesrcdir/doc/descriptions/unsupported.desc" if +	    $f eq "UNSUPPORTED";  	my $F = common::openFileMaybeCompressed($f);  	$to_add .= "\n# from $f"; -	my ($lineno, $cmd, $val) = 0; +	my ($lineno, $cmd, $val);  	my ($name, $intf, $comment, $mfg, $backend);  	my $fs = {  		  backend => sub { $backend = $val },  		  mfg => sub { $mfg = $val; $name = undef },#bug when a new mfg comes. should called $fs->{ $name }(); but ??  		  model => sub {  		      unless ($name) { $name = $val; return } -		      $name = member($mfg, keys %$sane2DB) ? +		      $name = exists $sane2DB->{$mfg} ?  			ref($sane2DB->{$mfg}) ? $sane2DB->{$mfg}($name) : "$sane2DB->{ $mfg }|$name" : "$mfg|$name"; -		      if (0 && member($name, keys %$scanner::scannerDB)) { -			  print "#[$name] already in ScannerDB!\n"; +		      # When adding the unsupported scanner models, check +		      # whether the model is not already supported. To +		      # compare the names ignore upper/lower case. +		      my $searchname = quotemeta($name); +		      if ($backend =~ /unsupported/i && +			  $to_add =~ /^NAME $searchname$/im) { +			  $to_add .= "# $name already supported!\n";  		      } else {  			  # SANE bug: "snapscan" calls itself "SnapScan"  			  $backend =~ s/SnapScan/snapscan/g; @@ -388,8 +570,10 @@ sub updateScannerDBfromSane {  			  # this backend and add what is needed for the  			  # interfaces of this scanner  			  foreach my $line (@{$configlines{$backend}}) { -			      my $i = $1 if $line =~ /^\s*(\S*?)LINE/; -			      if (!$i || $intf =~ /$i/i) { +			      my $i; +                              $i = $1 if $line =~ /^\s*(\S*?)LINE/; +			      if (!$i || $i eq "FIRMWARE" ||  +				  $intf =~ /$i/i) {  				  $to_add .= "$line\n";  			      }  			  } @@ -409,7 +593,7 @@ sub updateScannerDBfromSane {  	while (<$F>) { $lineno++;  		       s/\s+$//;  		       /^;/ and next; -		       ($cmd, $val) = /:(\S+)\s*\"([^;]*)\"/ or next; #log::l("bad line $lineno ($_)"), next; +		       ($cmd, $val) = /:(\S+)\s*"([^;]*)"/ or next; #log::l("bad line $lineno ($_)"), next;  		       my $f = $fs->{$cmd};  		       $f ? $f->() : log::l("unknown line $lineno ($_)");  		   } | 
