package printer;
# $Id$
#use diagnostics;
#use strict;
use common;
use run_program;
#-if we are in an DrakX config
my $prefix = "";
#-location of the printer database in an installed system
my $PRINTER_DB_FILE = "/usr/share/foomatic/db/compiled/overview.xml";
#-configuration directory of Foomatic
my $FOOMATICCONFDIR = "/etc/foomatic";
#-location of the file containing the default spooler's name
my $FOOMATIC_DEFAULT_SPOOLER = "$FOOMATICCONFDIR/defaultspooler";
#-Did we already read the subroutines of /usr/sbin/ptal-init?
my $ptalinitread = 0;
%spooler = (
_("CUPS - Common Unix Printing System") => "cups",
_("LPRng - LPR New Generation") => "lprng",
_("LPD - Line Printer Daemon") => "lpd",
_("PDQ - Print, Don't Queue") => "pdq"
# _("PDQ - Marcia, click here!") => "pdq"
);
%spooler_inv = reverse %spooler;
%shortspooler = (
_("CUPS") => "cups",
_("LPRng") => "lprng",
_("LPD") => "lpd",
_("PDQ") => "pdq"
);
%shortspooler_inv = reverse %shortspooler;
%lprcommand = (
"cups" => "lpr-cups",
"lprng" => "lpr-lpd",
"lpd" => "lpr-lpd",
"pdq" => "lpr-pdq"
);
%printer_type = (
_("Local printer") => "LOCAL",
_("Remote printer") => "REMOTE",
_("Printer on remote CUPS server") => "CUPS",
_("Printer on remote lpd server") => "LPD",
_("Network printer (TCP/Socket)") => "SOCKET",
_("Printer on SMB/Windows 95/98/NT server") => "SMB",
_("Printer on NetWare server") => "NCP",
_("Enter a printer device URI") => "URI",
_("Pipe job into a command") => "POSTPIPE"
);
%printer_type_inv = reverse %printer_type;
#------------------------------------------------------------------------------
sub set_prefix($) { $prefix = $_[0] }
sub default_printer_type($) { "LOCAL" }
sub spooler {
# LPD is taken from the menu for the moment because the classic LPD is
# highly unsecure. Depending on how the GNU lpr development is going on
# LPD support can be reactivated by uncommenting the line which is
# commented out now.
#return @spooler_inv{qw(cups lpd lprng pdq)};
return @spooler_inv{qw(cups lprng pdq)};
}
sub printer_type($) {
my ($printer) = @_;
for ($printer->{SPOOLER}) {
/cups/ && return @printer_type_inv{qw(LOCAL),
qw(LPD SOCKET SMB),
$::expert ? qw(URI) : ()};
/lpd/ && return @printer_type_inv{qw(LOCAL LPD SOCKET SMB NCP),
$::expert ? qw(POSTPIPE URI) : ()};
/lprng/ && return @printer_type_inv{qw(LOCAL LPD SOCKET SMB NCP),
$::expert ? qw(POSTPIPE URI) : ()};
/pdq/ && return @printer_type_inv{qw(LOCAL LPD SOCKET),
$::expert ? qw(URI) : ()};
}
}
sub get_default_spooler () {
if (-f "$prefix$FOOMATIC_DEFAULT_SPOOLER") {
open DEFSPOOL, "< $prefix$FOOMATIC_DEFAULT_SPOOLER";
my $spool = <DEFSPOOL>;
chomp $spool;
close DEFSPOOL;
return $spool if ($spool =~ /cups|lpd|lprng|pdq/);
}
}
sub set_default_spooler ($) {
my ($printer) = @_;
# Make Foomatic config directory if it does not exist yet
mkdir "$prefix$FOOMATICCONFDIR" if (!(-d "$prefix$FOOMATICCONFDIR"));
# Mark the default driver in a file
open DEFSPOOL, "> $prefix$FOOMATIC_DEFAULT_SPOOLER" ||
die "Cannot create $prefix$FOOMATIC_DEFAULT_SPOOLER!";
print DEFSPOOL $printer->{SPOOLER};
close DEFSPOOL;
}
sub set_permissions {
my ($file, $perms, $owner, $group) = @_;
# We only need to set the permissions during installation to be able to
# print test pages. After installation the devfsd daemon does the business
# automatically.
if (!$::isInstall) { return 1 }
if ($owner && $group) {
run_program::rooted($prefix, "/bin/chown", "$owner.$group", $file)
|| die "Could not start chown!";
} elsif ($owner) {
run_program::rooted($prefix, "/bin/chown", $owner, $file)
|| die "Could not start chown!";
} elsif ($group) {
run_program::rooted($prefix, "/bin/chgrp", $group, $file)
|| die "Could not start chgrp!";
}
run_program::rooted($prefix, "/bin/chmod", $perms, $file)
|| die "Could not start chmod!";
}
sub restart_service ($) {
my ($service) = @_;
# Exit silently if the service is not installed
return 1 if (!(-x "$prefix/etc/rc.d/init.d/$service"));
run_program::rooted($prefix, "/etc/rc.d/init.d/$service", "restart");
if (($? >> 8) != 0) {
return 0;
} else {
# CUPS needs some time to come up.
wait_for_cups() if ($service eq "cups");
return 1;
}
}
sub start_service ($) {
my ($service) = @_;
# Exit silently if the service is not installed
return 1 if (!(-x "$prefix/etc/rc.d/init.d/$service"));
run_program::rooted($prefix, "/etc/rc.d/init.d/$service", "start");
if (($? >> 8) != 0) {
return 0;
} else {
# CUPS needs some time to come up.
wait_for_cups() if ($service eq "cups");
return 1;
}
}
sub start_not_running_service ($) {
my ($service) = @_;
# Exit silently if the service is not installed
return 1 if (!(-x "$prefix/etc/rc.d/init.d/$service"));
run_program::rooted($prefix, "/etc/rc.d/init.d/$service", "status");
# The exit status is not zero when the service is not running
if (($? >> 8) != 0) {
run_program::rooted($prefix, "/etc/rc.d/init.d/$service", "start");
if (($? >> 8) != 0) {
return 0;
} else {
# CUPS needs some time to come up.
wait_for_cups() if ($service eq "cups");
return 1;
}
} else {
return 1;
}
}
sub stop_service ($) {
my ($service) = @_;
# Exit silently if the service is not installed
return 1 if (!(-x "$prefix/etc/rc.d/init.d/$service"));
run_program::rooted($prefix, "/etc/rc.d/init.d/$service", "stop");
if (($? >> 8) != 0) { return 0 } else { return 1 }
}
sub service_starts_on_boot ($) {
my ($service) = @_;
local *F;
open F, ($::testing ? $prefix : "chroot $prefix/ ") .
"/bin/sh -c \"export LC_ALL=C; /sbin/chkconfig --list $service 2>&1\" |" ||
return 0;
while (my $line = <F>) {
chomp $line;
if ($line =~ /:on/) {
close F;
return 1;
}
}
close F;
return 0;
}
sub start_service_on_boot ($) {
my ($service) = @_;
run_program::rooted($prefix, "/sbin/chkconfig", "--add", $service)
|| return 0;
return 1;
}
sub SIGHUP_daemon {
my ($service) = @_;
if ($service eq "cupsd") { $service = "cups" };
# PDQ has no daemon, exit.
if ($service eq "pdq") { return 1 };
# CUPS needs auto-correction for its configuration
run_program::rooted($prefix, "/usr/sbin/correctcupsconfig") if ($service eq "cups");
# Name of the daemon
my %daemons = (
"lpr" => "lpd",
"lpd" => "lpd",
"lprng" => "lpd",
"cups" => "cupsd",
"devfs" => "devfsd",
);
my $daemon = $daemons{$service};
$daemon = $service if (! defined $daemon);
# if ($service eq "cups") {
# # The current CUPS (1.1.13) dies on SIGHUP, do the normal restart.
# restart_service($service);
# # CUPS needs some time to come up.
# wait_for_cups();
# } else {
# Send the SIGHUP
run_program::rooted($prefix, "/usr/bin/killall", "-HUP", $daemon);
if ($service eq "cups") {
# CUPS needs some time to come up.
wait_for_cups();
}
return 1;
}
sub wait_for_cups {
# CUPS needs some time to come up. Wait up to 30 seconds, checking
# whether CUPS is ready.
my $cupsready = 0;
my $i;
for ($i = 0; $i < 30; $i++) {
run_program::rooted($prefix, "/usr/bin/lpstat", "-r");
if (($? >> 8) != 0) {
# CUPS is not ready, continue
sleep 1;
} else {
# CUPS is ready, quit
$cupsready = 1;
last;
}
}
return $cupsready;
}
sub assure_device_is_available_for_cups {
# Checks whether CUPS already "knows" a certain port, it does not
# know it usually when the appropriate kernel module is loaded
# after CUPS was started or when the printer is turned on after
# CUPS was started. CUPS 1.1.12 and newer refuses to set up queues
# on devices which it does not know, it points these queues to
# file:/dev/null instead. Restart CUPS if necessary to assure that
# CUPS knows the device.
my ($device) = @_;
local *F;
open F, ($::testing ? $prefix : "chroot $prefix/ ") .
"/bin/sh -c \"export LC_ALL=C; /usr/sbin/lpinfo -v\" |" ||
die "Could not run \"lpinfo\"!";
while (my $line = <F>) {
if ($line =~ /$device/) { # Found a line containing the device name,
# so CUPS knows it.
close F;
return 1;
}
}
close F;
return SIGHUP_daemon("cups");
}
sub network_running {
# If the network is not running return 0, otherwise 1.
local *F;
open F, ($::testing ? $prefix : "chroot $prefix/ ") .
"/bin/sh -c \"export LC_ALL=C; /sbin/ifconfig\" |" ||
die "Could not run \"ifconfig\"!";
while (my $line = <F>) {
if (($line !~ /^lo\s+/) && # The loopback device can have been
# started by the spooler's startup script
($line =~ /^(\S+)\s+/)) { # In this line starts an entry for a
# running network
close F;
return 1;
}
}
close F;
return 0;
}
sub get_security_level {
# Get security level by reading /etc/profile (only after install).
# This is a preliminary solution until msec puts the security level
# definition into the correct file.
$file = "/etc/profile";
if (-f $file) {
local *F;
open F, "< $file" || return 0;
while (my $line = <F>) {
if ($line =~ /^\s*SECURE_LEVEL=([0-5])\s*$/) {
close F;
return $1;
}
}
close F;
}
return 0;
}
sub spooler_in_security_level {
# Was the current spooler already added to the current security level?
my ($spooler, $level) = @_;
my $sp;
$sp = (($spooler eq "lpr") || ($spooler eq "lprng")) ? "lpd" : $spooler;
$file = "$prefix/etc/security/msec/server.$level";
if (-f $file) {
local *F;
open F, "< $file" || return 0;
while (my $line = <F>) {
if ($line =~ /^\s*$sp\s*$/) {
close F;
return 1;
}
}
close F;
}
return 0;
}
sub add_spooler_to_security_level {
my ($spooler, $level) = @_;
my $sp;
$sp = (($spooler eq "lpr") || ($spooler eq "lprng")) ? "lpd" : $spooler;
$file = "$prefix/etc/security/msec/server.$level";
if (-f $file) {
local *F;
open F, ">> $file" || return 0;
print F "$sp\n";
close F;
}
return 1;
}
sub files_exist {
my @files = @_;
for my $file (@files) {
return 0 if (! -f "$prefix$file"),
}
return 1;
}
sub set_alternative {
my ($command, $executable) = @_;
local *F;
# Read the list of executables for the given command to find the number
# of the desired executable
open F, ($::testing ? $prefix : "chroot $prefix/ ") .
"/bin/sh -c \"export LC_ALL=C; /bin/echo | update-alternatives --config $command \" |" ||
die "Could not run \"update-alternatives\"!";
my $choice = 0;
while (my $line = <F>) {
chomp $line;
if ($line =~ m/^[\* ][\+ ]\s*([0-9]+)\s+(\S+)\s*$/) { # list entry?
if ($2 eq $executable) {
$choice = $1;
last;
}
}
}
close F;
# If the executable was found, assign the command to it
if ($choice > 0) {
system(($::testing ? $prefix : "chroot $prefix/ ") .
"/bin/sh -c \"/bin/echo $choice | update-alternatives --config $command > /dev/null 2>&1\"");
}
return 1;
}
sub pdq_panic_button {
my $setting = $_[0];
if (-f "$prefix/usr/sbin/pdqpanicbutton") {
run_program::rooted($prefix, "/usr/sbin/pdqpanicbutton", "--$setting")
|| die "Could not $setting PDQ panic buttons!";
}
}
sub copy_printer_params($$) {
my ($from, $to) = @_;
map { $to->{$_} = $from->{$_} } grep { $_ ne 'configured' } keys %$from;
#- avoid cycles-----------------^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
}
sub getinfo($) {
my ($prefix) = @_;
my $printer = {};
my @QUEUES;
set_prefix($prefix);
# Initialize $printer data structure
resetinfo($printer);
return $printer;
}
#------------------------------------------------------------------------------
sub resetinfo($) {
my ($printer) = @_;
$printer->{QUEUE} = "";
$printer->{OLD_QUEUE} = "";
$printer->{OLD_CHOICE} = "";
$printer->{ARGS} = "";
$printer->{DBENTRY} = "";
$printer->{DEFAULT} = "";
$printer->{currentqueue} = {};
# -check which printing system was used previously and load the information
# -about its queues
read_configured_queues($printer);
}
sub read_configured_queues($) {
my ($printer) = @_;
my @QUEUES;
# Get the default spooler choice from the config file
$printer->{SPOOLER} ||= get_default_spooler();
if (!$printer->{SPOOLER}) {
#- Find the first spooler where there are queues
my $spooler;
for $spooler (qw(cups pdq lprng lpd)) {
#- poll queue info
local *F;
open F, ($::testing ? $prefix : "chroot $prefix/ ") .
"foomatic-configure -P -q -s $spooler |" ||
die "Could not run foomatic-configure";
eval (join('',(<F>)));
close F;
#- Have we found queues?
if ($#QUEUES != -1) {
$printer->{SPOOLER} = $spooler;
last;
}
}
} else {
#- Poll the queues of the current default spooler
local *F;
open F, ($::testing ? $prefix : "chroot $prefix/ ") .
"foomatic-configure -P -q -s $printer->{SPOOLER} |" ||
die "Could not run foomatic-configure";
eval (join('',(<F>)));
close F;
}
$printer->{configured} = {};
my $i;
my $N = $#QUEUES + 1;
for ($i = 0; $i < $N; $i++) {
$printer->{configured}{$QUEUES[$i]{queuedata}{queue}} =
$QUEUES[$i];
if ((!$QUEUES[$i]{make}) || (!$QUEUES[$i]{model})) {
if ($printer->{SPOOLER} eq "cups") {
$printer->{OLD_QUEUE} = $QUEUES[$i]{queuedata}{queue};
my $descr = get_descr_from_ppd($printer);
$descr =~ m/^([^\|]*)\|([^\|]*)\|.*$/;
$printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{make} ||= $1;
$printer->{configured}{$QUEUES[$i]{queuedata}{queue}}{queuedata}{model} ||= $2;
# Read out which PPD file was originally used to set up this
# queue
local *F;
if (open F, "< $prefix/etc/cups/ppd/$QUEUES[$i]{queuedata}{queue}.ppd") {
while (my $line = <F>) {
if ($line =~ /^\*%MDKMODELCHOICE:(.+)$/) {
|