NAME ^

src/io/io_unix.c - UNIX IO layer

DESCRIPTION ^

This is the Parrot UNIX IO layer. It implements unbuffered, low-level, UNIX-specific functionality.

As "UNIX" is already a generalization, it may be necessary to create separate OS-specific layers for UNIX flavors, to avoid over-complicating this file.

References: ^

APitUE - W. Richard Stevens, AT&T SFIO, Perl5 (Nick Ing-Simmons)

Functions ^

*/

#include "parrot/parrot.h" #include "io_private.h"

#ifdef PIO_OS_UNIX

/* Defined at bottom */ extern const ParrotIOLayerAPI pio_unix_layer_api;

ParrotIOLayer pio_unix_layer = { NULL, "unix", PIO_L_TERMINAL, &pio_unix_layer_api, 0, 0 };

/* HEADERIZER HFILE: none */

/* HEADERIZER BEGIN: static */

PARROT_CONST_FUNCTION static INTVAL flags_to_unix( INTVAL flags );

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static ParrotIO * PIO_unix_accept( PARROT_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io) ) __attribute__nonnull__(1) __attribute__nonnull__(3);

INTVAL PIO_unix_async( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), INTVAL b ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3);

static INTVAL PIO_unix_bind( SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING *l) ) __attribute__nonnull__(3) __attribute__nonnull__(4);

static INTVAL PIO_unix_close( SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io) ) __attribute__nonnull__(3);

static INTVAL PIO_unix_connect( SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NULLOK(STRING *r) ) __attribute__nonnull__(3);

PARROT_WARN_UNUSED_RESULT PARROT_CANNOT_RETURN_NULL static ParrotIO * PIO_unix_fdopen( PARROT_INTERP, SHIM(ParrotIOLayer *layer), PIOHANDLE fd, INTVAL flags ) __attribute__nonnull__(1);

static INTVAL PIO_unix_flush( SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io) ) __attribute__nonnull__(3);

static INTVAL PIO_unix_init( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer) ) __attribute__nonnull__(1) __attribute__nonnull__(2);

static INTVAL PIO_unix_isatty( PIOHANDLE fd ); static INTVAL PIO_unix_listen( SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), INTVAL sec ) __attribute__nonnull__(3);

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static ParrotIO * PIO_unix_open( PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(const char *spath), INTVAL flags ) __attribute__nonnull__(1) __attribute__nonnull__(2) __attribute__nonnull__(3);

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static ParrotIO * PIO_unix_pipe( PARROT_INTERP, SHIM(ParrotIOLayer *l), NOTNULL(const char *cmd), int flags ) __attribute__nonnull__(1) __attribute__nonnull__(3);

static INTVAL PIO_unix_poll( SHIM_INTERP, SHIM(ParrotIOLayer *l), NOTNULL(ParrotIO *io), int which, int sec, int usec ) __attribute__nonnull__(3);

static size_t PIO_unix_read( PARROT_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING **buf) ) __attribute__nonnull__(1) __attribute__nonnull__(3) __attribute__nonnull__(4);

static INTVAL PIO_unix_recv( PARROT_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING **s) ) __attribute__nonnull__(1) __attribute__nonnull__(3) __attribute__nonnull__(4);

static PIOOFF_T PIO_unix_seek( SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), PIOOFF_T offset, INTVAL whence ) __attribute__nonnull__(3);

static INTVAL PIO_unix_send( SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING *s) ) __attribute__nonnull__(3) __attribute__nonnull__(4);

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static ParrotIO * PIO_unix_socket( PARROT_INTERP, SHIM(ParrotIOLayer *layer), int fam, int type, int proto ) __attribute__nonnull__(1);

static PIOOFF_T PIO_unix_tell( SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io) ) __attribute__nonnull__(3);

static size_t PIO_unix_write( SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING *s) ) __attribute__nonnull__(3) __attribute__nonnull__(4);

/* HEADERIZER END: static */

