Holen Sie sich die Ausgabe von `posix_spawn`

9

Ich kann also einen Prozess unter Unix / Linux mit POSIX ausführen. Gibt es jedoch eine Möglichkeit, STDOUT und STDERR des Prozesses in einer Datei zu speichern / umzuleiten? Der spawn.hHeader enthält eine Verzögerung, posix_spawn_file_actions_adddup2die relevant aussieht, aber ich bin mir nicht sicher, wie ich sie verwenden soll.

Der Prozess erzeugt:

posix_spawn(&processID, (char *)"myprocess", NULL, NULL, args, environ);

Der Ausgabespeicher:

...?

nbubis
quelle
1
Der dritte Parameter von posix_spwanist ein Zeiger vom Typ posix_spawn_file_actions_t(einer, den Sie als angegeben haben NULL). posix_spawnöffnet, schließt oder dupliziert Dateideskriptoren, die vom aufrufenden Prozess geerbt wurden, wie vom posix_spawn_file_actions_tObjekt angegeben. Die posix_spawn_file_actions_{addclose,adddup2}Funktionen werden verwendet, um anzuzeigen, was mit welchem ​​fd passiert.
Muru
@muru - Denkst du, du könntest ein funktionierendes Beispiel hinzufügen? Ich habe verstanden, dass die Interaktion zwischen den Funktionen durch eine "Dateiaktion" erfolgt, aber es ist nicht klar, wie genau dies zusammenpasst oder wo der fd-Speicherort definiert ist.
Nbubis

Antworten:

16

Hier ist ein minimales Beispiel für das Ändern von Dateideskriptoren eines gespawnten Prozesses, gespeichert als foo.c:

#include <stdio.h>
#include <stdlib.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <spawn.h>

int main(int argc, char* argv[], char *env[])
{
    int ret;
    pid_t child_pid;
    posix_spawn_file_actions_t child_fd_actions;
    if (ret = posix_spawn_file_actions_init (&child_fd_actions))
        perror ("posix_spawn_file_actions_init"), exit(ret);
    if (ret = posix_spawn_file_actions_addopen (&child_fd_actions, 1, "/tmp/foo-log", 
            O_WRONLY | O_CREAT | O_TRUNC, 0644))
        perror ("posix_spawn_file_actions_addopen"), exit(ret);
    if (ret = posix_spawn_file_actions_adddup2 (&child_fd_actions, 1, 2))
        perror ("posix_spawn_file_actions_adddup2"), exit(ret);

    if (ret = posix_spawnp (&child_pid, "date", &child_fd_actions, NULL, argv, env))
        perror ("posix_spawn"), exit(ret);
}

Was tut es?

  • Der dritte Parameter von posix_spwanist ein Zeiger vom Typ posix_spawn_file_actions_t(einer, den Sie als angegeben haben NULL). posix_spawnöffnet, schließt oder dupliziert Dateideskriptoren, die vom aufrufenden Prozess geerbt wurden, wie vom posix_spawn_file_actions_tObjekt angegeben.
  • Also beginnen wir mit einem posix_spawn_file_actions_tObjekt ( chiild_fd_actions) und initialisieren es mit posix_spawn_file_actions_init().
  • Nun werden die posix_spawn_file_actions_{addopen,addclose,addup2}Funktionen können zu öffnen, schließen oder doppelte Datei - Deskriptoren verwendet werden (nach dem open(3), close(3)und dup2(3)Funktionen) sind.
  • Also haben wir posix_spawn_file_actions_addopeneine Datei an /tmp/foo-logDatei-Deskriptor 1(auch bekannt als stdout).
  • Dann posix_spawn_file_actions_adddup2fd 2(aka stderr) zu fd 1.
  • Beachten Sie, dass nichts geöffnet oder hinters Licht geführt wurde noch . Die letzten beiden Funktionen haben das child_fd_actionsObjekt einfach geändert, um zu beachten, dass diese Aktionen ausgeführt werden sollen.
  • Und schließlich verwenden wir posix_spawnmit dem child_fd_actionsObjekt.

