(M)  s i s t e m a   o p e r a c i o n a l   m a g n u x   l i n u x ~/ · documentação · suporte · sobre

  Next Previous Contents

9. Compartilhando um impressora Windows com máquinas Linux

Para compartilhar uma impressora de uma máquina Windows, você deve fazer o seguinte:

  1. Você deve possuir as entradas apropriadas no /etc/printcap e elas devem corresponder a estrutura local de diretórios (para o diretório de spool, etc).
  2. Você deve ter o script /usr/bin/smbprint. Ele vem junto dos fontes do Samba, mas não em todas as distribuições binárias. Uma cópia levemente modificada será discutida abaixo.
  3. Se você quer converter arquivos ASCII para Postscript, você deve ter o nenscript, ou um equivalente. nescript é um conversor para Postscript e é geralmente instalado em /usr/bin.
  4. Você pode querer fazer a impressão do Samba ficar mais fácil tendo um programa interface que torne-a simples de usar. Um script em Perl simples para manipular ASCII, Postscript ou Postscript criado é dado abaixo.
  5. Você pode também usar o MagicFilter para fazer o item acima. Detalhes sobre como configurar o MagicFilter são fornecidos depois do script perl. O MagicFilter tem vantegens porque ele sabe como converter automaticamente muitos formatos de arquivo.
    1. A entrada no arquivo /etc/printcap abaixo é para um impressora HP 5MP numa máquina Windows NT. As entradas são como as que seguem abaixo:
              cm - comentário
              lp - dispositivo a abrir para saída
              sd - o diretório de spool da impressora (na máquina local).
              af - o arquivo de registro (contabilidade)
              mx - o tamanho máximo de arquivo (zero é ilimitado)
              if - o nome do filtro de entrada.
      

      Para mais informações, veja o Printing HOWTO ou a página man do 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:
      

      Tenha certeza que os diretórios de spool e registro (contabilidade) existem e são graváveis. Assegure-se que a linha que tem o 'if' contenha o caminho correto para o script smbprint (dado abaixo) e tenha certeza que o dispositivo apropriado tenha sido indicado (o arquivo especial /dev). Depois vem o próprio script smbprint. Ele normalmente é colocado em /usr/bin e é atribuído a Andrew Tridgell, a pessoa que criou o Samba, até onde eu sei. Ele vem junto da distribuição dos fontes do Samba, mas não vem em algumas distribuições binárias, assim sendo eu o reproduzi aqui. Você pode quer dar uma olhada cuidadosa nele. Há algumas pequenas alterações que se mostraram muito úteis.

      #!/bin/sh -x
      
      # This script is an input filter for printcap printing on a unix machine. It
      # uses the smbclient program to print the file to the specified smb-based 
      # server and service.
      # For example you could have a printcap entry like this
      #
      # smb:lp=/dev/null:sd=/usr/spool/smb:sh:if=/usr/local/samba/smbprint
      #
      # which would create a unix printer called "smb" that will print via this 
      # script. You will need to create the spool directory /usr/spool/smb with
      # appropriate permissions and ownerships for your system.
      
      # Set these to the server and service you wish to print to 
      # In this example I have a WfWg PC called "lapland" that has a printer 
      # exported called "printer" with no password.
      
      #
      # Script further altered by hamiltom@ecnz.co.nz (Michael Hamilton)
      # so that the server, service, and password can be read from 
      # a /usr/var/spool/lpd/PRINTNAME/.config file.
      #
      # In order for this to work the /etc/printcap entry must include an 
      # accounting file (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:
      #
      # The /usr/var/spool/lpd/PRINTNAME/.config file should contain:
      #   server=PC_SERVER
      #   service=PR_SHARENAME
      #   password="password"
      #
      # E.g.
      #   server=PAULS_PC
      #   service=CJET_371
      #   password=""
      
      #
      # Debugging log file, change to /dev/null if you like.
      #
      logfile=/tmp/smb-print.log
      # logfile=/dev/null
      
      
      #
      # The last parameter to the filter is the accounting file name.
      #
      spool_dir=/var/spool/lpd/lp
      config_file=$spool_dir/.config
      
      # Should read the following variables set in the config file:
      #   server
      #   service
      #   password
      #   user
      eval `cat $config_file`
      
      #
      # Some debugging help, change the >> to > if you want to same space.
      #
      echo "server $server, service $service" >> $logfile
      
      (
      # NOTE You may wish to add the line `echo translate' if you want automatic
      # CR/LF translation when printing.
              echo translate
              echo "print -"
              cat
      ) | /usr/bin/smbclient "\\\\$server\\$service" $password -U $user -N -P >> $logfile
      

      A maioria das distribuições Linux vem com o nenscript para converter documentos ASCII em Postscript. O script perl abaixo facilita a nossa vida fornecendo uma interface simples ao Linux para impressão via smbprint.
      Uso: print [-a|c|p] <nomedoarquivo>
             -a imprime <nomedoarquivo> como ASCII
             -c imprime <nomedoarquivo> como arquivo fonte formatado
             -p imprime <nomedoarquivo> como Postscript
              Se nenhuma opção for dada, print tenta descobrir o 
              tipo de arquivo e imprimi-lo apropriadamente.
      

      Usando o smbprint para imprimir arquivos ASCII tende a cortar linhas grandes. Este script quebra linhas grandes com espaços em branco (ao invés de no meio das palavras), se possível. A formatação do código fonte é feita com o nescript. Ele pega um arquivo ASCII e o formata em duas colunas com um cabeçalho legal (data, nome do arquivo, etc). Ele também numera as linhas. Usando isto como exemplo, outros tipos de formatação podem ser obtidos. Documentos Postscript já estão formatados adequadamente, então repasse-os automaticamente.

      #!/usr/bin/perl
      
      # Script:   print
      # Autores:  Brad Marshall, David Wood
      #             Plugged In Communications
      # Data:     960808
      #
      # Script para imprimir numa impressora Postscript printer via Samba.
      # Finalidade: Pega arquivos de vários tipos e arqumentos e os processa 
      # apropriadamente para um canal(pipe) do script de impressão do Samba.
      #
      # Currently supported file types:
      # 
      # ASCII      - assegura-se que as linhas maiores que $line_length caracteres 
      #              sejam quebradas nos espaçoes em branco.
      # Postscript - Não faz nada.
      # Code       - Formata em Postscript (usando nenscript) para aparecer
      #              adequadamente(landscape, font, etc).
      #
      
      # Determina o maior comprimento de linha permitido em textos ASCII
      $line_length = 76;
      
      # Determina o nome e o caminho do script de impressão do samba
      $print_prog = "/usr/bin/smbprint";
      
      # Determina o nome e o caminho para o nescript (o conversor ASCII-->Postscript)
      $nenscript = "/usr/bin/nenscript";
      
      unless ( -f $print_prog ) {
              die "Não pude encontrar $print_prog!";
      }
      unless ( -f $nenscript ) {
              die "Não pude encontrar $nenscript!";
      }
      
      &ParseCmdLine(@ARGV);
      
      # DBG
      print "Arquivo do tipo $filetype\n";
      
      if ($filetype eq "ASCII") {
              &wrap($line_length);
      } elsif ($filetype eq "code") {
              &codeformat;
      } elsif ($filetype eq "ps") {
              &createarray;
      } else {
              print "Desculpe... tipo de arquivo desconhecido.\n";
              exit 0;
      }
      # Canalisa a matriz para o smbprint
      open(PRINTER, "|$print_prog") || die "Não pude abrir $print_prog: $!\n";
      foreach $line (@newlines) {
              print PRINTER $line;
      }
      # Envia um linefeed extra no caso do arquivo ter uma última linha incompleta.
      print PRINTER "\n";
      close(PRINTER);
      print "Completed\n";
      exit 0;
      
      # --------------------------------------------------- #
      #        Tudo daqui para baixo são sub-rotinas        #
      # --------------------------------------------------- #
      
      sub ParseCmdLine {
              # Parses the command line, finding out what file type the file is
      
              # Gets $arg and $file to be the arguments (if the exists)
              # and the filename
              if ($#_ < 0) {
                      &usage;
              }
              # DBG
      #       foreach $element (@_) {
      #               print "*$element* \n";
      #       }
      
              $arg = shift(@_);
              if ($arg =~ /\-./) {
                      $cmd = $arg;
              # DBG
      #       print "\$cmd found.\n";
      
                      $file = shift(@_);
              } else {
                      $file = $arg;
              }
              
              # Defining the file type
              unless ($cmd) {
                      # We have no arguments
      
                      if ($file =~ /\.ps$/) {
                              $filetype = "ps";
                      } elsif ($file =~ /\.java$|\.c$|\.h$|\.pl$|\.sh$|\.csh$|\.m4$|\.inc$|\.html$|\.htm$/) {
                              $filetype = "code";
                      } else {
                              $filetype = "ASCII";
                      }
      
                      # Process $file for what type is it and return $filetype 
              } else {
                      # We have what type it is in $arg
                      if ($cmd =~ /^-p$/) {
                              $filetype = "ps";
                      } elsif ($cmd =~ /^-c$/) {
                              $filetype = "code";
                      } elsif ($cmd =~ /^-a$/) {
                              $filetype = "ASCII"
                      }
              }
      }
      
      sub usage {
              print "
      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.\n
      ";
              exit(0);
      }
      
      sub wrap {
              # Create an array of file lines, where each line is < the 
              # number of characters specified, and wrapped only on whitespace
      
              # Get the number of characters to limit the line to.
              $limit = pop(@_);
      
              # DBG
              #print "Entering subroutine wrap\n";
              #print "The line length limit is $limit\n";
      
              # Read in the file, parse and put into an array.
              open(FILE, "<$file") || die "Can't open $file: $!\n";
              while(<FILE>) {
                      $line = $_;
                      
                      # DBG
                      #print "The line is:\n$line\n";
      
                      # Wrap the line if it is over the limit.
                      while ( length($line) > $limit ) {
                              
                              # DBG
                              #print "Wrapping...";
      
                              # Get the first $limit +1 characters.
                              $part = substr($line,0,$limit +1);
      
                              # DBG
                              #print "The partial line is:\n$part\n";
      
                              # Check to see if the last character is a space.
                              $last_char = substr($part,-1, 1);
                              if ( " " eq $last_char ) {
                                  # If it is, print the rest.
      
                                  # DBG
                                  #print "The last character was a space\n";
      
                                  substr($line,0,$limit + 1) = "";
                                  substr($part,-1,1) = "";
                                  push(@newlines,"$part\n");
                              } else {
                                   # If it is not, find the last space in the 
                                   # sub-line and print up to there.
      
                                  # DBG
                                  #print "The last character was not a space\n";
      
                                   # Remove the character past $limit
                                   substr($part,-1,1) = "";
                                   # Reverse the line to make it easy to find
                                   # the last space.
                                   $revpart = reverse($part);
                                   $index = index($revpart," ");
                                   if ( $index > 0 ) {
                                     substr($line,0,$limit-$index) = "";
                                     push(@newlines,substr($part,0,$limit-$index) 
                                         . "\n");
                                   } else {
                                     # There was no space in the line, so
                                     # print it up to $limit.
                                     substr($line,0,$limit) = "";
                                     push(@newlines,substr($part,0,$limit) 
                                         . "\n");
                                   }
                              }
                      }
                      push(@newlines,$line);
              }
              close(FILE);
      }
      
      sub codeformat {
              # Call subroutine wrap then filter through nenscript
              &wrap($line_length);
              
              # Pipe the results through nenscript to create a Postscript
              # file that adheres to some decent format for printing
              # source code (landscape, Courier font, line numbers).
              # Print this to a temporary file first.
              $tmpfile = "/tmp/nenscript$$";
              open(FILE, "|$nenscript -2G -i$file -N -p$tmpfile -r") || 
                      die "Can't open nenscript: $!\n";
              foreach $line (@newlines) {
                      print FILE $line;
              }
              close(FILE);
              
              # Read the temporary file back into an array so it can be
              # passed to the Samba print script.
              @newlines = ("");
              open(FILE, "<$tmpfile") || die "Can't open $file: $!\n";
              while(<FILE>) {
                      push(@newlines,$_);
              }
              close(FILE);
              system("rm $tmpfile");
      }
      
      sub createarray {
              # Create the array for postscript
              open(FILE, "<$file") || die "Can't open $file: $!\n";
              while(<FILE>) {
                      push(@newlines,$_);
              }
              close(FILE);
      }
      

      Agora, via MagicFilter. Obrigado a Alberto Menegazzi ( flash.egon@iol.it) por esta informação. Alberto disse: --------------------------%<---------------------------------- 1) Instale o MagicFilter com o filtro de impressoras que você precisa em /usr/bin/local mas não preencha o /etc/printcap com a sugestão dada na documentação do MagicFilter. 2) Escreva o /etc/printcap desta forma (está feito para minha LaserJet 4L): 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: Você pode perceber que o lp=/dev/... está aberto para fazer o locking de tais dispositivos "virtuais", deve ser usado um para cada impressora remota. Exemplo criando com: touch /dev/ljet4l Escreva o filtro /usr/local/bin/main-filter, o mesmo que você sugeriu usando o filtro ljet4l-filter ao invés do cat. Aqui está o meu: #! /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 P.S. : aqui vai uma citação do Print2Win mini-Howto sobre locking e porque criar impressoras virtuais Starts here--> ---Começa aqui Dica de Rick Bressler : Boa dica. Eu uso algo muito parecido. Uma dica útil é que isso não é uma boa idéia: :lp=/dev/null:\ lpr abre em modo exclusivo o arquivo que você especifica em lp=. Ele o faz para evitar que vários processos tentem imprimir na mesma impressora ao mesmo tempo. O efeito colateral disto no seu caso, eng e colour não podem imprimir ao mesmo tempo, (normalmente mais ou menos transparente, já que provavelmente elas imprimem rapidamente e já que elas enfileram, você provavelmente não percebe) mas qualquer outro processo que tenta escrever em /dev/null será quebrado! Em um sistema monousuário, provavelmente não é um grande problema. Eu tenho um sistema com mais de 50 impressoras. Haveria um problema aqui. A solução é criar uma impressora burra para cada. Exemplo: touch /dev/eng. Eu modifiquei as entradas lp no arquivo printcap acima para levar em conta as sugestões do Rick. Eu fiz o seguinte: #touch /dev/eng #touch /dev/colour Ends here--> ---Termina aqui --------------------------%<----------------------------------
      Next Previous Contents