/*

FUNCDOC: flags_to_unix

Returns a UNIX-specific interpretation of flags suitable for passing to open() and fopen() in PIO_unix_open() and PIO_unix_fdopen() respectively.

*/

PARROT_CONST_FUNCTION static INTVAL flags_to_unix(INTVAL flags) { INTVAL oflags = 0;

    if ((flags & (PIO_F_WRITE | PIO_F_READ)) == (PIO_F_WRITE | PIO_F_READ)) {
        oflags |= O_RDWR | O_CREAT;
    }
    else if (flags & PIO_F_WRITE) {
        oflags |= O_WRONLY | O_CREAT;
    }
    else if (flags & PIO_F_READ) {
        oflags |= O_RDONLY;
    }

    if (flags & PIO_F_APPEND) {
        oflags |= O_APPEND;
    }
    else if (flags & PIO_F_TRUNC) {
        oflags |= O_TRUNC;
    }
    return oflags;
}

/*

FUNCDOC: PIO_unix_init

Sets up the interpreter's standard std* IO handles. Returns 0 on success and -1 on error.

*/

static INTVAL PIO_unix_init(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer)) { ParrotIOData * const d = interp->piodata; if (d != NULL && d->table != NULL) { ParrotIO *io;

        io = PIO_unix_fdopen(interp, layer, STDIN_FILENO, PIO_F_READ);
        if (!io)
            return -1;
        _PIO_STDIN(interp) = new_io_pmc(interp, io);

        io = PIO_unix_fdopen(interp, layer, STDOUT_FILENO, PIO_F_WRITE);
        if (!io)
            return -1;
        _PIO_STDOUT(interp) = new_io_pmc(interp, io);

        io = PIO_unix_fdopen(interp, layer, STDERR_FILENO, PIO_F_WRITE);
        if (!io)
            return -1;
        _PIO_STDERR(interp) = new_io_pmc(interp, io);

        return 0;
    }
    return -1;
}

/*

FUNCDOC: PIO_unix_open

Opens *spath. flags is a bitwise or combination of PIO_F_* values.

*/

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static ParrotIO * PIO_unix_open(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(const char *spath), INTVAL flags) { INTVAL oflags; PIOHANDLE fd;

    const INTVAL type = PIO_TYPE_FILE;
    const INTVAL mode = DEFAULT_OPEN_MODE;

    if (flags & PIO_F_PIPE)
        return PIO_unix_pipe(interp, layer, spath, flags);

    if ((flags & (PIO_F_WRITE | PIO_F_READ)) == 0)
        return NULL;

    oflags = flags_to_unix(flags);

    /* Only files for now */
    flags |= PIO_F_FILE;

    /* Try open with no create first */
    while ((fd = open(spath, oflags & (O_WRONLY | O_RDWR | O_APPEND), mode))
           < 0 && errno == EINTR)
        errno = 0;

    /* File open */
    if (fd >= 0) {
        /*
         * Now check if we specified O_CREAT|O_EXCL or not.
         * If so, we must return NULL, else either use the
         * descriptor or create the file.
         */
        if ((oflags & (O_CREAT | O_EXCL)) == (O_CREAT | O_EXCL)) {
            close(fd);
            return NULL;
        }
        /*
         * Check for truncate?
         */
        if (oflags & O_TRUNC) {
            int tfd;
            while ((tfd = creat(spath, PIO_DEFAULTMODE)) < 0 && errno == EINTR)
                errno = 0;
            close(tfd);
        }
    }
    else if (oflags & O_CREAT) {
        /* O_CREAT and file doesn't exist. */
        while ((fd = creat(spath, PIO_DEFAULTMODE)) < 0 && errno == EINTR)
            errno = 0;
        if (!(oflags & O_WRONLY)) {
            close(fd);
            /*
             * File created, reopen with read+write
             */
            while ((fd = open(spath, oflags & (O_WRONLY | O_RDWR),
                              mode)) < 0 && errno == EINTR)
                errno = 0;
        }
    }
    else {
        /* File doesn't exist and O_CREAT not specified */
    }

    if (fd >= 0) {
        /* Set generic flag here if is a terminal then
         * higher layers can know how to setup buffering.
         * STDIN, STDOUT, STDERR would be in this case
         * so we would setup linebuffering.
         */
        ParrotIO *io;
        if (PIO_unix_isatty(fd))
            flags |= PIO_F_CONSOLE;
        io = PIO_new(interp, type, flags, mode);
        io->fd = fd;
        return io;
    }
    return NULL;
}

