Pergunta

Estou tendo um monte de construção de problemas de 'intermediário' logger - a intenção é colocá-lo no caminho acima um item em / usr / bin e capturar tudo o que acontece de e para a aplicação. (Caixa preta 3-parte aplicativo está falhando FTP por algum motivo.) Uma vez executado, o intermediário será bifurcar, redirecionamento stdout e stdin de / para tubos que o pai tem o controle, e, em seguida, executar o programa em / usr / bin. (Hardcoded;. Sim, eu sei, eu sou ruim)

No entanto, uma vez que eu executar poll (), as coisas ficam estranhas. I perder a alça para o meu arquivo de log, a pesquisa sobre o tubo de saída da criança joga um erro, gatos e cães começar a viver juntos, et cetera.

Alguém pode lançar alguma luz sobre isso?

Aqui está o que eu tenho atualmente ... O poll () em questão é marcada com comentários não-recuado para facilidade de localização.

#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <poll.h>
#include <time.h>
#include <sys/types.h>
#include <fcntl.h>

#define MAX_STR_LEN 1024
static int directionFlag; /* 0 = input, 1 = output */
static int eofFlag;

/* Splits the next char from the stream inFile, with extra
information logged if directionFlag swaps */
void logChar(int inFilDes, int outFilDes, FILE *logFile, int direction)
{
    char inChar = 0;
    if(read(inFilDes, &inChar, sizeof(char)) > 0)
    {

        if(direction != directionFlag)
        {
            directionFlag = direction;
            if(direction)
            {
                fprintf(logFile, "\nOUTPUT: ");
            } else {
                fprintf(logFile, "\nINPUT: ");
            }
        }

        write(outFilDes, &inChar, sizeof(char));
        fputc(inChar, stderr);
        fputc(inChar, logFile);
    } else {
        eofFlag = 1;
    }
    return;
}

int main(int argc, char* argv[])
{
    pid_t pid;

    int childInPipe[2];
    int childOutPipe[2];

    eofFlag = 0;

    /* [0] is input, [1] is output*/

    if(pipe(childInPipe) < 0 || pipe(childOutPipe) < 0) {
        fprintf(stderr,"Pipe error; aborting\n");
            exit(1);
    }

    if((pid = fork()) == -1){
        fprintf(stderr,"Fork error; aborting\n");
        exit(1);
    }

    if(pid)
    {
        /*Parent process*/

        int i;
        int errcode;
        time_t rawtime;
        struct tm * timeinfo;
        time(&rawtime);
        timeinfo=localtime(&rawtime);

        struct pollfd pollArray[2] = {
            { .fd = 0, .events = POLLIN, .revents = 0 },
            { .fd = childOutPipe[0], .events = POLLIN, .revents = 0 }
        };
        /* Yet again, 0 = input, 1 = output */

        nfds_t nfds = sizeof(struct pollfd[2]);

        close(childInPipe[0]);
        close(childOutPipe[1]);

        /* We don't want to change around the streams for this one,
        as we will be logging everything - and I do mean everything */

        FILE *logFile;
        if(!(logFile = fopen("/opt/middleman/logfile.txt", "a"))) {
            fprintf(stderr, "fopen fail on /opt/middleman/logfile.txt\n");
            exit(1);
        }

        fprintf(logFile, "Commandline: ");

        for(i=0; i < argc; i++)
        {
            fprintf(logFile, "%s ", argv[i]);
        }
        fprintf(logFile, "\nTIMESTAMP: %s\n", asctime(timeinfo));

        while(!eofFlag)
        {

// RIGHT HERE is where things go to pot
            errcode = poll(pollArray, nfds, 1);
// All following fprintf(logfile)s do nothing
            if(errcode < 0) {
                fprintf(stderr, "POLL returned with error %d!", errcode);
                eofFlag = 1;
            }
            if((pollArray[0].revents && POLLERR) & errno != EAGAIN ) {
                fprintf(stderr, "POLL on input has thrown an exception!\n");
                fprintf(stderr, "ERRNO value: %d\n", errno);
                fprintf(logFile, "POLL on input has thrown an exception!\n");
                eofFlag = 1;
            } else if(pollArray[0].revents && POLLIN) {
                logChar(pollArray[0].fd, childInPipe[1], logFile, 0);
            } else if((pollArray[1].revents && POLLERR) & errno != EAGAIN ) {
                fprintf(stderr, "POLL on output has thrown an exception!\n");
                fprintf(stderr, "ERRNO value: %d\n", errno);
                fprintf(logFile, "POLL on output has thrown an exception!\n");
                eofFlag = 1;
            } else if(pollArray[1].revents && POLLIN) {
                logChar(pollArray[1].fd, 1, logFile, 1);
            }

        }

        fclose(logFile);

    }
    else
    {
        /*Child process; switch streams and execute application*/
        int i;
        int catcherr = 0;
        char stmt[MAX_STR_LEN] = "/usr/bin/";

        close(childInPipe[1]);
        close(childOutPipe[0]);

        strcat(stmt, argv[0]);

        if(dup2(childInPipe[0],0) < 0) {
            fprintf(stderr, "dup2 threw error %d on childInPipe[0] to stdin!\n", errno);
        }
//      close(childInPipe[0]);

        if(dup2(childOutPipe[1],1) < 0)
        {
            fprintf(stderr, "dup2 threw error %d on childInPipe[1] to stdout!\n", errno);
        }

        /* Arguments need to be in a different format for execv */
        char* args[argc+1];
        for(i = 0; i < argc; i++)
        {
            args[i] = argv[i];
        }
        args[i] = (char *)0;

        fprintf(stderr, "Child setup complete, executing %s\n", stmt);
        fprintf(stdout, "Child setup complete, executing %s\n", stmt);

        if(execv(stmt, args) == -1) {
            fprintf(stderr, "execvP error!\n");
            exit(1);
        }
    }
    return 0;
}


