Avanti Indietro Indice

10. Condividere una stampante Windows con macchine Linux

Per condividere una stampante su una macchina Windows, è necessario:

  1. Avere una configurazione corretta in /etc/printcap che corrisponda alla struttura delle directory locali (per le directory di spool, ecc.)
  2. Usare lo script /usr/bin/smbprint. Questo script è disponibile con il sorgente di Samba, purtroppo non con tutte le distribuzioni in formato binario. Più avanti verrà presentata una versione leggermente diversa di smbprint.
  3. Se si desidera convertire file ASCII in Postscript, è necessario avere nenscript, o qualcosa di equivalente. nenscript è un convertitore Postscript generalmente installato in /usr/bin.
  4. Si potrebbe desiderare rendere la stampa con Samba più semplice tramite un semplice front-end. Di seguito è riportato un semplice script ( print) in Perl per manipolare ASCII, Postscript...
  5. Si potrebbe anche usare MagicFilter per ottenere il risultato. I dettagli necessari per configurare MagicFilter sono presenti nello script perl. MagicFilter ha dei punti di favore in quanto conosce automaticamente come convertire tra un gran numero di formati.

La successiva configurazione di /etc/printcap è valida per una stampante HP 5MP su un host Windows NT. Le linee hanno il seguente significato:

cm

commento

lp

dispositivo da aprire per output

sd

directory di spool della stampante sulla macchina locale

af

file per registrare le transazioni

mx

massima grandezza per un file (zero significa nessun limite)

if

nome del filtro di output (script)

Per maggiori informazioni consultare il Printing HOWTO o le pagine del manuale per la voce printcap.


# /etc/printcap
#
# //zimmerman/oreilly via smbprint
#
lp:\
        :cm=HP 5MP Postscript OReilly on zimmerman:\
        :lp=/dev/lp1:\
        :sd=/var/spool/lpd/lp:\
        :af=/var/spool/lpd/lp/acct:\
        :mx#0:\
        :if=/usr/bin/smbprint:

È necessario accertarsi che le directory di spool e per la registrazione delle transazioni (log) esistano e siano scrivibili. Assicurarsi che la linea 'if' contenga un percorso corretto allo script di smbprint ( dato oltre) ed inoltre che il dispositivo puntato sia corretto (il file speciale /dev).

Di seguito lo script smbprint. Di solito è installato in /usr/bin ed è attribuibile, per quanto ne so, ad Andrew Tridgell, la persona che ha creato Samba. Viene fornito con la distribuzione in formato sorgente di Samba ma è riportato, essendo assente in certe distribuzioni in formato binario.

Potrebbe essere utile studiarlo attentamente, alcune piccole modifiche hanno dimostrato essere di enorme utilità.


#!/bin/sh -x

# Questo script è un filtro di input per la stampa via printcap da una
# macchina UNIX. Usa il programma smbclient per stampare il file sul
# server e servizio specificati usando il protocollo smb.
# Per esempio è possibile avere una linea del printcap del tipo:
#
# smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
#
# Che dovrebbe creare una stampante UNIX chiamata "smb" che stamperà
# attraverso questo script. E' necessario creare la directory di spool
# /usr/spool/smb con permessi, proprietario e gruppo appropriati per il
# sistema.

# Assegnare i valori che seguono in modo da accordarsi al server e
# e servizio su cui si desidera stampare. In questo esempio si suppone
# di avere un PC con WfWg chiamato "lapland" che ha una stampante
# esportata chiamata "printer" senza password.
#
# Script modificato da hamiltom@ecnz.co.nz (Michael Hamilton)
# in modo da poter leggere server, servizio e password dal file
# /usr/var/spool/lpd/PRINTNAME/.config
#
# Affinché tutto funzioni è necessario che esista una linea in
# /etc/printcap che includa il file di accounting (af=...):
#
#   cdcolour:\
#   :cm=CD IBM Colorjet on 6th:\
#   :sd=/var/spool/lpd/cdcolour:\
#   :af=/var/spool/lpd/cdcolour/acct:\
#   :if=/usr/local/etc/smbprint:\
#   :mx=0:\
#   :lp=/dev/null:
#
# Il file /usr/var/spool/lpd/PRINTNAME/.config dovrebbe contenere:
#   server=PC_SERVER
#   service=PR_SHARENAME
#   password="password"
#
# Esempio:
#   server=PAULS_PC
#   service=CJET_371
#   password=""