# if PARROT_ASYNC_DEVEL

/*

FUNCDOC: PIO_unix_async

Experimental asynchronous IO.

This is available if PARROT_ASYNC_DEVEL is defined.

Only works on Linux at the moment.

Toggles the O_ASYNC flag on the IO file descriptor.

*/

INTVAL PIO_unix_async(PARROT_INTERP, NOTNULL(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), INTVAL b) { int rflags; # if defined(linux) if ((rflags = fcntl(io->fd, F_GETFL, 0)) >= 0) { if (b) rflags |= O_ASYNC; else rflags &= ~O_ASYNC; return fcntl(io->fd, F_SETFL, rflags); } # else real_exception(interp, NULL, PIO_NOT_IMPLEMENTED, "Async support not available"); # endif return -1; }

# endif

/*

FUNCDOC: PIO_unix_fdopen

Returns a new ParrotIO with file descriptor fd.

*/

PARROT_WARN_UNUSED_RESULT PARROT_CANNOT_RETURN_NULL static ParrotIO * PIO_unix_fdopen(PARROT_INTERP, SHIM(ParrotIOLayer *layer), PIOHANDLE fd, INTVAL flags) { ParrotIO *io; const INTVAL mode = 0;

# if 0 /* XXX the fcntl fails (-1, errno=0) with * ./parrot -tf - < foo.pir */

    /* FIXME - Check file handle flags, validity */
#    ifdef PARROT_HAS_HEADER_FCNTL
    {
        INTVAL rflags;
        /* Get descriptor flags */
        if ((rflags = fcntl(fd, F_GETFL, 0)) >= 0) {
            UNUSED(rflags);
            /*int accmode = rflags & O_ACCMODE; */
            /* Check other flags (APPEND, ASYNC, etc) */
        }
        else {
            /* Probably invalid descriptor */
            return NULL;
        }
    }
#    endif
#  endif

    if (PIO_unix_isatty(fd))
        flags |= PIO_F_CONSOLE;

    /* fdopened files are always shared */
    flags |= PIO_F_SHARED;

    io = PIO_new(interp, PIO_F_FILE, flags, mode);
    io->fd = fd;
    return io;
}

/*

FUNCDOC: PIO_unix_close

Closes *io's file descriptor.

*/

static INTVAL PIO_unix_close(SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io)) { if (io->fd >= 0) close(io->fd); io->fd = -1; return 0; }

/*

FUNCDOC: PIO_unix_isatty

Returns a boolean value indicating whether fd is a console/tty.

*/

static INTVAL PIO_unix_isatty(PIOHANDLE fd) { return isatty(fd); }

/*

FUNCDOC: PIO_unix_getblksize

Various ways of determining block size.

If passed a file descriptor then fstat() and the stat buffer are used if available.

If called without an argument then the BLKSIZE constant is returned if it was available at compile time, otherwise PIO_BLKSIZE is returned.

*/

