Sehr Umfangreiche Webseite zum Programmieren in C Perl CGI, Skripting, Linux, SystemprogrammierungC C++ C/C++ ANSI C Linux Linuxsystemprogrammierung Unix Pipe FIFO System-V IPC Linuxsystemprogrammieren Interprozesskommunikation IPC Pipe FIFO System-V IPC 5. Interprozesskommunikationen (IPC)

5.2.5. popen() - Pipes etwas komfortabler           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Als nächstes zur Funktion popen() die uns, im Gegensatz zu den Kapiteln zuvor, eine Menge Schreibarbeit abnimmt. Hier der Syntax zu popen ...

FILE *popen(const char* command, const char* wie);

Der Parameter command entspricht hier einem Funktionsaufruf von system. Intern wird dabei aber trotzdem eine Pipe angelegt. Ob Sie aus diesem Prozess Lesen oder Schreiben wollen, legen Sie mit dem Parameter wie fest. "r" steht für lesen aus der Pipe und "w" für das Schreiben. Im Fehlerfall liefert popen() NULL ansonsten den FILE-Zeiger zurück. Somit nimmt einem die Funktion popen() folgende Arbeiten gegenüber der Funktion pipe ab ...

Bevor Sie einige Beispielen mit der Funktion popen() sehen werden, soll hierzu noch eine alternative Implemtiereung erstellt werden, da nicht auf jedem System die Funktion popen vorhanden ist. Hier die Funktion popen selbstgebastelt ...

#include <stdio.h>
#define READ  0
#define WRITE 1
#define wr(a,b) (mode==READ?(b):(a))

FILE *p_open(char *commando, int mode)
{

 int fd[2];

 if(pipe(fd) < 0) return NULL;
 switch(fork())
  {

   case  -1 : return(NULL);
   case   0 : close(wr(fd[WRITE],fd[READ]));
              close(0,1);
              dup(wr(fd[READ],fd[WRITE]));
              close(wr(fd[READ],fd[WRITE]));
              execl("/bin/sh", "sh", "-c", commando, NULL);

   default  : close(wr(fd[READ],fd[WRITE]));
              return(FILE *)(wr(fd[WRITE],fd[READ]));
  }
}

Nun möchte ich Ihnen die Funktion popen anhand eines Beispiels näher zeigen ...

#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>

#define EXIT(s) {fprintf(stderr, "%s",s); exit(0);}
#define USAGE(s) {fprintf(stderr, "%s Datei zum lesen\n",s); exit(0);}
#define MAX 8192

enum {ERROR=-1,SUCCESS};

int main(int argc, char **argv)
{
  FILE *pipe_writer, *file;
  char puffer[MAX];

  if(argc!=2)
    USAGE(argv[0]);

  if((file=fopen(argv[1], "r")) == NULL)
    EXIT("Fehler bei fopen.........\n");

  if(( pipe_writer=popen("./filter" ,"w")) == NULL)
    EXIT("Fehler bei popen...........\n");

  while(1)
    {
      if(fgets(puffer, MAX, file) == NULL)
         break;
      if(fputs(puffer, pipe_writer) == EOF)
         EXIT("Fehler bei fputs........\n");
    }
  pclose(pipe_writer);
}

Das Programm bewirkt das selbe, wie mit der Funktion pipe() im Kapitel zuvor. Sie können diese beiden Programme ruhig mal vergleichen. Wollen wir diese Funktion doch mal etwas genauer analysieren ...

Als erstes Argument der Funktion popen() geben Sie das Programm ein, dass unseren Kindprozess überlagern soll. Und das zweite Argument stellt den Modus da. Dafür haben Sie wie schon bei den Pipes die folgenden 2 Möglichkeiten ...

w - Die geschriebenen Daten in die Pipe werden an die Standardeingabe von kommandozeile weitergegeben.

popen - in Pipe schreiben

