Obter saída de `posix_spawn`


9

Para que eu possa executar um processo no Unix / Linux usando POSIX, mas existe alguma maneira de armazenar / redirecionar o STDOUT e o STDERR do processo para um arquivo? O spawn.hcabeçalho contém uma desaceleração posix_spawn_file_actions_adddup2que parece relevante, mas não tenho certeza de como usá-lo.

O processo gera:

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

O armazenamento de saída:

...?


1
O terceiro paramater de posix_spwané um ponteiro do tipo posix_spawn_file_actions_t(que você forneceu como NULL). posix_spawnabrirá, fechará ou duplicará descritores de arquivo herdados do processo de chamada, conforme especificado pelo posix_spawn_file_actions_tobjeto. As posix_spawn_file_actions_{addclose,adddup2}funções são usadas para indicar o que acontece com qual fd.
Muru

@muru - Você acha que poderia adicionar um exemplo de trabalho? Eu entendi que a interação entre as funções é feita por uma "ação de arquivo", mas não está claro como exatamente isso se encaixa, ou onde o local fd é definido.
Nbubis

Respostas:


16

Aqui está um exemplo mínimo de modificação de descritores de arquivo de um processo gerado, salvo como 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);
}

O que isso faz?

  • O terceiro parâmetro de posix_spwané um ponteiro do tipo posix_spawn_file_actions_t(que você forneceu como NULL). posix_spawnabrirá, fechará ou duplicará descritores de arquivo herdados do processo de chamada, conforme especificado pelo posix_spawn_file_actions_tobjeto.
  • Então começamos com um posix_spawn_file_actions_tobjeto ( chiild_fd_actions) e o inicializamos com posix_spawn_file_actions_init().
  • Agora, as posix_spawn_file_actions_{addopen,addclose,addup2}funções podem ser usadas para descritores de arquivos abertos, próximos ou duplicados (após a open(3), close(3)e dup2(3)funções), respectivamente.
  • Então, temos posix_spawn_file_actions_addopenum arquivo no /tmp/foo-logdescritor de arquivos 1(também conhecido como stdout).
  • Então nós posix_spawn_file_actions_adddup2fd 2(aka stderr) para fd 1.
  • Observe que nada foi aberto ou enganado ainda . As duas últimas funções simplesmente mudaram o child_fd_actionsobjeto para observar que essas ações devem ser executadas.
  • E finalmente usamos posix_spawncom o child_fd_actionsobjeto.

Testando:

$ 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

Como você pode ver, foram usados ​​stdout e stderr do processo gerado /tmp/foo-log.


Observe que posix_spawn*não defina errno. Assim, você não pode usar perror(). Use algo como em fprintf(stderr, "...: %s\n", strerror(ret))vez disso. Além disso, a função principal está faltando uma return 0instrução.
maxschlepzig

1

Sim você pode. Definindo a lista correta de ações do arquivo posix spawn definitivamente é o caminho a percorrer.

Exemplo:

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

Compilar e testar:

$ 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

Observe que as posix_spawnfunções não definem errno; ao contrário da maioria das outras funções UNIX, elas retornam um código de erro. Assim, não podemos usar, perror()mas temos que usar algo parecido strerror().

Usamos duas ações de arquivo de reprodução: addopen e addup2. O addopen é semelhante ao normal, open()mas você também especifica um descritor de arquivo que é fechado automaticamente se já estiver aberto (aqui 1, isto é, stdout). O addup2 tem efeitos semelhantes a dup2(), ou seja, o descritor do arquivo de destino (aqui 2, ou seja, stderr) é fechado atomicamente antes de 1 ser duplicado para 2. Essas ações são executadas apenas no filho criado por posix_spawn, ou seja, antes de executar o comando especificado.

Como fork(), posix_spawn()e posix_spawnp()retornar imediatamente para o pai. Portanto, temos que usar waitid()ou waitpid()aguardar explicitamente a child_pidrescisão do contrato.

Ao utilizar nosso site, você reconhece que leu e compreendeu nossa Política de Cookies e nossa Política de Privacidade.
Licensed under cc by-sa 3.0 with attribution required.