Fernsteuerung im Intranet

In großen, verteilten convert4print-Installationen kann es vorkommen, daß man einzelnen Mitarbeitern die Kontrolle über eine kleine Anzahl Drucker übertragen möchte, ohne diesen gleich den vollen Zugriff auf das Kontrollfeld des Konverter/Gateway-Services zu erlauben. Möglicherweise stehen dem auch Rechteprobleme mit dem Zugriff auf den Rechner mit dem Konverter/Gateway-Service entgegen.

Bei solchen Konstellationen kann die Integration des Programms c4pRemote in eine Intranet-Umgebung die Anforderung nach dezentraler Druckerverwaltung auf elegante Weise umsetzen. Durch die Steuerung der Drucker über eine Browser-Oberfläche entfällt jeglicher convert4print-Installationsaufwand auf den eingesetzten Rechnern, und die Installations- und Konfigurationsarbeiten beschränken sich auf den Web-Server im Intranet.

Die hier vorgestellte Lösung soll nur als Leitfaden für eigene Installationen dienen und stellt keineswegs die einzige und bestimmt auch nicht die für alle Fälle optimale Lösung dar. Beispielsweise muß die Definition der zu kontrollierenden Drucker bei mehrfachem Einsatz der Lösung innerhalb eines Unternehmens anders realisiert werden. Auch wird eine Integration in eventuell bereits vorhandene Management-Oberflächen andere Vorgehensweisen erfordern. Zu guter Letzt ist die graphische Präsentation ausbaufähig - bis hin zu Gebäudeplänen mit eingezeichneten Druckerstandorten.


Um einen ersten Eindruck von der hier diskutierten Intranet-Integration zu zeigen, nachfolgend eine Abbildung der von der Lösung erzeugten Startseite und dann eine Abbildung von der eigentlichen Statusanzeige.


Mit der Startseite wird ein gemeinsames Portal für alle Zugriffe auf convert4print realisiert. Hinter den drei oder gegebenenfalls auch mehr Schaltflächen befinden sich Verweise auf Textdateien, in denen die Liste der jeweils zu überwachenden Drucker abgelegt sind. Durch Anklicken der Schaltfläche wird dann auf die Statusanzeige weitergeleitet.


In der Statusanzeige werden in einer Tabelle die Namen und der Status zweier (oder mehrerer) Konverter (oder Gateways) angezeigt. Der eine Drucker Drucker im Marketing ist durch einen Klick in die am Anfang jeder Tabellenzeile positionierte CheckBox für eine weitere Aktion ausgewählt. Der Drucker Etikettendrucker im Versand ist im Augenblick nicht betriebsbereit, und erfordert ein manuelles Eingreifen, weil die Druckerabdeckung offen ist.

Über die vier Schaltflächen unterhalb der Tabelle werden Aktionen ausgeführt, wobei Aktualisieren die Anzeige auf den neuesten Stand bringt (unabhängig von den getroffenen Auswahlen) und die drei anderen Schaltflächen die jeweilige Aktion auf den ausgewählten Druckern (oder Gateways) ausführen. Dabei haben die Aktionen und auch die Flaggen die aus dem Kontrollfeld des Konverter/Gateway Services bekannten Bedeutungen.


Die im nachfolgenden vorgestellten technischen Informationen basieren auf einer Beispielinstallation mit einem Apache-Web-Server mit Perl-Unterstützung auf einem Windows-Rechner. Eine solche Umgebung ist mit der Standardinstallation eines XXAMP-Paketes leicht testweise herzustellen. An notwendigen Basis-Techniken werden lediglich HTML, CSS und zwei kleine Perl-Skripte eingesetzt. Muss die Lösung in ein anderes Umfeld portiert werden, müssen gegebenenfalls die ersten Zeile der Skripte und alle in den Skripten und HTML-Seiten vorkommenden Pfade angepasst werden.

Die Lösung geht auch der Einfachheit halber zunächst davon aus, daß der Apache-Server auf dem gleichen Rechner läuft, wie der convert4print Konverter/Gateway-Service, der unter dem Pfad C:\Programme\convert4print installiert ist. Läuft convert4print auf einem anderen Rechner, muß der Aufruf des Programms c4pRemote über einen Aufruf des Microsoft-Tools psExec erfolgen, was die Ausführung von Programmen auf anderen Rechnern ermöglicht.