EDIT 6/23/09 12:20

Após as correções, tentei run 'bandeira' através deste programa, e aqui está a saída fico ...

Child setup complete, executing /usr/bin/banner
POLL on output has thrown an exception!
ERRNO value: 0

O arquivo de log tem o seguinte:

Commandline: banner testing 
TIMESTAMP: Tue Jun 23 11:21:00 2009

O ERRNO razão tem um 0 nela é porque poll () retorna apenas multa; é o pollArray [1] .revents que voltaram com um erro, o que significa childOutPipe [0] entrevistados como tendo um erro. logChar (), tanto quanto eu posso dizer, nunca é chamado.

Eu vou tentar rachar a poll () em duas chamadas diferentes.


Ok, a pesquisa momento I () - mesmo em stdin, que não retorna com uma mensagem de erro - ele mata a minha capacidade de gravação para o arquivo de log. Além disso, descobri que o loop while () é executado várias vezes antes da votação saída de volta com um erro no tubo. Estou cada vez mais convencido de que poll () é simplesmente uma causa perdida.
Toda tentativa de gravação para logFile falhar após a votação (), mesmo uma pesquisa bem sucedida (), com errno definido como "número de arquivo Bad". Isso realmente não deveria estar acontecendo. Eu honestamente não posso ver como ele iria estar afetando meu identificador de arquivo.
Ok, então, aparentemente, eu sou um idiota. Obrigado por me definir reta; Eu estava assumindo NFDS era de um tamanho de bytes, não um tamanho de matriz. Isso é fixo, e voila! Não está matando meu punho logFile mais.

Foi útil?

Solução

Os problemas reais:

1º (mas menor) Problema

struct pollfd pollArray[2] = {{0, POLLIN, 0}, {childOutPipe[0], POLLIN, 0}};

Você está fazendo suposições possivelmente injustificadas sobre a ordem e conteúdo do 'pollfd struct'. Todo o padrão diz é que ele contém (pelo menos) três membros; não diz nada sobre a ordem em que eles aparecem.

O cabeçalho deve definir a estrutura pollfd, que inclua, pelo menos, os seguintes membros:

int    fd       The following descriptor being polled. 
short  events   The input event flags (see below). 
short  revents  The output event flags (see below). 

Uma vez que você estiver usando C99, use a notação de inicialização segura:

    struct pollfd pollArray[2] =
    {
        { .fd = 0,               .events = POLLIN, .revents = 0 },
        { .fd = childOutPipe[0], .events = POLLIN, .revents = 0 },
    };

Você poderia substituir a 0 para a entrada padrão com FILENO_STDIN de <fcntl.h>.

2º (o principal) Problema

    nfds_t nfds = sizeof(pollArray);

O tamanho da matriz de sondagem é provavelmente 16 (bytes) - na maioria, mas nem todas as máquinas (32 bits e de 64 bits). Você precisa da dimensão da matriz poll (que é 2). É por isso que ele explodir; o sistema está olhando para lixo e ficando confuso.

Dirigindo um comentário :

Para a dimensão de uma matriz definida no arquivo ou função local (mas não um parâmetro de matriz passado para uma função, nem uma matriz definida em outro arquivo), use uma variante da macro:

#define DIM(x) (sizeof(x)/sizeof(*(x)))

Este nome remete ao uso do BASIC no escuro, passado distante; outros nomes que eu vi são NELEMS ou ARRAY_SIZE ou DIMENSION (remontando ao Fortran IV), e eu tenho certeza que existem muitos outros.