#
# File per i messaggi di debug, cambiare in /dev/null se si preferisce.
#
logfile=/tmp/smb-print.log
# logfile=/dev/null


#
# L'ultimo parametro del filtro è il file per la registrazione delle
# transazioni.
#
spool_dir=/var/spool/lpd/lp
config_file=$spool_dir/.config

# Le seguenti variabili dovrebbero essere assegnate nel file di
# configurazione:
#   server
#   service
#   password
#   user
eval `cat $config_file`

#
# Supporto per il debugging si può cambiare >> con > per risparmiare
# spazio.
#
echo "server $server, service $service" >> $logfile

(
# NOTA Si potrebbe desiderare di aggiungere la linea `echo translate'
# per avere la conversione automatica CR/LF quando si stampa.
    echo translate
    echo "print -"
    cat
) | /usr/bin/smbclient "\\\\$server\\$service" $password -U $user -N -P >> $logfile

La maggior parte delle distribuzioni Linux include nenscript per convertire documenti ASCII a Postscript. Lo script Perl che segue rende la vita più semplice agendo da semplice interfaccia per la stampa di Linux attraverso smbprint.

Usage: print [-a|c|p] <filename>
       -a prints <filename> as ASCII
       -c prints <filename> formatted as source code
       -p prints <filename> as Postscript
        If no switch is given, print attempts to
        guess the file type and print appropriately.

Usare smbprint per stampare file ASCII comporta a volte troncare le linee lunghe. Lo script che segue interrompe, se possibile, le linee lunghe su uno spazio (invece che in mezzo ad una parola).

La formattazione del codice sorgente è fatta con nenscript. Il file ASCII viene formattato su 2 colonne con un'intestazione (data, nome del file, ecc.) inoltre numera le linee. Usandolo come esempio, è possibile realizzare altri tipi di formattazione.

I documenti Postscript sono già correttamente formattati, quindi passano senza essere modificati.


#!/usr/bin/perl

# Script:   print
# Autore:   Brad Marshall, David Wood
#           Plugged In Communications
# Data:     960808
#
# Script per stampare su oreilly che è attualmente connessa a zimmerman
# Scopo:  Dati diversi tipi di file come argomenti, li elabora 
# correttamente per mandarli in pipe ad uno script di stampa Samba.
#
# Tipi di file correntemente supportati:
#
# ASCII      - Verifica che le linee più lunghe di $line_length caratteri
#              si interrompano su spazio.
# Postscript - Intrapresa nessuna azione.
# Codice     - Formatta in Postscript (usando nenscript) per rendere al
#              al meglio (in orizzontale, font, ecc.)
#

# Lunghezza massima concessa per ciascuna linea di testo ASCII.
$line_length = 76;

# Nome e percorso dello script di stampa Samba
$print_prog = "/usr/bin/smbprint";

# Nome e percorso di nenscript (converte ASCII-->Postscript)
$nenscript = "/usr/bin/nenscript";

unless ( -f $print_prog ) {
    die "Non trovo: $print_prog!";
}
unless ( -f $nenscript ) {
    die "Non trovo: $nenscript!";
}

&ParseCmdLine(@ARGV);

# DBG
print "Il file e' di tipo: $filetype\n";

if ($filetype eq "ASCII") {
    &wrap($line_length);
} elsif ($filetype eq "code") {
    &codeformat;
} elsif ($filetype eq "ps") {
    &createarray;
} else {
    print "Spiacente...file di tipo sconosciuto.\n";
    exit 0;
}
# Pipe the array to smbprint
open(PRINTER, "|$print_prog") || die "Impossibile aprire $print_prog: $!\n";
foreach $line (@newlines) {
    print PRINTER $line;
}
# Spedisce un linefeed extra nel caso in cui il file abbia l'ultima linea
# incompleta.
print PRINTER "\n";
close(PRINTER);
print "Finito.\n";
exit 0;

