Beispielcode ohne großartige Erklärung.
In dem Programm wird die Ausgabe von stdout durch ein Log-Prefix in jeder Zeile ergänzt.
Beispielcode ohne großartige Erklärung.
In dem Programm wird die Ausgabe von stdout durch ein Log-Prefix in jeder Zeile ergänzt.
Um in C ein externes Programm auszuführen, und dabei auf die Standardein- und Ausgabe des Programms zugreifen, muss man dies per Hand mit ein paar Syscalls machen. Es gibt zwar auch die Funktion popen, aber damit kann man nur in stdin des Programms schreiben, oder stdout lesen.
Programme werden in der Unixwelt gestartet, in dem mit fork der aktuelle Prozess kopiert wird. In dem neuen Kind-Prozess wird dann mit exec das gewünschte Programm ausgeführt.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char **argv) {
pid_t pid = fork();
if(pid == 0) {
// child
return execl("/usr/bin/echo", "echo", "hello world");
} else if(pid > 0) {
// parent
int status = -1;
waitpid(pid, &status, 0); // wait for child termination
} else {
perror("fork");
return EXIT_FAILURE;
}
return EXIT_SUCCESS;
}
Dies ist ein einfaches Grundgerüst, um ein Programm zu starten. Jetzt müssen wir noch stdin, stdout und stderr im Kind-Prozess umleiten. Dafür werden 3 Pipes benötigt, die vor dem Fork erstellt werden müssen.
int newin[2];
int newout[2];
int newerr[2];
if(pipe(newin) || pipe(newout) || pipe(newerr)) {
perror("pipe");
return EXIT_FAILURE;
}
Der Syscall pipe liefert uns 2 File-Deskriptoren, aus dem 1. im Array kann gelesen und in den 2. geschrieben werden. Nach dem Fork stehen die Pipes in beiden Prozessen zur Verfügung. Was jetzt noch fehlt ist, dass im Kind-Prozess die Filedeskriptoren für stdin, stdout und stderr auf die Pipe-Filedeskriptoren zeigen. Dies erledigen wir mit dup2. Dies kopiert unsere Pipe-Filedeskriptoren und ersetzt damit stdin, stdout und stderr im Kind-Prozess.
// child
// we need stdin, stdout and stderr refer to the previously
// created pipes
if(dup2(newin[0], STDIN_FILENO) == -1) {
perror("dup2");
return EXIT_FAILURE;
}
if(dup2(newout[1], STDOUT_FILENO) == -1) {
perror("dup2");
return EXIT_FAILURE;
}
if(dup2(newerr[1], STDERR_FILENO) == -1) {
perror("dup2");
return EXIT_FAILURE;
}
Danach würde im Kind-Prozess ein einfaches printf("Hello World!\n");
in unserer Pipe landen und könnte im Parent aus der Pipe gelesen werden.
Jetzt fehlt nur noch eine Kleinigkeit. Durch fork wurden alle Filedeskriptoren kopiert. Einige davon müssen geschlossen werden, weil sonst das Ende der Streams nicht erreicht werden könnte.
Im Kind-Prozess:
close(newin[1]);
Im Eltern-Prozess:
close(newout[1]);
close(newerr[1]);
Den kompletten Beispielcode gibt es hier. Es wird dort das Programm bc aufgerufen, die Standardeingabe davon befüllt und danach die die Ausgabe des Programms gelesen.
Betrachten wir zunächst folgendes Beispielprogramm:
#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv) {
printf("sizeof(void*) = %d\n", sizeof(void*));
printf("sizeof(off_t) = %d\n", sizeof(off_t));
FILE *file = fopen("testfile", "w");
if(!file) return -1;
size_t len = 1024*1024*256;
char *buf = malloc(len);
int count = 0;
while(fwrite(buf, 1, len, file) == len) {
count++;
if(count >= 10)
break;
}
printf("count: %d\n", count);
fclose(file);
return 0;
}
Das Programm öffnet eine Datei und schreibt dann 10 mal einen 256MB großen Block. Insgesammt sollte eine 2,5GB große Datei rauskommen. Kompilieren wir das Programm nun im 32bit Modus mit cc -m32 -o test test.c
wird die Schleife nicht 10 mal ausgeführt, denn vorher überschreitet die Datei eine Größe von 2GB. Die APIs können auf Grund des 32bit Limits nicht mit größeren Dateien umgehen.
Es gibt jedoch ein Large File Interface für 32bit Anwendungen, damit diese auch mit größeren Dateien umgehen können. Dazu stehen neben den typischen Funktionen, wie z.B. open, fopen, stat, lseek, fseek, ftell, usw., 64bit fähige Funktionen zur Verfügung, deren Name einfach nur um ein 64 erweitert wurde (open64, fopen64, ...). Außerdem gibt es zusätzliche Typen wie off64_t.
Jetzt wäre es etwas unschön, müsste man immer diese 64 bit fähigen Funktionen direkt verwenden, und daher seinen ganzen Code anpassen. Doch definiert man _FILE_OFFSET_BITS=64 stehen unter den alten Namen die 64bit fähigen Funktionen bzw. Typen zur Verfügung. Damit ist off_t nun in 32bit Anwendungen 64bit groß und fopen wird transparent durch fopen64 ersetzt. Wir können also das obere Beispiel einfach mit cc -D_FILE_OFFSET_BITS=64 -o test test.c
kompilieren und es verhält sich dann wie sein 64bit Pendant.
Das ganze sollte unter jedem unixoiden Betriebsystem funktionieren. Unter OS X sind die IO-Funktionen allerdings standardmäßig schon im 64bit-Modus und die *64-Funktionen existieren dort nicht.
Siehe auch: lf64(5)
Unter der Leitung von freedesktop.org wurden einige Teile von Unix-Desktop-Umgebungen vereinheitlicht, unter anderem das Handling von Mime-Typen. Hierfür gibt es die XDG MIME Application specification.
Außerdem gibt es die offiziellen xdg-utils für den Umgang mit Mime-Typen. Mit diesen können unter anderem neue Mime-Typen installiert, Standard-Anwendungen konfiguriert oder Informationen abgefragt werden.
Praktisch im alltäglichen Gebrauch ist xdg-open. Dies öffnet eine Datei mit der dazugehörigen Standard-Anwendung. Dies ist nicht nur hilfreich, wenn man vom Terminal aus deine Datei mit dem passenden grafischen Programm öffnen möchte, sondern kann auch gut in eigenen Programmen verwendet werden, wenn diese externe Programme wie z.B. den Standard-Browser öffnen sollen.
Die xdg-utils dürften bei allen (Unix-)Desktop-Umgebungen dabei sein.