O que está acontecendo é que porque você não está definindo nfds a 2, a chamada de sistema é a leitura de dados Após a matriz struct pollfd real, e tentando fazer cabeça ou cauda de coisas que não é um struct pollfd. Em particular, é provável que escrever sobre o que você disse é o campo revents de uma linha na matriz struct pollfd, mas o espaço real é o FILE * log, de modo que é completamente asneira. Da mesma forma para outras variáveis ??locais. Em outras palavras, você tem uma pilha de estouro de buffer - aka Stack Overflow, um nome que deve ser levemente familiar. Mas isso está acontecendo porque você programou.

Fix:

    nfds_t nfds = DIM(pollArray);

3 (grau médio) problema

   poll(pollArray, nfds, 1);
   if (errcode < 0) {

O resultado da poll() não é salvo, eo errcode variável nunca é atribuído um valor, mas você verificar qual é o valor imediatamente depois. O código corrigido provavelmente seria:

errcode = poll(pollArray, nfds, 1);
if (errcode < 0)
{
    fprintf(stderr, "POLL returned with error %d!\n", errcode);
    eofFlag = 1;
}

Observe o caractere de nova linha adicionada à mensagem de erro - você precisar dele. Ou:

if (poll(pollArray, nfds, 1) < 0)
{
    int errnum = errno;
    fprintf(stderr, "POLL returned with error (%d: %s)\n",
            errnum, strerror(errnum));
    eofFlag = 1;
}

No segundo caso, você gostaria de acrescentar '#include <errno.h>' à lista de cabeçalho. Salvando o valor de conservas errno-lo contra a mudança por chamadas de função - mas você só pode confiavelmente errno teste quando uma função (chamada de sistema) falhou. Mesmo chamadas de função de sucesso pode deixar errno não-zero. (Por exemplo, em alguns sistemas, se stderr não vai a um terminal, o valor de errno após uma chamada de I / O é ENOTTY, mesmo que a chamada como um todo conseguiu.)


ruminações anterior

Alguns pensamentos anteriores sobre o que poderia ser o problema; Eu acho que ainda há alguma informação útil aqui.

Eu suspeito que o problema é que poll() 'danos' o conjunto de descritores pesquisados, e você tem que reconstruí-lo em cada loop. (Tendo verificado a página do manual para o Open grupo , parece que poll() não tem os problemas que sofre select() de) Esta é certamente uma. problema com a chamada de sistema select() relacionado.

Seu código criança não está fechando todos os descritores de arquivo quando deveria - você comentado um 'close () `e não há outra faltando completamente. Quando a criança tem finished conectar canos para entrada e saída padrão, você não quer que os descritores de arquivos un-dupped ainda em aberto; os processos não pode detectar EOF corretamente.

Comentários semelhantes podem ser aplicadas no pai.

Além disso, nota que o processo de envio pode precisar enviar vários pacotes de dados para a criança antes de aparecer qualquer coisa na saída padrão da criança. Como um caso extremo, considere 'sort'; que lê todos os seus dados antes de gerar qualquer saída. . Eu me preocupo com o código de comutação direção, portanto, embora eu não tenho totalmente digerido o que ele faz Por si só, a comutação direção é inofensivo - ele simplesmente escreve a nova direção quando se começa a escrever na frente direção da última vez.

Mais a sério, não use um único caractere lê e escreve; leia buffers tamanho razoável completo. tamanho razoável pode ser quase qualquer poder de dois entre 256 e 8192; você poderia escolher outros tamanhos em liberdade (o tamanho do buffer de tubo pode ser um bom tamanho para escolher). Manipulação de vários personagens em um tempo vai melhorar muito o desempenho.


A forma como eu ter resolvido problemas semelhantes é por ter dois processos que fazem o monitoramento, uma para a entrada padrão e outro para a saída padrão - ou equivalentes. Isso significa que eu não precisar usar poll() (ou select()) em tudo. O processo de lidar com a entrada padrão lê e blocos à espera de mais informações; quando algo chega, ele registra-o e escreve-o para a entrada padrão da criança. Da mesma forma para o processo de manipulação de saída padrão.

Eu posso cavar o código que funciona com tubos se você precisar dele (ver o meu perfil). Olhei para ele um ou dois anos atrás (hmmm; últimas edições, em 2005, na verdade, embora eu recompilados que em 2007) e ele ainda estava em ordem de trabalho (ele foi escrito por volta de 1989). Eu também tenho código que funciona em sockets em vez de tubos. Eles precisam de alguma adaptação para atender às suas necessidades; eles foram bastante especializado (ea versão de tubo, em particular, está ciente de um protocolo de banco de dados cliente-servidor e tentativas de manipular pacotes completos de informações).

Licenciado em: CC-BY-SA com atribuição
Não afiliado a StackOverflow
scroll top