r - Die gelesenen Daten aus der Pipe stammen direkt aus der Standardausgabe von kommandozeile.

popen - aus Pipe lesen

Um die pipe wieder zu schließen sollte die Funktion ...

int pclose(FILE *pipe_zeiger);

...verwendet werden. Diese Funktion wartet auf die Beendigung des Prozesses, den Sie in kommandozeile angegeben haben.

5.2.6. Mail versenden mit Pipes und sendmail           zurück Ein Kapitel tiefer Ein Kapitel höher zum Inhaltsverzeichnis

Eine Mail aus einer Anwendung zu versenden ist nun mit den Pipes kein Problem mehr. Ich verwende für das folgende Beispiel das Programm sendmail für das Versenden von Mails, da es bei den meisten System als Dämon im Hintergrund läuft. sendmail ist eigentlich für ein solches Beispiel unterfordert. Natürlich setzt dies voraus, dass Sie sendmail richtig konfiguriert haben.

#include <unistd.h>
#include <sys/wait.h>
#include <stdio.h>
#include <sys/types.h>
#include <fcntl.h>

int sendmail(char *to, char *from, char *subject,
             /*char *cc, char *bcc,*/ char *inhalt)
{
 FILE *pipe,*pfad;
 char pfad_sendmail[255];
 /*Wir ermitteln den Pfad zu sendmail*/
 /*ACHTUNG!!! Sollte nur als root funktionieren*/
 pfad=popen("which sendmail", "r");
 if(pfad == NULL)
  {
    fprintf(stderr,"Konnte keine Pipe zu \'which\' erstellen!\n");
    exit(0);
  }
 fscanf(pfad,"%s",pfad_sendmail);
 pclose(pfad);
 strcat(pfad_sendmail, " -n -oi -t");
 /*Den Pfad ist meistens /usr/sbin/sendmail */
 pipe=popen(pfad_sendmail, "w");
 if(pipe == NULL)
  {
    fprintf(stderr, "Konnte keine Verbindung zu sendmail aufbauen!n");
    exit(0);
  }
 /*In sendmail schreiben..........*/
 fprintf(pipe, "To       : %s"
               "From     : %s"
               "Subject  : %s"
               /*"Cc      : %s\n" */
               /*"Bcc     : %s\n"*/
               "%s",to,from,subject,inhalt);
 fflush(pipe);
 pclose(pipe);
 printf("Mail wurde erfolgreich an versendet an : %s\n",to);
 return 0;
}

int main(int argc, char **argv)
{
 char to[255],fr[255],sub[255],inhalt[8192],puffer[255];
 printf("Mail To   : ");
 fgets(to,255,stdin);
 printf("Mail from : ");
 fgets(fr,255,stdin);
 printf("Betreff   : ");
 fgets(sub,255,stdin);
 inhalt[0]='\0';
 printf("Inhalt    (beenden mit STRG+D) \n>");
 while(fgets(puffer,8192,stdin) != NULL)   {

    strcat(inhalt,puffer);
    printf(">");
    }
 sendmail(to,fr,sub,inhalt);
 return 0;
}

Wir verwenden bei diesem Programm gleich 2 Pipes. Eine zum Ermitteln des Pfades von sendmail und die zweite zum Versenden der Mail mit sendmail. Wenn Sie eine Flaterate oder Standleitung haben, können Sie ja noch eine Funktion mit dem Programm fetchmail schreiben. fetchmail dient zum Abholen von Mails. Filtern Sie dann mit Hilfe der Pipe die Zeile 'from' aus, und schicken sie eine Mail an den Absender zurück mit der Information, dass Sie die Mail erhalten haben (Autoresponder). Verwenden Sie den cron-Dämon und lassen Sie stündlich Ihre Mails abholen.

5.2.7. Drucken über ein Pipe mit lpr           zurück Ein Kapitel tiefer zum Inhaltsverzeichnis

