Pregunta

Estoy teniendo un montón de problemas para la construcción de un 'intermediario' logger - la intención es colocar en el camino por encima de un elemento en /usr/bin y capturar todo lo que sucede a y desde la aplicación.(Caja negra de la 3º parte de la aplicación está fallando FTP por alguna razón.) Una vez que se ejecute, el intermediario se tenedor, redirigir stdout y stdin a/de las tuberías de que el padre tiene el control y, a continuación, ejecutar el programa en /usr/bin.(Codificado;sí, lo sé, soy malo.)

Sin embargo, una vez que se ejecuta la encuesta(), las cosas se ponen extrañas.Voy a perder la manija de mi logfile, la encuesta a la salida de la tubería desde el niño tira un error, los gatos y los perros comienzan a vivir juntos, et cetera.

¿Alguien puede arrojar algo de luz sobre esto?

Aquí es lo que tengo actualmente...La encuesta() en cuestión está marcado con el no-sangría comentarios para facilitar su ubicación.

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


EDITAR 6/23/09 12:20PM

Después de las correcciones, he intentado ejecutar 'banner' a través de este programa, y aquí está la salida yo...

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

El archivo de registro tiene el siguiente:

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

La razón ERRNO tiene un 0 en es porque poll() devuelve bien;es el pollArray[1].revents, que volvió con un error, lo que significa que childOutPipe[0] votados como tener un error.logChar(), como de lo que puedo decir, nunca se llama.

Voy a tratar de dividir a cabo la encuesta() en dos llamadas diferentes.


Bueno, en el momento que me poll() - incluso en stdin, que no devuelve un mensaje de error - mata mi capacidad para escribir en el archivo de registro.También, descubrí que el bucle while() se ejecuta varias veces antes de la salida de la encuesta viene de vuelta con un error en el tubo.Estoy cada vez más convencido de que la encuesta() es simplemente una causa perdida.
Cada intento de escribir en archivo de registro de error después de la encuesta(), incluso un éxito de la encuesta(), con el valor ajustado a "Malas número de archivo".Esto realmente no debería estar sucediendo.Sinceramente, no puedo ver cómo se estaría afectando mi identificador de archivo.
Bueno, por lo que al parecer soy un imbécil.Gracias por dar me rectas;Yo estaba asumiendo nfds era de un tamaño en bytes, no una matriz de tamaño.Eso es fijo, y voila!No es de matar a mi logFile manejar más.

¿Fue útil?

Solución

Los problemas reales:

1º (pero menor) Problema

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

Usted está haciendo posiblemente infundadas suposiciones sobre el orden y el contenido de 'struct pollfd'.Todas la norma dice que es que contiene (al menos) tres miembros;no dice nada sobre el orden en que aparecen.

El encabezado deberá definir la pollfd estructura, que deberá incluir al menos los siguientes miembros:

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

Ya que usted está utilizando C99, utiliza el seguro de inicialización de la notación:

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

Usted podría reemplazar el 0 para la entrada estándar con FILENO_STDIN de <fcntl.h>.

2º (la gran) Problema

    nfds_t nfds = sizeof(pollArray);

El tamaño de la encuesta de la matriz es, probablemente, 16 (bytes) - en la mayoría pero no todas las máquinas (32-bit y 64-bit).Usted necesita la dimensión de la encuesta de la matriz (que es 2).Esta es la razón por todo el infierno se rompe suelto;el sistema está buscando en la basura y confundirse.

Abordar un comentario:

Para encontrar la dimensión de una matriz se define en el archivo local o función (pero no de una matriz de parámetros pasados a una función, ni una matriz definida en otro archivo), el uso de una variante de la macro:

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

Este nombre nos remite a la utilización de BÁSICA en el oscuro pasado, lejano;otros nombres que he visto son NELEMS o ARRAY_SIZE o DIMENSION (se remonta a Fortran IV), y estoy seguro de que hay un montón de otras personas.

