Ich habe die Lösung inzwischen auch zu 90% (würd ich sagen) verstanden, ich kann mal versuchen, das in Worten wiederzugeben. Danach hänge ich mal ein Beispiel an, wie ich das gelöst habe.
Wenn ich das richtig verstanden habe, und mein Testcode hat zumindest funktioniert, dann funktioniert ein CGI- Call wie folgt:
Zuerst mal erzeuge ich 2 Pipes. Jede Pipe hat logischerweise 2 Handles, nämlich eins am Lese- und eins am Schreibende.
Die eine Pipe ist quasi die Richtung "Parent -> Child", die andere ist die Richtung "Child -> Parent".
Dann wird geforkt.
Im Child wird zunächst STDIN geschlossen (
). Grund dafür ist der, dass der (spätere) CGI- Prozess ja keine Pipe öffnen soll, sondern diese Pipes auf STDIN und STDOUT gemappt werden sollen, damit der Child direkt darüber mit dem Parent kommunizieren können.
Nachdem STDIN geschlossen wurde, kann jetzt das Lese- Handle unserer "Parent->Child" Pipe dupliziert werden:
.
p0 ist hierbei unsere Parent -> Child Pipe, p1 die umgekehrte Richtung.
Damit wird das Handle auf den gerade geschlossenen Kanal (nämlich STDIN) dupliziert.
Als nächstes machen wir dasselbe mit STDOUT:
.
In diesem Fall wurde das Schreibende der anderen Richtung (p1) auf den wiederum zuvor geschlossenen Kanal (STDOUT) gemappt.
Jetzt kann der Childprozess seine nicht benötigten Handles der Pipes schließen, nämlich die Enden, die der Parent braucht.
------------------
Im Parent machen wir dasselbe, nur dass wir in dem Fall nicht noch STDIN und STDOUT umbiegen müssen. Wir öffnen stattdessen einfach die Handles und schließen die überflüssigen, das wars auch schon.
------------------
Die letzte Anweisung im Child- Zweig lautet dann execlp(Programmname, Argumente, NULL). Damit wird das Programm beendet und an dessen Stelle tritt dann das aufgerufene Kommando. Der somit neu erzeugte Prozess kennt weder p0 noch p1, aber STDIN und STDOUT werden automatisch vererbt. Beim Beenden des Prozesses werden diese Handles standardmäßig auch wieder von der Shell geschlossen, so dass da keine offenen Handles zurückbleiben.
-------------------
Beispiel:
Meine CGIComm.h und .cpp:
CGIComm.h
Code:
#ifndef CGICOMM_H
#define CGICOMM_H
#include <stdlib.h>
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
typedef struct CGI
{
FILE *in;
FILE *out;
int pid;
};
CGI *OpenCGI(char *Command, char *Args); //CGI- Prozess erzeugen
char* ReadCGI(CGI* cgi); //Vom CGI- Prozess lesen
void WriteCGI(CGI* cgi, char* Data); //An CGI- Prozess senden
void CloseCGI(CGI *cgi); //CGI- Handles parentseitig schließen
int CGICommDirect(char* Command, char* Data, char* Answer, int AnsLen); //Kurzbefehl für alles zusammen
#endif
--------------------------------
CGIComm.cpp
Code:
#include "CGIComm.h"
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <errno.h>
CGI *OpenCGI(char *Command, char *Args)
{
FILE *f = fopen(Command, "r");
if (f == NULL)
return NULL;
fclose(f);
CGI *cgi = (CGI*)malloc(sizeof(CGI));
int p0[2], p1[2];
pipe(p0);
pipe(p1);
int cid = fork();
if (!cid)
{
close(0);
dup(p0[0]);
close(1);
dup(p1[1]);
close(p0[1]);
close(p1[0]);
execlp(Command, Args, NULL);
}
else
{
cgi->in = fdopen(p0[1], "w");
close(p0[0]);
cgi->out = fdopen(p1[0], "r");
close(p1[1]);
cgi->pid = cid;
}
return cgi;
}
char* ReadCGI(CGI* cgi)
{
char* Data = NULL;
if (cgi == NULL)
return NULL;
int Len = 0;
int DataLen = 0;
char Buffer[128];
while ((Len = fread(Buffer, 1, 128, cgi->out)))
{
char* NData = (char*)malloc(Len + DataLen);
memcpy(NData, Data, DataLen);
memcpy(&NData[DataLen], Buffer, Len);
if (Data != NULL)
free(Data);
Data = NData;
DataLen += Len;
}
if (DataLen == 0)
return NULL;
char *NData = (char*)malloc(DataLen+1);
memcpy(NData, Data, DataLen);
NData[DataLen] = 0;
if (Data != NULL)
free(Data);
Data = NData;
return Data;
}
void WriteCGI(CGI* cgi, char* Data)
{
if (cgi == NULL)
return;
int Len = strlen(Data)+1;
fputs(Data, cgi->in);
fclose(cgi->in);
cgi->in = NULL;
}
void CloseCGI(CGI *cgi)
{
if (cgi == NULL)
return;
int result;
waitpid(cgi->pid, &result, 0);
if (cgi->in != NULL)
fclose(cgi->in);
fclose(cgi->out);
free(cgi);
}
int CGICommDirect(char* Command, char* Data, char* Answer, int AnsLen)
{
int pfds0[2], pfds1[2];
FILE *fp_in, *fp_out;
pipe(pfds0);
pipe(pfds1);
int cid = fork();
if (!cid) {
close(0);
dup(pfds1[0]);
close(1);
dup(pfds0[1]);
close(pfds0[0]);
close(pfds1[1]);
execlp(Command, NULL, NULL);
} else {
fp_in = fdopen(pfds0[0], "r");
close(pfds0[1]);
fp_out = fdopen(pfds1[1], "w");
close(pfds1[0]);
fputs(Data, fp_out);
fclose(fp_out);
fgets(Answer, AnsLen, fp_in);
fclose(fp_in);
wait(NULL);
}
return 0;
}
-------------------------------
Bei Fragen fragen
Der Code steht übrigens zur freien Verwendung zur Verfügung, ich übernehme allerdings keine Garantie auf Funktionsfähigkeit oder Zweckdienlichkeit (halt die Haftungsauschlussklausel und so...)