Unter Linux werden ja Geräte genauso wie Dateien angesprochen. Somit könnten Sie, wenn der Drucker sich Beispielsweise am Parallelen Port befindet, eine Datei mit dem Aufruf ...

cp datei.txt /dev/lp0

...oder...

cp datei.txt /dev/lp1

...ausdrucken lassen. Dies erweist sich aber nicht als recht komfortabel und sicher. Beim Systemstart wird daher der Druckerdämon lpd aufgerufen, der sich um die Umsetzung der Druckeraufträge kümmert. Mit dem Druckertool lpr können Sie dann Ihre Texte komfortabler aufrufen. Bei Aufruf von lpr ruft der Druckerdämon lpd einen neuen Prozess mit fork auf. Aus diesem Prozess wird anschließend ein Kinderdämon (lpr), welcher dann die Arbeit zum Drucken übernimmt. Somit können Sie mit dem Aufruf ...

lpr datei.txt

...den Text in datei.txt ausdrucken. Die unzähligen Optionen die Sie mit lpr verwenden können, entnehmen Sie bitte aus der man-Page. Weitere interessante Druckertools währen ...

Dies soll nur ein kurzer Überblick zum Drucken unter Linux darstellen. Nach den Kapiteln zuvor, denke ich mal, dass Ihnen das Drucken über eine Pipe als Programm nicht mehr sehr schwer fallen dürfte ...

#include <stdio.h>
#include <unistd.h>
#define BUF 8192

void an_den_drucker(char *text)
{
 FILE *p;
/*Pipe zum Tool lpr erstellen zum Schreiben auf lpr*/
 p=popen("lpr","w");
 if(p == NULL)
  {
    fprintf(stderr,"Konnte keine Pipe zu \'lpr\' erstellen!!\n");
    exit(0);
  }
 /*An den Drucker schreiben*/
 printf("Sende Auftrag an den Drucker......\n");
 fprintf(p, "%s", text);
 fflush(p);
 pclose(p);
}

int main()
{
  char puffer[BUF], inhalt[BUF];
  inhalt[0]='\0';

  printf("Bitte schreiben sie Ihren Text den sie Drucken wollen"
         " (Beenden : STRG+D)!\n-> ");
  while(fgets(puffer,BUF,stdin) != NULL){

    strcat(inhalt,puffer);
    printf("-> ");
   }
 an_den_drucker(inhalt);

 return 0;
}

Natürlich können Sie nun auch andere Texte mittels ...

echo Hallo Welt | ./printme

...senden. Was uns in dem Programmbeispiel fehlt, ist ein Filterprogramm, um die Druckerausgabe zu formatieren oder Beispielsweise nach Alphabet sortiert ausgibt. Etwa bei einer Dateiverwaltung.

#include <stdio.h>
#include <unistd.h>
#define BUF 8192

void an_den_drucker(char *text)
{
 FILE *p;
/*Pipe zum Tool lpr erstellen zum Schreiben auf lpr*/
 p=popen("lpr","w");
 if(p == NULL)
  {
    fprintf(stderr,"Konnte keine Pipe zu \'lpr\' erstellen!!\n");
    exit(0);
  }
 /*An den Drucker schreiben*/
 printf("Sende Auftrag an den Drucker......\n");
 fprintf(p, "%s", text);
 fflush(p);
 pclose(p);
}

void sortiere_ausgabe(char *text)
{
 FILE *p;

 p=popen("sort","w");
 if(p == NULL)
  {
    fprintf(stderr,"Konnte keine Pipe zu \'sort\' erstellen!!\n");
    exit(0);
  }
 printf("Drucke den Text sortiert aus.......\n");
 fprintf(p, "%s", text);
 an_den_drucker(text);
 fflush(p);
 pclose(p);
}