Lo que sucede es que debido a que no se ajuste nfds 2, la llamada al sistema es la lectura de los datos después de que el real struct pollfd de la matriz, y tratando de hacer que la cabeza o la cola de cosas que no es una struct pollfd.En particular, es probablemente escrito en lo que has dicho es la revents campo de una fila en el struct pollfd de la matriz, pero el espacio real es el registro de FILE *, por lo que es completamente jodido.Lo mismo para otras variables locales.En otras palabras, tienes un stack buffer overflow - también conocido como Desbordamiento de la Pila, un nombre que debe ser vagamente familiar.Pero lo que está ocurriendo debido a que usted programó.

Revisión:

    nfds_t nfds = DIM(pollArray);

3º (de grado medio) problema

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

El resultado de poll() no se guarda, y la variable errcode nunca se le asigna un valor, sin embargo, comprobar cuál es el valor inmediatamente después.El código corregido probablemente leer:

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

Nota el carácter de nueva línea añadido el mensaje de error - usted lo necesita.O:

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

En el segundo caso, tendría que añadir"#include <errno.h>'a la cabecera de la lista.Guardar el valor de errno conserva contra el cambio mediante llamadas a la función, pero sólo pueden fiable de la prueba errno cuando una función (llamada al sistema) ha fallado.Incluso el éxito de la función de llamadas puede dejar errno distinto de cero.(Por ejemplo, en algunos sistemas, si stderr no es ir a un terminal, el valor de errno después de una llamada de e/S es ENOTTY, aunque la llamada como todo un éxito.)


Anterior cavilaciones

Algunos antes de los pensamientos sobre lo que podría ser el problema;Creo que todavía hay un poco de información útil aquí abajo.

Sospecho que tu problema es que poll() "daños y perjuicios" el conjunto de los encuestados descriptores, y tiene que volver a generar en cada bucle. (Después de haber comprobado la página de manual en el Grupo Abierto, parece que poll() no tiene los problemas que select() sufre de.) Este es sin duda un problema con el select() llamada del sistema.

Su hijo, el código no es el cierre de todos los descriptores de archivo cuando se debe - usted ha comentado un 'close()` y no es otro que falta por completo.Cuando el niño ha terminado de conectar las tuberías de entrada y salida estándar, usted no quiere que la onu-dupped descriptores de fichero abiertos todavía;los procesos no pueden detectar EF correctamente.

Comentarios similares se pueden aplicar en el padre.

También, tenga en cuenta que el proceso de envío puede ser que necesite para enviar múltiples paquetes de datos para que el niño antes de que cualquier cosa que aparezca en la salida estándar.Como un caso extremo de considerar 'sort';que lee todos sus datos antes de generar cualquier tipo de salida. Me preocupa la dirección de conmutación código, por lo tanto, a pesar de que no he digerido por completo lo que hace. De sí mismo, la dirección de conmutación es inofensivo, simplemente escribe la nueva dirección cuando se empieza a escribir en la dirección opuesta a la de la última vez.

Más en serio, no uso único personaje de lee y escribe;leer sensible búferes de tamaño completo.Sensible tamaño podría ser casi cualquier potencia de dos entre 256 y 8192;usted puede elegir otros tamaños en la libertad (el tamaño de la tubería de búfer que podría ser un buen tamaño a elegir).Manejo de múltiples personajes en un tiempo va a mejorar enormemente el rendimiento.


La forma en que me han resuelto problemas similares es por tener dos procesos que realizan la vigilancia, uno para la entrada estándar y la otra para la salida estándar o los equivalentes.Esto significa que no es necesario el uso de poll() (o select()) a todos.El proceso de manipulación de la entrada estándar se lee y se bloquea en espera de más información;cuando algo llega, lo registra y lo escribe en el niño de la entrada estándar.De manera similar para el proceso de manejo de la salida estándar.

Puedo sacar el código que funciona con tubos si usted necesita (ver mi perfil).Lo miré un año o hace dos (hmmm;la última de las ediciones de 2005, de hecho, a pesar de que me compilar en 2007) y que todavía estaba en orden de trabajo (que fue escrito alrededor del año 1989).También tengo el código que funciona en sockets en lugar de los tubos.Se necesitaría una adaptación para satisfacer sus necesidades;ellos eran bastante especializados (y la versión de canalización, en particular, es consciente de que un cliente-servidor de base de datos de protocolo y los intentos para manejar paquetes completos de la información).

Licenciado bajo: CC-BY-SA con atribución
No afiliado a StackOverflow
scroll top