# --------------------------------------------------- #
#   Da questo punto in poi ci sono solo subroutine    #
# --------------------------------------------------- #

sub ParseCmdLine {
    # Analizza la linea di comand, cercando di riconoscere il tipo di
    # file.

        # Se esistono imposta $arg e $file agli argomenti.
    if ($#_ < 0) {
        &usage;
    }
    # DBG
#   foreach $element (@_) {
#       print "*$element* \n";
#   }

    $arg = shift(@_);
    if ($arg =~ /\-./) {
        $cmd = $arg;
    # DBG
#   print "\$cmd trovato.\n";

        $file = shift(@_);
    } else {
        $file = $arg;
    }

    # Definisce il tipo di file
    unless ($cmd) {
        # Nessun argomento

        if ($file =~ /\.ps$/) {
            $filetype = "ps";
        } elsif ($file =~ /\.java$|\.c$|\.h$|\.pl$|\.sh$|\.csh$|\.m4$|\.inc$|\.html$|\.htm$/) {
            $filetype = "code";
        } else {
            $filetype = "ASCII";
        }

        # Elabora $file in base al suo tipo e ritorna $filetype
    } else {
        # Il tipo che e' viene restituito in $arg
        if ($cmd =~ /^-p$/) {
            $filetype = "ps";
        } elsif ($cmd =~ /^-c$/) {
            $filetype = "code";
        } elsif ($cmd =~ /^-a$/) {
            $filetype = "ASCII"
        }
    }
}

sub usage {
    print "
Uso: print [-a|c|p] <nomefile>
       -a stampa <nomefile> come ASCII
       -c stampa <nomefile> formattato come codice sorgente
       -p stampa <nomefile> come Postscript
        Se non viene fornito alcun parametro, cerca di
        indovinare il tipo e stamparlo adeguatamente.\n
";
    exit(0);
}

sub wrap {
        # Crea un array di linee del file, dove ciascuna linea e' <
        # del numero di caratteri specificato, e termina solo su spazi.

        # Recupera il numero di caratteri a cui limitare la linea.
    $limit = pop(@_);

    # DBG
    #print "Entra subroutine wrap\n";
    #print "La lunghezza limite per la linea e' $limit\n";

    # Leggi il file, analizzalo e mettilo nell'array.
    open(FILE, "<$file") || die "Impossibile aprire: $file: $!\n";
    while(<FILE>) {
        $line = $_;

        # DBG
        #print "La linea e':\n$line\n";

        # Se la linea e' oltre il limite vai a capo.
        while ( length($line) > $limit ) {

            # DBG
            #print "Limita...";

            # Prendi i primi $limit +1 caratteri.
            $part = substr($line,0,$limit +1);

            # DBG
            #print "La linea parziale e':\n$part\n";

                        # verifica se l'ultimo carattere e' spazio
            $last_char = substr($part,-1, 1);
            if ( " " eq $last_char ) {
                # Se lo e' stampa il resto.

                # DBG
                #print "L'ultimo carattere era spazio\n";

                substr($line,0,$limit + 1) = "";
                substr($part,-1,1) = "";
                push(@newlines,"$part\n");
            } else {
                 # se non lo e', cerca l'ultimo spazio nella
                 # sottolinea e stampa fino a li'.

                # DBG
                #print "L'ultimo carattere non era spazio\n";

                 # RImuove i caratteri oltre $limit
                 substr($part,-1,1) = "";
                 # Rovescia la linea per rendere semplice da trovare
                 # l'ultimo carattere.
                 $revpart = reverse($part);
                 $index = index($revpart," ");
                 if ( $index > 0 ) {
                   substr($line,0,$limit-$index) = "";
                   push(@newlines,substr($part,0,$limit-$index)
                       . "\n");
                 } else {
                   # Non c'erano spazi cosi' stampa fino a $limit
                   substr($line,0,$limit) = "";
                   push(@newlines,substr($part,0,$limit)
                       . "\n");
                 }
            }
        }
        push(@newlines,$line);
    }
    close(FILE);
}