int main()
{
  char puffer[BUF], inhalt[BUF];
  inhalt[0]='\0';

  printf("Bitte schreiben sie Ihren Text den sie Drucken wollen "
         "(Beenden : STRG+D)!\n-> ");
  while(fgets(puffer,BUF,stdin) != NULL){

    strcat(inhalt,puffer);
    printf("-> ");
   }
 an_den_drucker(inhalt);
 sortiere_ausgabe(inhalt);

 return 0;
}

Sie könnten das ganze Programm jetzt mit Filtern und Schaltern weiterstricken. Zum Schluss wollen wir noch unser Programm zum Versenden von Emails mittels sendmail erweitern. Jede Mail die versendet wird, soll auf einen Drucker mitprotokolliert werden ...

#include <unistd.h>
#include >stdio.h>

void an_den_drucker(char *);
int sendmail(char *, char *, char *, char *);

int sendmail(char *to, char *from, char *subject,
             /*char *cc, char *bcc,*/ char *inhalt)
{
 FILE *pipe,*pfad;
 char pfad_sendmail[255];
 char protokol[8192];
 /*Wir ermitteln den Pfad zu sendmail*/
 /*ACHTUNG!!! Sollte nur als root funktionieren*/
 pfad=popen("which sendmail", "r");
 if(pfad == NULL)
  {
    fprintf(stderr,"Konnte keine Pipe zu \'which\' erstellen!\n");
    exit(0);
  }
 fscanf(pfad,"%s",pfad_sendmail);
 pclose(pfad);
 strcat(pfad_sendmail, " -n -oi -t");
 /*Den Pfad ist meistens /usr/sbin/sendmail */
 pipe=popen(pfad_sendmail, "w");
 if(pipe == NULL)
  {
    fprintf(stderr, "Konnte keine Verbindung zu sendmail aufbauen!n");
    exit(0);
  }
 /*In sendmail schreiben..........*/
 fprintf(pipe, "To       : %s"
               "From     : %s"
               "Subject  : %s"
               /*"Cc      : %s\n" */
               /*"Bcc     : %s\n"*/
               "%s",to,from,subject,inhalt);
 fflush(pipe);
 pclose(pipe);
 printf("Mail wurde erfolgreich an versendet an : %s\n",to);
 sprintf(protokol, "To       : %s"
                   "From     : %s"
                   "Subject  : %s"
                 /*"Cc      : %s\n" */
                 /*"Bcc     : %s\n"*/
                   "%s",to,from,subject,inhalt);
 an_den_drucker(protokol);
 return 0;
}

void an_den_drucker(char *text)
{
 FILE *p;
/*Pipe zum Tool lpr erstellen zum Schreiben auf lpr*/
 p=popen("lpr","w");
 if(p == NULL)
  {
    fprintf(stderr,"Konnte keine Pipe zu \'lpr\' erstellen!!\n");
    exit(0);
  }
 /*An den Drucker schreiben*/
 printf("Sende Auftrag an den Drucker......\n");
 fprintf(p, "%s", text);
 fflush(p);
 pclose(p);
}

int main(int argc, char **argv)
{
 char to[255],fr[255],sub[255],inhalt[8192],puffer[255];
 printf("Mail To   : ");
 fgets(to,255,stdin);
 printf("Mail from : ");
 fgets(fr,255,stdin);
 printf("Betreff   : ");
 fgets(sub,255,stdin);
 inhalt[0]='\0';
 printf("Inhalt    (beenden mit STRG+D) \n>");
 while(fgets(puffer,8192,stdin) != NULL)   {

    strcat(inhalt,puffer);
    printf(">");
    }
 sendmail(to,fr,sub,inhalt);
 return 0;
}

Sie sehen schon mit den Pipes könnte man praktisch jedes beliebige Programm einhängen. Voraussetzung ist natürlich, dass bei dem Endanwender diese Programm ebenso installiert ist. Im nächsten Kapiteln werden Sie sehen, wie wir eine Art Faxemfangsprogramm erstellen mit FIFO's.

Weiter mit 5.3.1 FIFO - named Pipes