INTVAL PIO_unix_getblksize(PIOHANDLE fd) { if (fd >= 0) { /* Try to get the block size of a regular file */ # if 0 /* * Is it even worth adding non-portable code here * or should we just estimate a nice buffer size? * Some systems have st_blksize, some don't. */ { struct stat sbuf; int err; err = fstat(fd, &sbuf); if (err == 0) { return sbuf.st_blksize; } } # endif } /* Try to determine it from general means. */ # ifdef BLKSIZE return BLKSIZE; # else return PIO_BLKSIZE; # endif }

/*

FUNCDOC: PIO_unix_flush

At lowest layer all we can do for flush is to ask the kernel to sync().

XXX: Is it necessary to sync() here?

*/

static INTVAL PIO_unix_flush(SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io)) { return fsync(io->fd); }

/*

FUNCDOC: PIO_unix_read

Calls read() to return up to len bytes in the memory starting at buffer.

*/

static size_t PIO_unix_read(PARROT_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING **buf)) { STRING * const s = PIO_make_io_string(interp, buf, 2048);

    const size_t len = s->bufused;
    void * const buffer = s->strstart;

    for (;;) {
        const int bytes = read(io->fd, buffer, len);
        if (bytes > 0) {
            s->bufused = s->strlen = bytes;
            return bytes;
        }
        else if (bytes < 0) {
            switch (errno) {
            case EINTR:
                continue;
            default:
                s->bufused = s->strlen = 0;
                return bytes;
            }
        }
        else {
            /* Read returned 0, EOF if len requested > 0 */
            if (len > 0)
                io->flags |= PIO_F_EOF;
            s->bufused = s->strlen = 0;
            return bytes;
        }
    }
}

/*

FUNCDOC: PIO_unix_write

Calls write() to write len bytes from the memory starting at buffer to the file descriptor in *io.

*/

static size_t PIO_unix_write(SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING *s)) { int err; size_t bytes; size_t to_write; char * const buffer = s->strstart; const size_t len = s->bufused; const char *ptr = buffer;

    to_write = len;
    bytes = 0;

  write_through:
    while (to_write > 0) {
        if ((err = write(io->fd, ptr, to_write)) >= 0) {
            ptr += err;
            to_write -= err;
            bytes += err;
        }
        else {
            switch (errno) {
            case EINTR:
                goto write_through;
#  ifdef EAGAIN
            case EAGAIN:
                return bytes;
#  endif
            default:
                return (size_t)-1;
            }
        }
    }
    return bytes;
}

/*

FUNCDOC: PIO_unix_seek

Hard seek.

Calls lseek() to advance the read/write position on *io's file descriptor to offset bytes from the location indicated by whence.

*/

static PIOOFF_T PIO_unix_seek(SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), PIOOFF_T offset, INTVAL whence) { const PIOOFF_T pos = lseek(io->fd, offset, whence);

    if (pos >= 0) {
        switch (whence) {
            case SEEK_SET:
                io->fsize = offset > io->fsize ? offset : io->fsize;
                break;
            case SEEK_CUR:
                {
                const PIOOFF_T avail = io->b.next - io->b.startb + offset;
                io->fsize = ( avail > io->fsize) ? avail : io->fsize;
                }
                break;
            case SEEK_END:
            default:
                break;
        }

        io->lpos = io->fpos;
        io->fpos = pos;
    }
    /* Seek clears EOF */
    io->flags &= ~PIO_F_EOF;
    return pos;
}

/*

FUNCDOC: PIO_unix_tell

Returns the current read/write position on *io's file discriptor.

*/

static PIOOFF_T PIO_unix_tell(SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io)) { const PIOOFF_T pos = lseek(io->fd, (PIOOFF_T)0, SEEK_CUR);

    return pos;
}

/*

Networking ^

Define PARROT_NET_DEVEL to enable networking.

These could be native extensions but they probably should be here if we wish to make them integrated with the async IO system.

Very minimal stubs for now, maybe someone will run with these.

FUNCDOC: PIO_sockaddr_in

PIO_sockaddr_in() is not part of the layer and so must be extern.

XXX: We can probably just write our own routines (htons(), inet_aton(), etc.) and take this out of platform specific compilation

*/