sub codeformat {
    # Chiama la funzione wrap e poi filtra il risultato attraverso
    # nenscript
    &wrap($line_length);

    # Manda il risultato attraverso nenscript per creare un file
    # Postscript che abbia un formato accettabile di stampa per
    # il codice sorgente (stile orizzontale, font Courier, numeri
    # di linea)
    # Per prima cosa stampa su un file temporaneo.
    $tmpfile = "/tmp/nenscript$$";
    open(FILE, "|$nenscript -2G -i$file -N -p$tmpfile -r") ||
        die "Non posso aprire nenscript: $!\n";
    foreach $line (@newlines) {
        print FILE $line;
    }
    close(FILE);

    # Legge il file temporaneo inun array per passarlo allo script di
    # stampa di Samba
    @newlines = ("");
    open(FILE, "<$tmpfile") || die "Impossibile aprire $file: $!\n";
    while(<FILE>) {
        push(@newlines,$_);
    }
    close(FILE);
    system("rm $tmpfile");
}

sub createarray {
    # Crea l'array per Postscript
    open(FILE, "<$file") || die "Impossibile aprire $file: $!\n";
    while(<FILE>) {
        push(@newlines,$_);
    }
    close(FILE);
}

A questo punto si può esaminare MagicFilter. Ringraziamenti ad Alberto Menegazzi ( flash.egon@iol.it) per queste informazioni.

Alberto scrive:

  1. Installare MagicFilter con il filtro per le stampanti in /usr/bin/local ma non si compili il file /etc/printcap con i suggerimenti dati dalla documentazione di MagicFilter.
  2. Si scriva >/etc/printcap in questo modo (l'esempio si riferisce alla LaserJet 4L dell'autore):

    lp|ljet4l:\
            :cm=HP LaserJet 4L:\
            :lp=/dev/null:\                         # or /dev/lp1
            :sd=/var/spool/lpd/ljet4l:\
            :af=/var/spool/lpd/ljet4l/acct:\
            :sh:mx#0:\
            :if=/usr/local/bin/main-filter:
    

    È necessario spiegare che 'lp=/dev/...' viene aperta e bloccata (locked) quindi è necessario utilizzare un dispositivo "virtuale" per ciascuna stampante remota che si debba usare.

    È possibile crearli ad esempio con: 'touch /dev/ljet41'

  3. Si scriva un filtro /usr/local/bin/main-filter come quello suggerito, usando ljet4l-filter invece di bf/cat/.

    Ad esempio:


    #! /bin/sh
    logfile=/var/log/smb-print.log
    spool_dir=/var/spool/lpd/ljet4l
    (
      echo "print -"
      /usr/local/bin/ljet4l-filter
    ) | /usr/bin/smbclient "\\\\SHIR\\HPLJ4" -N -P >> $logfile
    

C'è anche un riferimento dal Print2Win mini-Howto relativamente il locking dei dispositivi e sul perché conviene creare stampanti virtuali.


     Suggerimento da Rick Bressler:
     A titolo di suggerimento, la configurazione che segue non è
     una buona idea:

        :lp=/dev/null:\

     in quanto lpr apre in modo 'esclusivo' il file specificato
     tramite lp=.  È necessario per prevenire il tentativo di
     diversi processi di stampare contemporaneamente sulla
     stessa stampante.

     Un effetto collaterale è che in tal caso, eng e colour non
     possono stampare contemporaneamente, (di solito è più o
     meno trasparente dal momento che stampano velocemente, e
     dal momento che usano una coda, probabilmente non si nota),
     tuttavia ogni altro processo che cerca di scrivere a
     /dev/null sarà interrotto!

     Non si tratta di un grosso problema in un sistema per utente
     singolo.  L'autore ha un sistema con più di 50 stampanti.
     Sarebbe un problema in questo caso.

     La soluzione è quella di creare una stampante fittizia per
     ciascuna di quelle fisiche, esempio con il comando:

     touch /dev/eng


Avanti Indietro Indice