Testen Sie es aus:

$ make foo
cc     foo.c   -o foo
$ ./foo
$ cat /tmp/foo-log 
Sun Jan  3 03:48:17 IST 2016
$ ./foo +'%F %R'  
$ cat /tmp/foo-log
2016-01-03 03:48
$  ./foo -d 'foo'  
$ cat /tmp/foo-log
./foo: invalid date foo

Wie Sie sehen können, gingen sowohl stdout als auch stderr des erzeugten Prozesses an /tmp/foo-log.

muru
quelle
Beachten Sie, dass posix_spawn*errno nicht gesetzt wird. Sie können also nicht verwenden perror(). Verwenden Sie fprintf(stderr, "...: %s\n", strerror(ret))stattdessen so etwas wie . Außerdem fehlt der Hauptfunktion eine return 0Anweisung.
Maxschlepzig
1

Ja, du kannst. Das Definieren der richtigen Liste von Aktionen für Posix-Spawn-Dateien ist definitiv der richtige Weg.

Beispiel:

#include <errno.h>
#include <fcntl.h>
#include <spawn.h>
#include <stdio.h>
#include <string.h>    
#define CHECK_ERROR(R, MSG) do { if (R) { fprintf(stderr, "%s: %s\n",
        (MSG), strerror(R)); return 1; } } while (0)    
extern char **environ;   
int main(int argc, char **argv)
{
    if (argc < 3) {
        fprintf(stderr, "Call: %s OUTFILE COMMAND [ARG]...\n", argv[0]);
        return 2;
    }
    const char *out_filename = argv[1];
    char **child_argv = argv+2;
    posix_spawn_file_actions_t as;
    int r = posix_spawn_file_actions_init(&as);
    CHECK_ERROR(r, "actions init");
    r = posix_spawn_file_actions_addopen(&as, 1, out_filename,
            O_CREAT | O_TRUNC | O_WRONLY, 0644);
    CHECK_ERROR(r, "addopen");
    r = posix_spawn_file_actions_adddup2(&as, 1, 2);
    CHECK_ERROR(r, "adddup2");
    pid_t child_pid;
    r = posix_spawnp(&child_pid, child_argv[0], &as, NULL,
            child_argv, environ);
    CHECK_ERROR(r, "spawnp");
    r = posix_spawn_file_actions_destroy(&as);
    CHECK_ERROR(r, "actions destroy");
    return 0;
}

Kompilieren und testen:

$ cc -Wall -g -o spawnp spawnp.c
$ ./spawnp log date -I
$ cat log
2018-11-03
$ ./a.out log dat 
spawnp: No such file or directory

Beachten Sie, dass die posix_spawnFunktionen nicht errno setzen, sondern im Gegensatz zu den meisten anderen UNIX-Funktionen einen Fehlercode zurückgeben. Daher können wir nicht verwenden, perror()sondern müssen so etwas verwenden strerror().

Wir verwenden zwei Aktionen für Spawn-Dateien: addopen und addup2. Das Addopen ähnelt einem normalen, open()aber Sie geben auch einen Dateideskriptor an, der automatisch geschlossen wird, wenn er bereits geöffnet ist (hier 1, dh stdout). Das Addup2 hat ähnliche Auswirkungen wie dup2(), dh der Zieldateideskriptor (hier 2, dh stderr) wird atomar geschlossen, bevor 1 zu 2 dupliziert wird. Diese Aktionen werden nur in dem von erstellten untergeordneten Element ausgeführt posix_spawn, dh unmittelbar bevor es den angegebenen Befehl ausführt .

Wie fork(), posix_spawn()und posix_spawnp()sofort wieder an die Eltern. Daher müssen wir die Kündigung verwenden waitid()oder waitpid()explizit darauf warten child_pid.

maxschlepzig
quelle