PARROT_WARN_UNUSED_RESULT PARROT_CANNOT_RETURN_NULL STRING * PIO_sockaddr_in(PARROT_INTERP, unsigned short port, NOTNULL(STRING *addr)) { struct sockaddr_in sa; /* Hard coded to IPv4 for now */ int family = AF_INET;

    char * const s = string_to_cstring(interp, addr);
    /*
     * due to a bug in OS/X, we've to zero the struct
     * else bind is failing erratically
     */
    memset(&sa, 0, sizeof (sa));
#  ifdef PARROT_DEF_INET_ATON
    if (inet_aton(s, &sa.sin_addr) != 0) {
#  else
    /* positive retval is success */
    if (inet_pton(family, s, &sa.sin_addr) > 0) {
#  endif
        /* Success converting numeric IP */
    }
    else {
        /* Maybe it is a hostname, try to lookup */
        /* XXX Check PIO option before doing a name lookup,
         * it may have been toggled off.
         */
        struct hostent *he = gethostbyname(s);
        /* XXX FIXME - Handle error condition better */
        if (!he) {
            string_cstring_free(s);
            fprintf(stderr, "gethostbyname failure [%s]\n", s);
            return NULL;
        }
        memcpy((char*)&sa.sin_addr, he->h_addr, sizeof (sa.sin_addr));
    }
    string_cstring_free(s);

    sa.sin_family = family;
    sa.sin_port = htons(port);

    return string_make(interp, (char *)&sa, sizeof (struct sockaddr_in),
            "binary", 0);
}

# if PARROT_NET_DEVEL

/*

FUNCDOC: PIO_unix_socket

Uses socket() to create a socket with the specified address family, socket type and protocol number.

*/

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static ParrotIO * PIO_unix_socket(PARROT_INTERP, SHIM(ParrotIOLayer *layer), int fam, int type, int proto) { const int sock = socket(fam, type, proto); if (sock >= 0) { ParrotIO * const io = PIO_new(interp, PIO_F_SOCKET, 0, PIO_F_READ|PIO_F_WRITE); io->fd = sock; memset(&io->local, 0, sizeof (struct sockaddr_in)); memset(&io->remote, 0, sizeof (struct sockaddr_in)); io->remote.sin_family = fam; return io; } return NULL; }

/*

FUNCDOC: PIO_unix_connect

Connects *io's socket to address *r.

*/

static INTVAL PIO_unix_connect(SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NULLOK(STRING *r)) { if (r) { memcpy(&io->remote, PObj_bufstart(r), sizeof (struct sockaddr_in)); } AGAIN: if ((connect(io->fd, (struct sockaddr*)&io->remote, sizeof (struct sockaddr_in))) != 0) { switch (errno) { case EINTR: goto AGAIN; case EINPROGRESS: goto AGAIN; case EISCONN: return 0; default: return -1; } }

    return 0;
}

/*

FUNCDOC: PIO_unix_bind

Binds *io's socket to the local address and port specified by *l.

*/

static INTVAL PIO_unix_bind(SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING *l)) { if (!l) return -1;

    memcpy(&io->local, PObj_bufstart(l), sizeof (struct sockaddr_in));

    if ((bind(io->fd, (struct sockaddr *)&io->local,
                    sizeof (struct sockaddr_in))) == -1) {
        return -1;
    }

    return 0;
}

/*

FUNCDOC: PIO_unix_listen

Listen for new connections. This is only applicable to STREAM or SEQ sockets.

*/

static INTVAL PIO_unix_listen(SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), INTVAL sec) { if ((listen(io->fd, sec)) == -1) { return -1; } return 0; }

/*

FUNCDOC: PIO_unix_accept

Accept a new connection and return a newly created ParrotIO socket.

*/

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static ParrotIO * PIO_unix_accept(PARROT_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io)) { ParrotIO * const newio = PIO_new(interp, PIO_F_SOCKET, 0, PIO_F_READ|PIO_F_WRITE);

    Parrot_Socklen_t addrlen = sizeof (struct sockaddr_in);

    const int newsock = accept(io->fd, (struct sockaddr *)&newio->remote,
                          &addrlen);
    if (newsock == -1 ) {
        mem_sys_free(newio);
        return NULL;
    }

    newio->fd = newsock;

    /* XXX FIXME: Need to do a getsockname and getpeername here to
     * fill in the sockaddr_in structs for local and peer */

    /* Optionally do a gethostyaddr() to resolve remote IP address.
     * This should be based on an option set in the master socket
     */

    return newio;
}