Die erste Aufgabe für die Realisation einer Intranet-Integration ist die Erstellung einer HTML-Seite, die als Startseite für unsere Lösung dient. Auf dieser Seite wird ausgewählt, welche Drucker gesteuert und überwacht werden sollen. Am sinnvollsten ist wohl eine Auswahl nach Abteilungen oder innerhalb der Firma existierenden organisatorischen Einheiten. Die Seite heist convert4print.html und ihr HTML-Code ist nachfolgend abgebildet.


 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 <html>
   <head>
     <title>Willkommen</title>
     <style type="text/css">
 
       body  { font-size: 8pt; 
               font-family: Verdana, Arial, Helvetica, MS Sans Serif; }
       h1    { font-size: 24pt; 
               margin-left: 190px; margin-top: 80px; margin-bottom: 0px; } 
 
       .Button { background-color: #ccc; color: #000; width: 100px;
                 border: 1px solid #888; margin-right: 10px; }
 
     </style>
   </head>
   <body style="background-image:url(imgs/c4p.gif); 
                background-repeat: no-repeat;
                background-position: 0px 20px;
                background-color: white;">
 
     <h1>Willkommen</h1>
 
     <br/>
     <br/>
 
     <form action="/cgi-bin/remote.pl" method="get">
 
       <input type="hidden" name="Drucker" value="">
 
       <input type="submit" class="button" value="Versand" 
              onclick="this.form.Drucker.value = 'Versand.txt'">
       <input type="submit" class="button" value="Lager"
              onclick="this.form.Drucker.value = 'Lager.txt'">
       <input type="submit" class="button" value="Büro"
              onclick="this.form.Drucker.value = 'Buero.txt'">
     </form>
 
   </body>
 </html>


Diese recht überschaubare HTML-Datei stellt drei Schaltflächen mit der Beschriftung Versand, Lager und Büro zur Verfügung, die auf die Statusanzeige für die jeweilige Abteilung verweisen. Damit die Statusanzeige weiss, welche Drucker zu welcher Abteilung gehören, wird die Liste der Drucker als Textdatei übergeben. In einer solchen Datei stehen einfach die Namen der zu überwachenden Drucker oder Gateways. Jeweils ein Name je Zeile.

Die zweite HTML-Seite, die benötigt wird, ist die, die zur Statusanzeige verwendet werden soll. Der erste Teil der Datei (CSS) ist identisch mit der der Startseite, um ein einheitliches Bild zu gewährleisten. Diese Seite wird unter dem Namen Template.html abgelegt, weil sie nicht direkt angezeigt werden kann, sondern erst noch durch ein Skript bearbeitet wird. Der HTML-Code ist nachfolgend abgebildet.


 <!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
 <html>
   <head>
     <title>Druckerstatus</title>
     <style type="text/css">
 
       body  { font-size: 8pt; 
               font-family: Verdana, Arial, Helvetica, MS Sans Serif; }
       table { font-size: 10pt; }
       h1    { font-size: 24pt; 
               margin-left: 190px; margin-top: 80px; margin-bottom: 0px; } 
 
       .Button { background-color: #aaa; color: #000; width: 100px;
                 border: 1px solid #888; margin-right: 10px; }
 
     </style>
   </head>
   <body style="background-image:url(imgs/c4p.gif); 
                background-repeat: no-repeat;
                background-position: 0px 20px;
                background-color: white;">
 
     <h1>Druckerstatus</h1>
 
     <div style="margin-left: 192px;">
       Zuletzt aktualisiert: 
 <!--@@Datum-->
     </div>
 
     <br/>
 
     <form action="/cgi-bin/update.pl" method="get">
 
       <input type="hidden" name="Aktion" value="">
 <!--@@Liste-->
 
       <table cellpadding="3" cellspacing="0" border="1"
              style="border-collapse:collapse; border-style:solid;">
         <tr bgcolor="#EEEEEE">
           <td width="20"></td>
           <td width="20" height="20"></td>
           <td width="200"><b>Drucker</b></td>
           <td width="200"><b>Statusinformation</b></td>
         </tr>
 
 <!--@@Drucker-->
       </table>
 
       <br/>
       <br/>
 
       <input type="submit" class="button" value="Aktualisieren" 
                            onclick="this.form.Aktion.value = 'u'"
                            style="margin-right: 35px;">
       <input type="submit" class="button" value="Starten"
                            onclick="this.form.Aktion.value = 's'">
       <input type="submit" class="button" value="Anhalten"
                            onclick="this.form.Aktion.value = 't'">
       <input type="submit" class="button" value="Abbrechen"
                            onclick="this.form.Aktion.value = 'a'">
     </form>
 
   </body>
 </html>


Das einzig Besondere an diesem HTML-Code sind die drei als Kommentare eingefügten Platzhalter, die bei der Bearbeitung durch ein Perl-Skript durch weiteren HTML-Code ersetzt werden. Soweit der HTML-Code.


Ein weiterer wichtiger Bestandteil der Lösung sind zwei kleine Perl-Skripte. Das erste Skript hat den Namen remote.pl und wird von der Startseite aus aufgerufen. Beim Aufruf wird der Name der Textdatei mit den Drucker- und Gateway-Namen als Parameter Drucker übergeben.

Am Anfang des Skriptes werden zwei Umsetzungstabellen (Hashes %Flaggen und %Druckerstatus) definiert. Diese dienen zum einen zur Auswahl einer den Status des Druckers anzeigenden farbigen Flagge und zum anderen zur Anzeige des Druckerstatus im Klartext.


 #!"C:\perl\bin\perl.exe"
 
 my %Flaggen = (
                 100 => "Grey.gif",
                 101 => "Yellow.gif",
                 102 => "Green.gif",
                 103 => "Cyan.gif",
                 104 => "Magenta.gif",
                 105 => "White.gif",
                 106 => "Red.gif",
                 110 => "Blue.gif",
                 111 => "Blue.gif",
                 112 => "Blue.gif",
                 113 => "Blue.gif",
                 114 => "Blue.gif",
                 115 => "Blue.gif",
                 116 => "Blue.gif",
                 117 => "Blue.gif"
               );     
 
 my %Druckerstatus = (
                       100 => "Angehalten",
                       101 => "Bereit",
                       102 => "Druckt",
                       103 => "Lizenzanfrage",
                       104 => "Druckeranfrage",
                       105 => "",
                       106 => "Fehler",
                       110 => "Eingriff erforderlich",                            
                       111 => "Papierstau",
                       112 => "Papierende",
                       113 => "Wenig Toner",
                       114 => "Abdeckung offen",
                       115 => "Offline",
                       116 => "Netzwerkverbindung verloren",
                       117 => "Ausgabefach voll"
                     );


Der erste Teil des Skripts dient der Aufbereitung der durch den HTTP-Server übergebenen Formularparameter. Die Komplexität dieser Anweisungen ist der aufwändigen Umkodierung von Steuerzeichen und in HTML selbst benutzten Zeichen innerhalb der Formularparameter geschuldet. Die Formularparameter werden als Paare aus Namen und Werten abwechselnd im Feld $Parameter untergebracht.

Anschliessend liest das Skript die HTML-Datei Template.html und sucht über einen regulären Ausdruck nach den Kommentarzeilen. Enthält der Kommentar den Text Datum, wird das aktuelle Datum und Uhrzeit ermittelt und statt des Kommentars in die Ausgabe eingesetzt. Enthält der Kommentar den Text Liste, wird ein Formularparameter statt des Kommentars erzeugt, der einfach den Namen der Textdatei weitergibt.

Das Entscheidende passiert, wenn der Kommentar den Text Drucker enthält. In diesem Fall wird die als Parameter übergebene Textdatei geöffnet, die Druckernamen werden gelesen und an eine Prozedur mit dem Namen DruckerStatus übergeben. Diese Prozedur ermittelt für jeden Drucker dessen aktuellen Status.

Wichtigster Bestandteil dieser Prozedur ist der Aufruf des mit der Installation von convert4print mitgelieferten Hilfsprogramms c4pRemote, Dieses Hilfsprogramm gestattet eine gegenüber dem Kontrollfeld etwas eingeschränkte Steuerung des Konverter/Gateway-Servers via Kommandozeile - eine Beschreibung dieses Programms findet sich übrigens im vorangegangenen Kapitel.

Das Programm c4pRemote wird mit dem Parameter '-q' für eine Statusanfrage und dem als Prozedurparameter übergebenen Druckernamen aus der Textdatei aufgerufen. Aus dem Rückgabewert des Programms wird dann der HTML-Code für die Statusanzeige erzeugt.


 #------------------------------------------------------------------------------#
 
 if ($ENV{'REQUEST_METHOD'} eq 'GET')
   {
     my @ParameterListe = split(/&/, $ENV{'QUERY_STRING'});
     my $i = 0;
 
     foreach $Param (@ParameterListe)  
       {
         ($Parameter [$i], $Parameter [$i + 1]) = split(/=/, $Param);
         $i++;   
         $Parameter [$i] =~ tr/+/ /;
         $Parameter [$i] =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; 
         $Parameter [$i] =~ s/<!--(.|\n)*-->//g;      
         $i++;
       }
 
    open (TMPL, "<Template.html");
    print "Content-type: text/html\n\n";
 
    while (<TMPL>)
      {
        if (/^<!--\@@(.+)-->.*/)
          {
            if ($1 eq "Datum")
              {
                ($sec, $min, $hour, $mday, $mon, $year, $wday, $yday)
                                                         = localtime (time);
                 $year += 1900;
                 $mon +=printf "%02d.%02d.%04d %02d:%02d:%02d\n",
                        $mday, $mon, $year, $hour, $min, $sec;
              }
            elsif ($1 eq "Drucker")
                 {
                   open (PRT, "<$Parameter[1]");
 
                   while ()
                     {
                       chomp ($_);
                       DruckerStatus ($_);
                     }
 
                   close (PRT);
                 }
               elsif ($1 eq "Liste")
                    {
                      print "<input type=\"hidden\" name=\"Drucker\" "
                          . "value=\"$Parameter[1]\">\n";
                    }
           }
         else { print $_; }
       }
 
     close (TMPL);
   }
 
 #------------------------------------------------------------------------------# 
 
 sub DruckerStatus 
   {
     $Status = system ("C:\\Programme\\convert4print\\c4pRemote",
                        "-q",
                        "-n\"$_[0]\"") / 256;
     print "<tr>\n";
     print "  <td><input type=\"checkbox\" name=\"$_[0]\" /></td>\n"; 
     print "  <td align=\"center\">\n";
     print "    <img src=\"imgs/$Flaggen{$Status}\""
                 . " align=\"center\" alt=\"\" />\n"; 
     print "  </td>\n";
     print "  <td>$_[0]</td>\n";
     print "  <td>$Druckerstatus{$Status}</td>\n";
     print "</tr>\n";
   }
 
 #------------------------------------------------------------------------------#


Der bereits letzte Bestandteil der Lösung ist ein weiteres Skript update.pl, welches aufgerufen wird, wenn eine der vier Schaltflächen unterhalb der Tabelle angeklickt wird. Welche der Schaltflächen angeklickt wurde, entnimmt das Skript dem verdeckt übermittelten Formularparameter Aktion, der von jeder Schaltfläche via JavaScript entsprechend gesetzt wird.

Die erste Hälfte des Skripts dient wieder der Aufbereitung der durch den HTTP-Server übergebenen Formularparameter und ist ähnlich mit den Anweisungen aus dem ersten Skript. Allerdings bezieht sich hier die Aufbereitung auf die als Parametername übermittelten Druckernamen.

Der einfachste Fall für das Skript ist die Aktualisierung der Anzeige, weil dies lediglich den erneuten Aufruf des Skriptes remote.pl bedeutet. Dabei muss der als zweiter Parameter an das Skript übergebene Name der Textdatei erneut übergeben werden.

In allen anderen Fällen (Starten, Anhalten, Abbrechen) wird wieder das Programm c4pRemote aufgerufen, diesmal allerdings zusätzlich parametrisiert mit der durch die Schaltfläche vorgegebenen Aktion, also '-s' für Starten, '-t' für Anhalten und '-a' für Abbrechen. Der zweite Parameter ist wieder der Druckername.


 #!"C:\perl\bin\perl.exe"
 
 if($ENV{'REQUEST_METHOD'} eq 'GET')
   {
     my @ParameterListe = split(/&/, $ENV{'QUERY_STRING'});
     my $i = 0;
 
     foreach $Param (@ParameterListe)  
       {
         ($Parameter [$i], $Parameter [$i + 1]) = split(/=/, $Param);
         $Parameter [$i] =~ tr/+/ /;
         $Parameter [$i] =~ s/%([a-fA-F0-9][a-fA-F0-9])/pack("C", hex($1))/eg; 
         $Parameter [$i] =~ s/<!--(.|\n)*-->//g;
         $i++; $i++;
       }
 
     if ($Parameter [1] ne 'u')
       {
         while ($i > 2)
           {
             $i--; $i--;
 
             system ("C:\\Programme\\convert4print\\c4pRemote",
                     "-$Parameter[1]",
                     "-n\"$Parameter[$i]\"");
           }
       }
 
    print "Location: http://$ENV{'HTTP_HOST'}/cgi-bin/remote.pl?"
        . "$Parameter[2]=$Parameter[3]\n\n";
   }
 
 exit 0;


Die Liste der ausgewählten Drucker (oder Gateways) wird über eine Schleife beginnend mit dem als letzten ausgewählten Drucker abgearbeitet. Sollte übrigens das Installationsverzeichnis von convert4print nicht C:\Programme\convert4print sein, ist der Programmaufruf in beiden Skripten entsprechend abzuändern.


Bisher nicht weiter erwähnt wurde, daß die Lösung noch eine Reihe von Graphikdateien benötigt. Zum einen die acht möglichen Flaggen zur Statusanzeige und natürlich das convert4print-Logo. Alle diese Dateien befinden sich in einem Ordner mit dem Namen imgs.

Die beiden Skripte remote.pl und update.pl gehören ins CGI-Verzeichnis des HTTP-Servers, genauso wie die Datei Template.html und alle Textdateien mit den Druckernamen - im Falle von Apache also nach C:\xxamp\cgi-bin. Die Seite convert4print.html wurde im Beispiel auf die Hauptebene des Servers kopiert, kann aber natürlich abhängig von den lokalen Gegebenheiten auch in jedem Unterverzeichnis liegen. Im Falle von Apache wird die Datei convert4print.html ins Verzeichnis C:\xxamp\htdocs kopiert. Dorthin wird auch der Ordner imgs mit den Bildern kopiert.

Wenn die Datei convert4print.html für den HTTP-Server nicht prinzipiell als Startseite angegeben wird, erfolgt der Aufruf im Browser mit http://c4p.spe-systemhaus.de/convert4print.html, wobei der Servername natürlich an die lokalen Gegebenheiten angepasst werden muss.


Bereits mit so überschaubarem Aufwand ist also eine Lösung für die Druckerüberwachung und Steuerung im Intranet zu realisieren. Dadurch, dass nur der HTTP-Server Zugriff auf die Rechner mit der convert4print-Installation hat, ist keine aufwändige Konfiguration von Zugriffsrechten erforderlich. Für die HTTP-Zugriffe kann auf das zumeist bereits im Intranet vorhandene Zugriffskontrollsystem zurückgegriffen werden, und auf dem Rechner des jeweiligen Mitarbeiters ist nur ein Internet-Browser notwendig.


Zum Abschluß sei noch kurz erwähnt, was zu ändern ist, wenn convert4print nicht auf dem gleichen Rechner installiert ist, wie der Web-Server. In diesem Fall muß der Web-Server die Ausführung des Programms c4pRemote auf dem Rechner mit der convert4print-Installation veranlassen. Dazu kann von www.sysinternals.com das Paket PsTools geladen werden, das ein Programm mit dem Namen PsExec.exe enthält.

PsExec.exe führt ein als Parameter übergebenes Kommando auf einem anderen Rechner aus. Eine Beschreibung des Programms findet sich in der der Tool-Sammlung beiliegenden Dokumentation. Im Skript remote.pl muß demnach der Programmaufruf wie folgt geändert werden:


 $Status = system ("C:\\Programme\\Sysinternals\\psexec",
                    "\\\\10.1.17.7",
                    "-u",
                    "Benutzer",
                    "-p",
                    "Passwort",
                    "D:\\Programme\\convert4print\\c4pRemote.exe", 
                    "-q",
                    "-n\"$_[0]\"") / 256;


Dem entsprechend sieht der Programmaufruf im Skript update.pl dann so aus:


 system ("C:\\Programme\\Sysinternals\\psexec",
         "\\\\10.1.17.7",
         "-u",
         "Benutzer",
         "-p",
         "Passwort",
         "D:\\Programme\\convert4print\\c4pRemote",                
         "-$Parameter[1]",
         "-n\"$Parameter[$i]\"");


Dabei wird angenommen, daß das Tool PsExec im Verzeichnis C:\Programme\Sysinternals liegt, daß der andere Rechner die IP-Adresse '10.1.17.7' hat, und daß dort die convert4print-Installation auf D:\Programme\convert4print erfolgte. Ein weiterer wesentlicher Punkt ist, daß auch eventuell die Anmeldung als Benutzer auf dem anderen Rechner notwendig ist.

Zu dieser Lösung ist allerdings zu sagen, daß sie sehr teuer ist, im Sinne von Prozessor- und Netzwerklast (ca. 300 KByte / Zugriff). Auch aus Sicht der Systemsicherheit ist das Erlauben von Remote-Zugriffen durch andere Rechner nicht immer gern gesehen. Dieses Vorgehen sollte daher wirklich nur in Ausnahmefällen zum Einsatz kommen.


Hinweise