Récupère la sortie de `posix_spawn`

9

Je peux donc exécuter un processus sous Unix / Linux en utilisant POSIX, mais existe-t-il un moyen de stocker / rediriger à la fois le STDOUT et le STDERR du processus vers un fichier? L'en- spawn.htête contient une décélération posix_spawn_file_actions_adddup2qui semble pertinente, mais je ne sais pas trop comment l'utiliser.

Le processus apparaît:

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

Le stockage de sortie:

...?

nbubis
la source
1
Le troisième paramètre de posix_spwanest un pointeur de type posix_spawn_file_actions_t(celui que vous avez donné comme NULL). posix_spawnouvrira, fermera ou dupliquera les descripteurs de fichiers hérités du processus appelant comme spécifié par l' posix_spawn_file_actions_tobjet. Les posix_spawn_file_actions_{addclose,adddup2}fonctions sont utilisées pour indiquer ce qui arrive à quel fd.
muru
@muru - Pensez-vous que vous pourriez ajouter un exemple de travail? J'ai compris que l'interaction entre les fonctions se faisait par une "action de fichier", mais on ne sait pas exactement comment cela s'emboîtait, ni où l'emplacement fd était défini.
nbubis

Réponses:

16

Voici un exemple minimal de modification des descripteurs de fichier d'un processus généré, enregistré sous 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);
}

Qu'est ce que ça fait?

  • Le troisième paramètre de posix_spwanest un pointeur de type posix_spawn_file_actions_t(celui que vous avez donné comme NULL). posix_spawnouvrira, fermera ou dupliquera les descripteurs de fichiers hérités du processus appelant comme spécifié par l' posix_spawn_file_actions_tobjet.
  • Nous commençons donc avec un posix_spawn_file_actions_tobjet ( chiild_fd_actions), et l'initialisons avec posix_spawn_file_actions_init().
  • Maintenant, les posix_spawn_file_actions_{addopen,addclose,addup2}fonctions peuvent être utilisées pour ouvrir, fermer ou dupliquer des descripteurs de fichiers (après les fonctions open(3), close(3)et dup2(3)) respectivement.
  • Nous avons donc posix_spawn_file_actions_addopenun fichier à /tmp/foo-logun descripteur de fichier 1(aka stdout).
  • Ensuite, nous posix_spawn_file_actions_adddup2fd 2(aka stderr) à fd 1.
  • Notez que rien n'a été ouvert ou dupés encore . Les deux dernières fonctions ont simplement modifié l' child_fd_actionsobjet pour noter que ces actions doivent être effectuées.
  • Et enfin nous utilisons posix_spawnavec l' child_fd_actionsobjet.

Le tester:

$ 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

Comme vous pouvez le voir, stdout et stderr du processus engendré sont tous deux passés à /tmp/foo-log.

muru
la source
Notez que posix_spawn*ne définissez pas errno. Ainsi, vous ne pouvez pas utiliser perror(). Utilisez quelque chose comme à la fprintf(stderr, "...: %s\n", strerror(ret))place. En outre, la fonction principale manque une return 0instruction.
maxschlepzig
1

Oui, vous pouvez. Définir la bonne liste d'actions de fichiers de réapparition posix est définitivement la voie à suivre.

Exemple:

#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;
}

Compiler et tester:

$ 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

Notez que les posix_spawnfonctions ne définissent pas errno, contrairement à la plupart des autres fonctions UNIX, elles renvoient un code d'erreur. Ainsi, nous ne pouvons pas utiliser perror()mais devons utiliser quelque chose comme strerror().

Nous utilisons deux actions de création de fichier: addopen et addup2. L'addopen est similaire à un normal open()mais vous spécifiez également un descripteur de fichier qui est automatiquement fermé s'il est déjà ouvert (ici 1, c'est-à-dire stdout). L'addup2 a des effets similaires à dup2(), c'est- à -dire que le descripteur de fichier cible (ici 2, c'est-à-dire stderr) est atomiquement fermé avant que 1 ne soit dupliqué sur 2. Ces actions ne sont exécutées que dans l'enfant créé par posix_spawn, c'est-à-dire juste avant d'exécuter la commande spécifiée.

Comme fork(), posix_spawn()et posix_spawnp()revenez immédiatement au parent. Ainsi, nous devons utiliser waitid()ou waitpid()attendre explicitement child_pidla résiliation de.

maxschlepzig
la source