/*

FUNCDOC: PIO_unix_send

Send the message *s to *io's connected socket.

*/

static INTVAL PIO_unix_send(SHIM_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING *s)) { int error, bytes, byteswrote;

    bytes = s->bufused;
    byteswrote = 0;
AGAIN:
    /*
     * Ignore encoding issues for now.
     */
    if ((error = send(io->fd, (char *)s->strstart + byteswrote,
                    bytes, 0)) >= 0) {
        byteswrote += error;
        bytes -= error;
        if (!bytes) {
            return byteswrote;
        }
        goto AGAIN;
    }
    else {
        switch (errno) {
            case EINTR:
                goto AGAIN;
#    ifdef EWOULDBLOCK
            case EWOULDBLOCK:
                goto AGAIN;
#    else
            case EAGAIN:
                goto AGAIN;
#    endif
            case EPIPE:
                /* XXX why close it here and not below */
                close(io->fd);
                return -1;
            default:
                return -1;
        }
    }
}

/*

FUNCDOC: PIO_unix_recv

Receives a message in **s from *io's connected socket.

*/

static INTVAL PIO_unix_recv(PARROT_INTERP, SHIM(ParrotIOLayer *layer), NOTNULL(ParrotIO *io), NOTNULL(STRING **s)) { int error; unsigned int bytesread = 0; char buf[2048];

AGAIN: if ((error = recv(io->fd, buf, 2048, 0)) >= 0) { bytesread += error; /* The charset should probably be 'binary', but right now httpd.imc * only works with 'ascii' */ *s = string_make(interp, buf, bytesread, "ascii", 0); return bytesread; } else { switch (errno) { case EINTR: goto AGAIN; # ifdef EWOULDBLOCK case EWOULDBLOCK: goto AGAIN; # else case EAGAIN: goto AGAIN; # endif case ECONNRESET: /* XXX why close it on err return result is -1 anyway */ close(io->fd); *s = string_make_empty(interp, enum_stringrep_one, 0); return -1; default: close(io->fd); *s = string_make_empty(interp, enum_stringrep_one, 0); return -1; } } }

/*

FUNCDOC: PIO_unix_poll

Utility function for polling a single IO stream with a timeout.

Returns a 1 | 2 | 4 (read, write, error) value.

This is not equivalent to any speficic POSIX or BSD socket call, however it is a useful, common primitive.

Not at all usefule --leo.

Also, a buffering layer above this may choose to reimpliment by checking the read buffer.

*/

static INTVAL PIO_unix_poll(SHIM_INTERP, SHIM(ParrotIOLayer *l), NOTNULL(ParrotIO *io), int which, int sec, int usec) { int n; fd_set r, w, e; struct timeval t;

    t.tv_sec = sec;
    t.tv_usec = usec;
    FD_ZERO(&r); FD_ZERO(&w); FD_ZERO(&e);
    /* These should be defined in header */
    if (which & 1) FD_SET(io->fd, &r);
    if (which & 2) FD_SET(io->fd, &w);
    if (which & 4) FD_SET(io->fd, &e);
AGAIN:
    if ((select(io->fd+1, &r, &w, &e, &t)) >= 0) {
        n = (FD_ISSET(io->fd, &r) ? 1 : 0);
        n |= (FD_ISSET(io->fd, &w) ? 2 : 0);
        n |= (FD_ISSET(io->fd, &e) ? 4 : 0);
        return n;
    }
    else {
        switch (errno) {
            case EINTR:        goto AGAIN;
            default:           return -1;
        }
    }
}

# endif /*

FUNCDOC: PIO_unix_pipe

Very limited exec for now.

XXX: Where does this fit, should it belong in the ParrotIOLayerAPI?

*/

PARROT_WARN_UNUSED_RESULT PARROT_CAN_RETURN_NULL static ParrotIO * PIO_unix_pipe(PARROT_INTERP, SHIM(ParrotIOLayer *l), NOTNULL(const char *cmd), int flags) { /* * pipe(), fork() should be defined, if this header is present * if that's not true, we need a test */ # ifdef PARROT_HAS_HEADER_UNISTD int pid, err, fds[2];

    if ((err = pipe(fds)) < 0) {
        return NULL;
    }

    /* Parent - return IO stream */
    if ((pid = fork()) > 0) {
        ParrotIO * const io =
            PIO_new(interp, PIO_F_PIPE, 0, flags & (PIO_F_READ|PIO_F_WRITE));
        if (flags & PIO_F_READ) {
            /* close this writer's end of pipe */
            close(fds[1]);
            io->fd = fds[0];
            io->fd2 = 0;
        }
        else {  /* assume write only for now */
            /* close this reader's end */
            close(fds[0]);
            io->fd = fds[1];
            io->fd2 = 0;
        }
        return io;
    }

    /* Child - exec process */
    if (pid == 0) {
        char *argv[10], *p, *c;
        int n;

        if (flags & PIO_F_WRITE) {
            /* the other end is writing - we read from the pipe */
            close(STDIN_FILENO);
            close(fds[1]);
            if (dup(fds[0]) != STDIN_FILENO) {
                exit(EXIT_SUCCESS);
            }
        }
        else {
            /* XXX redirect stdout, stderr to pipe */
            close(STDIN_FILENO);
            close(STDOUT_FILENO);
            close(STDERR_FILENO);
            if (dup(fds[0]) != STDIN_FILENO || dup(fds[1]) != STDOUT_FILENO
                    || dup(fds[1]) != STDERR_FILENO)
            {
                exit(EXIT_SUCCESS);
            }
        }
        /*
         * XXX ugly hack to be able to pass some arguments
         *     split cmd at blanks
         */
        c = strdup(cmd);
        for (n = 0, p = strtok(c, " "); n < 9 && p; p = strtok(NULL, " ")) {
            if (n == 0)
                cmd = p;
            argv[n++] = p;
        }
        argv[n] = NULL;
        execv(cmd, argv);       /* XXX use execvp ? */
        /* Will never reach this unless exec fails. */
        perror("execvp");
        exit(EXIT_FAILURE);
    }

    perror("fork");
#  else
    UNUSED(l);
    UNUSED(cmd);
    UNUSED(flags);
    real_exception(interp, NULL, UNIMPLEMENTED, "pipe() unimplemented");
#  endif
    return NULL;
}

const ParrotIOLayerAPI pio_unix_layer_api = { PIO_unix_init, PIO_base_new_layer, PIO_base_delete_layer, PIO_null_push_layer, PIO_null_pop_layer, PIO_unix_open, PIO_null_open2, PIO_null_open3, PIO_null_open_async, PIO_unix_fdopen, PIO_unix_close, PIO_unix_write, PIO_null_write_async, PIO_unix_read, PIO_null_read_async, PIO_unix_flush, NULL, /* no peek */ PIO_unix_seek, PIO_unix_tell, PIO_null_setbuf, PIO_null_setlinebuf, PIO_null_getcount, PIO_null_fill, PIO_null_eof, # if PARROT_NET_DEVEL PIO_unix_poll, PIO_unix_socket, PIO_unix_connect, PIO_unix_send, PIO_unix_recv, PIO_unix_bind, PIO_unix_listen, PIO_unix_accept # else NULL, /* no poll */ NULL, /* no socket */ NULL, /* no connect */ NULL, /* no send */ NULL, /* no recv */ NULL, /* no bind */ NULL, /* no listen */ NULL /* no accept */ # endif };

#endif /* PIO_OS_UNIX */

/*

SEE ALSO ^

src/io/io_buf.c, src/io/io_passdown.c, src/io/io_stdio.c, src/io/io_unix.c, src/io/io_win32.c, src/io/io.c, src/io/io_private.h.

HISTORY ^

Initially written by Melvin Smith (mrjoltcola@mindspring.com).

*/

/* * Local variables: * c-file-style: "parrot" * End: * vim: expandtab shiftwidth=4: */


parrot