예제 #1
0
파일: impl.c 프로젝트: Hochikong/huptime
static int
do_dup(int fd)
{
    int rval = -1;
    fdinfo_t *info = NULL;

    if( fd < 0 )
    {
        errno = EINVAL;
        return -1;
    }

    DEBUG("do_dup(%d, ...) ...", fd);
    L();
    info = fd_lookup(fd);
    if( info == NULL )
    {
        U();
        rval = libc.dup(fd);
        DEBUG("do_dup(%d) => %d (no info)", fd, rval);
        return rval;
    }

    rval = libc.dup(fd);
    if( rval >= 0 )
    {
        inc_ref(info);
        fd_save(rval, info);
    }

    U();
    DEBUG("do_dup(%d) => %d (with info)", fd, rval);
    return rval;
}
예제 #2
0
파일: impl.c 프로젝트: Hochikong/huptime
static int
do_epoll_create1(int flags)
{
    int rval = libc.epoll_create1(flags);
    if( rval >= 0 )
    {
        fdinfo_t* info = alloc_info(EPOLL);
        fd_save(rval, info);
    }
    return rval;
}
예제 #3
0
파일: impl.c 프로젝트: Hochikong/huptime
static int
do_dup3(int fd, int fd2, int flags)
{
    int rval = -1;
    fdinfo_t *info = NULL;
    fdinfo_t *info2 = NULL;

    if( fd < 0 || fd2 < 0 )
    {
        errno = EINVAL;
        return -1;
    }

    DEBUG("do_dup3(%d, %d, ...) ...", fd, fd2);
    L();
    if( fd == fd2 )
    {
        U();
        DEBUG("do_dup3(%d, %d, ...) => 0", fd, fd2);
        return fd2;
    }

    info = fd_lookup(fd);
    info2 = fd_lookup(fd2);
    if( info2 != NULL )
    {
        rval = info_close(fd2, info2);
        if( rval < 0 )
        {
            U();
            DEBUG("do_dup3(%d, %d, ...) => %d (close failed)", fd, fd2, rval);
            return rval;
        }
    }

    rval = libc.dup3(fd, fd2, flags);
    if( rval < 0 )
    {
        U();
        DEBUG("do_dup3(%d, %d, ...) => %d (dup3 failed)", fd, fd2, rval);
        return rval;
    }

    if( info != NULL )
    {
        inc_ref(info);
        fd_save(fd2, info);
    }

    U();
    DEBUG("do_dup3(%d, %d, ...) => %d", fd, fd2, rval);
    return rval;
}
예제 #4
0
파일: fakehost.c 프로젝트: AKuHAK/ps2sdk
/** Handle open request.
 * @ingroup fakehost
 *
 * @param f     Pointer to io_device structure.
 * @param name  pathname.
 * @param mode  open mode.
 * @return Status (as for fileio open).
 */
int fd_open( iop_io_file_t *f, const char *name, int mode)
{
	char nameBuffer[ 250 ];
	int fd;

#ifdef DEBUG_T
	printf( "fakehost: open %i %s %s\n", f->unit, name ,base);
#endif
	fd = open( fd_name( nameBuffer, name), mode, 0 );
	if ( fd < 0 ) return fd;

	return fd_save( fd, f );
}
예제 #5
0
파일: impl.c 프로젝트: Hochikong/huptime
static int
impl_dummy_server(void)
{
    int dummy_server = -1;

    /* Create our dummy sock. */
    struct sockaddr_un dummy_addr;
    char *socket_path = tempnam("/tmp", ".huptime");

    memset(&dummy_addr, 0, sizeof(struct sockaddr_un));
    dummy_addr.sun_family = AF_UNIX;
    strncpy(dummy_addr.sun_path, socket_path, sizeof(dummy_addr.sun_path)-1);

    /* Create a dummy server. */
    dummy_server = socket(AF_UNIX, SOCK_STREAM, 0);
    if( dummy_server < 0 )
    {
        fprintf(stderr, "Unable to create unix socket?");
        return -1;
    }
    if( fcntl(dummy_server, F_SETFD, FD_CLOEXEC) < 0 )
    {
        close(dummy_server);
        fprintf(stderr, "Unable to set cloexec?");
        return -1;
    }
    if( libc.bind(
            dummy_server,
            (struct sockaddr*)&dummy_addr,
            sizeof(struct sockaddr_un)) < 0 )
    {
        close(dummy_server);
        fprintf(stderr, "Unable to bind unix socket?");
        return -1;
    }
    if( libc.listen(dummy_server, 1) < 0 )
    {
        close(dummy_server);
        fprintf(stderr, "Unable to listen on unix socket?");
        return -1;
    }

    /* Connect a dummy client. */
    int dummy_client = socket(AF_UNIX, SOCK_STREAM, 0);
    if( dummy_client < 0 )
    {
        close(dummy_server);
        fprintf(stderr, "Unable to create unix socket?");
        return -1;
    }
    if( fcntl(dummy_client, F_SETFD, FD_CLOEXEC) < 0 )
    {
        close(dummy_server);
        close(dummy_client);
        fprintf(stderr, "Unable to set cloexec?");
        return -1;
    }
    if( connect(
            dummy_client,
            (struct sockaddr*)&dummy_addr,
            sizeof(struct sockaddr_un)) < 0 )
    {
        close(dummy_server);
        close(dummy_client);
        fprintf(stderr, "Unable to connect dummy client?");
        return -1;
    }

    /* Put the client into an error state. */
    int dummy_fd = libc.accept(dummy_server, NULL, 0);
    if( dummy_fd < 0 )
    {
        fprintf(stderr, "Unable to accept internal client?");
        close(dummy_server);
        close(dummy_client);
        return -1;
    }
    close(dummy_fd);

    /* Save the dummy info. */
    fdinfo_t* dummy_info = alloc_info(DUMMY);
    if( dummy_info == NULL )
    {
        fprintf(stderr, "Unable to allocate dummy info?");
        return -1;
    }
    dummy_info->dummy.client = dummy_client;
    fd_save(dummy_server, dummy_info);
    inc_ref(dummy_info);
    fd_save(dummy_client, dummy_info);

    /* Ensure that it's unlinked. */
    unlink(socket_path);
    free(socket_path);

    return dummy_server;
}
예제 #6
0
파일: impl.c 프로젝트: Hochikong/huptime
void
impl_init(void)
{
    const char* mode_env = getenv("HUPTIME_MODE");
    const char* multi_env = getenv("HUPTIME_MULTI");
    const char* revive_env = getenv("HUPTIME_REVIVE");
    const char* debug_env = getenv("HUPTIME_DEBUG");
    const char* pipe_env = getenv("HUPTIME_PIPE");
    const char* wait_env = getenv("HUPTIME_WAIT");

    if( debug_env != NULL && strlen(debug_env) > 0 )
    {
        debug_enabled = !strcasecmp(debug_env, "true") ? TRUE: FALSE;
    }

    DEBUG("Initializing...");

    /* Initialize our lock. */
    impl_init_lock();

    /* Save this pid as our master pid.
     * This is done to handle processes that use
     * process pools. We remember the master pid and
     * will do the full fork()/exec() only when we are
     * the master. Otherwise, we will simply shutdown
     * gracefully, and all the master to restart. */
    master_pid = getpid();

    /* Grab our exit strategy. */
    if( mode_env != NULL && strlen(mode_env) > 0 )
    {
        if( !strcasecmp(mode_env, "fork") )
        {
            exit_strategy = FORK;
            DEBUG("Exit strategy is fork.");
        }
        else if( !strcasecmp(mode_env, "exec") )
        {
            exit_strategy = EXEC;
            DEBUG("Exit strategy is exec.");
        }
        else
        {
            fprintf(stderr, "Unknown exit strategy.");
            libc.exit(1);
        }
    }

    /* Check if we have something to unlink. */
    to_unlink = getenv("HUPTIME_UNLINK");
    if( to_unlink != NULL && strlen(to_unlink) > 0 )
    {
        DEBUG("Unlink is '%s'.", to_unlink);
    }

    /* Clear up any outstanding child processes.
     * Because we may have exited before the process
     * could do appropriate waitpid()'s, we try to
     * clean up children here. Note that we may have
     * some zombies that hang around during the life
     * of the program, but at every restart they will
     * be cleaned up (so at least they won't grow
     * without bound). */
    int status = 0;
    while( waitpid((pid_t)-1, &status, WNOHANG) > 0 );

    /* Check if we're in multi mode. */
    if( multi_env != NULL && strlen(multi_env) > 0 )
    {
        multi_mode = !strcasecmp(multi_env, "true") ? TRUE: FALSE;
    }
#ifndef SO_REUSEPORT
    if( multi_mode == TRUE )
    {
        fprintf(stderr, "WARNING: Multi mode not supported.\n");
        fprintf(stderr, "(Requires at least Linux 3.9 and recent headers).\n");
    } 
#endif

    /* Check if we're in revive mode. */
    if( revive_env != NULL && strlen(revive_env) > 0 )
    {
        revive_mode = !strcasecmp(revive_env, "true") ? TRUE : FALSE;
    }

    /* Check if we are in wait mode. */
    if( wait_env != NULL && strlen(wait_env) > 0 )
    {
        wait_mode = !strcasecmp(wait_env, "true") ? TRUE : FALSE;
    }

    /* Check if we're a respawn. */
    if( pipe_env != NULL && strlen(pipe_env) > 0 )
    {
        int fd = -1;
        fdinfo_t *info = NULL;
        int pipefd = strtol(pipe_env, NULL, 10);

        DEBUG("Loading all file descriptors.");

        /* Decode all passed information. */
        while( !info_decode(pipefd, &fd, &info) )
        {
            fd_save(fd, info);
            DEBUG("Decoded fd %d (type %d).", fd, info->type);
            info = NULL;
        }
        if( info != NULL )
        {
            dec_ref(info);
        }

        /* Finished with the pipe. */
        libc.close(pipefd);
        unsetenv("HUPTIME_PIPE");
        DEBUG("Finished decoding.");

        /* Close all non-encoded descriptors. */
        for( fd = 0; fd < fd_max(); fd += 1 )
        {
            info = fd_lookup(fd);
            if( info == NULL )
            {
                DEBUG("Closing fd %d.", fd);
                libc.close(fd);
            }
        }

        /* Restore all given file descriptors. */
        for( fd = 0; fd < fd_limit(); fd += 1 )
        {
            info = fd_lookup(fd);
            if( info != NULL && info->type == SAVED )
            {
                fdinfo_t *orig_info = fd_lookup(info->saved.fd);
                if( orig_info != NULL )
                {
                    /* Uh-oh, conflict. Move the original (best effort). */
                    do_dup(info->saved.fd);
                    do_close(info->saved.fd);
                }

                /* Return the offset (ignore failure). */
                if( info->saved.offset != (off_t)-1 )
                {
                    lseek(fd, info->saved.offset, SEEK_SET);
                }

                /* Move the SAVED fd back. */
                libc.dup2(fd, info->saved.fd);
                DEBUG("Restored fd %d.", info->saved.fd);
            }
        }
    }
    else
    {
        DEBUG("Saving all initial file descriptors.");

        /* Save all of our initial files. These are used
         * for re-execing the process. These are persisted
         * effectively forever, and on restarts we close
         * everything that is not a BOUND socket or a SAVED
         * file descriptor. */
        for( int fd = 0; fd < fd_max(); fd += 1 )
        {
            fdinfo_t *info = fd_lookup(fd);
            if( info != NULL )
            {
                /* Encoded earlier. */
                continue;
            }

            /* Make a new SAVED FD. */
            int newfd = libc.dup(fd);
            if( newfd >= 0 )
            {
                fdinfo_t *saved_info = alloc_info(SAVED);

                if( saved_info != NULL )
                {
                    saved_info->saved.fd = fd;
                    saved_info->saved.offset = lseek(fd, 0, SEEK_CUR);
                    fd_save(newfd, saved_info);
                    DEBUG("Saved fd %d (offset %lld).",
                        fd, (long long int)saved_info->saved.offset);
                }
            }
        }
    }

    /* Save the environment.
     *
     * NOTE: We reserve extra space in the environment
     * for our special start-up parameters, which will be added
     * in impl_exec() below. (The encoded BOUND/SAVED sockets).
     *
     * We also filter out the special variables above that were
     * used to pass in information about sockets that were bound. */
    free(environ_copy);
    environ_copy = (char**)read_nul_sep("/proc/self/environ");
    DEBUG("Saved environment.");

    /* Save the arguments. */
    free(args_copy);
    args_copy = (char**)read_nul_sep("/proc/self/cmdline");
    DEBUG("Saved args.");
    for( int i = 0; args_copy[i] != NULL; i += 1 )
    {
        DEBUG(" arg%d=%s", i, args_copy[i]);
    }

    /* Save the cwd & exe. */
    free(cwd_copy);
    cwd_copy = (char*)read_link("/proc/self/cwd");
    DEBUG("Saved cwd.");
    free(exe_copy);
    exe_copy = (char*)read_link("/proc/self/exe");
    DEBUG("Saved exe.");

    /* Install our signal handlers. */
    impl_install_sighandlers();

    /* Initialize our thread. */
    impl_init_thread();

    /* Unblock our signals.
     * Note that we have specifically masked the
     * signals prior to the exec() below, to cover
     * the race between program start and having
     * installed the appropriate handlers. */
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGHUP);
    sigprocmask(SIG_UNBLOCK, &set, NULL);

    /* Done. */
    DEBUG("Initialization complete.");
}
예제 #7
0
파일: impl.c 프로젝트: Hochikong/huptime
static int
do_accept4(int sockfd, struct sockaddr *addr, socklen_t *addrlen, int flags)
{
    int rval = -1;
    fdinfo_t *info = NULL;

    if( sockfd < 0 )
    {
        errno = EINVAL;
        return -1;
    }

    DEBUG("do_accept4(%d, ...) ...", sockfd);
    L();
    info = fd_lookup(sockfd);
    if( info == NULL || (info->type != BOUND && info->type != DUMMY) )
    {
        U();
        /* Should return an error. */
        rval = libc.accept4(sockfd, addr, addrlen, flags);
        DEBUG("do_accept4(%d, ...) => %d (no info)", sockfd, rval);
        return rval;
    }

    /* Check that they've called listen. */
    if( info->type == BOUND && !info->bound.stub_listened )
    {
        U();
        DEBUG("do_accept4(%d, ...) => -1 (not listened)", sockfd);
        errno = EINVAL;
        return -1;
    }

    /* Check if this is a dummy.
     * There's no way that they should be calling accept().
     * The dummy FD will never trigger a poll, select, epoll,
     * etc. So we just act as a socket with no clients does --
     * either return immediately or block forever. NOTE: We
     * still return in case of EINTR or other suitable errors. */
    if( info->type == DUMMY && info->dummy.client >= 0 )
    {
        rval = info->dummy.client;
        info->dummy.client = -1;
        U();
        DEBUG("do_accept4(%d, ...) => %d (dummy client)", sockfd, rval);
        return rval;
    }

    U();

    if( !(flags & SOCK_NONBLOCK) )
    {
        /* Wait for activity on the socket. */
        struct pollfd poll_info;
        poll_info.fd = sockfd;
        poll_info.events = POLLIN;
        poll_info.revents = 0;
        if( poll(&poll_info, 1, -1) < 0 )
        {
            return -1;
        }
    }

    L();

    /* Check our status. */
    if( is_exiting == TRUE )
    {
        /* We've transitioned from not exiting
         * to exiting in this period. This will
         * circle around a return a dummy descriptor. */
        U();
        DEBUG("do_accept4(%d, ...) => -1 (interrupted)", sockfd);
        errno = flags & SOCK_NONBLOCK ? EAGAIN : EINTR;
        return -1;
    }

    /* Do the accept for real. */
    fdinfo_t *new_info = alloc_info(TRACKED);
    if( new_info == NULL )
    {
        U();
        DEBUG("do_accept4(%d, ...) => -1 (alloc error?)", sockfd);
        return -1;
    }
    inc_ref(info);
    new_info->tracked.bound = info;
    rval = libc.accept4(sockfd, addr, addrlen, flags);

    if( rval >= 0 )
    {
        /* Save the reference to the socket. */
        fd_save(rval, new_info);
    }
    else
    {
        /* An error occured, nothing to track. */
        dec_ref(new_info);
    }

    U();
    DEBUG("do_accept4(%d, ...) => %d (tracked %d) %s",
        sockfd, rval, total_tracked,
        rval == -1 ? strerror(errno) : "");
    return rval;
}
예제 #8
0
파일: impl.c 프로젝트: Hochikong/huptime
static int
do_bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)
{
    fdinfo_t *info = NULL;
    int rval = -1;

    if( sockfd < 0 )
    {
        errno = EINVAL;
        return -1;
    }

    /* At this point, we can reasonably assume
     * the program has started up and has installed
     * whatever signal handlers it wants. We check
     * that our own signal handler is installed.
     * If the user doesn't want us to override the
     * built-in signal handlers, they shouldn't use
     * huptime. */
    impl_install_sighandlers();

    DEBUG("do_bind(%d, ...) ...", sockfd);
    L();

    /* See if this socket already exists. */
    for( int fd = 0; fd < fd_limit(); fd += 1 )
    {
        fdinfo_t *info = fd_lookup(fd);
        if( info != NULL && 
            info->type == BOUND &&
            info->bound.addrlen == addrlen &&
            !memcmp(addr, (void*)info->bound.addr, addrlen) )
        {
            DEBUG("Found ghost %d, cloning...", fd);

            /* Give back a duplicate of this one. */
            int rval = do_dup2(fd, sockfd);
            if( rval < 0 )
            {
                /* Dup2 failed? */
                DEBUG("Failed.");
                continue;
            }
            if( info->bound.is_ghost )
            {
                /* Close the original (not needed). */
                info->bound.is_ghost = 0;
                do_close(fd);
            }

            /* Success. */
            U();
            DEBUG("do_bind(%d, ...) => 0 (ghosted)", sockfd);
            return 0;
        }
    }

#ifdef SO_REUSEPORT
    /* Multi mode? Set socket options. */
    if( multi_mode == TRUE )
    {
        int optval = 1;
        if( setsockopt(sockfd,
                       SOL_SOCKET,
                       SO_REUSEPORT,
                       &optval,
                       sizeof(optval)) < 0 )
        {
            U();
            DEBUG("do_bind(%d, ...) => -1 (no multi?)", sockfd);
            return -1;
        }

        DEBUG("Multi mode enabled.");
    }
#endif

    /* Try a real bind. */
    info = alloc_info(BOUND);
    if( info == NULL )
    {
        U();
        DEBUG("do_bind(%d, ...) => -1 (alloc error?)", sockfd);
        return -1;
    }
    rval = libc.bind(sockfd, addr, addrlen);
    if( rval < 0 )
    {
        dec_ref(info);
        U();
        DEBUG("do_bind(%d, ...) => %d (error)", sockfd, rval);
        return rval;
    }

    /* Ensure that this socket is non-blocking,
     * this is because we override the behavior
     * for accept() and we require non-blocking
     * behavior. We deal with the consequences. */
    rval = fcntl(sockfd, F_SETFL, O_NONBLOCK);
    if( rval < 0 )
    {
        dec_ref(info);
        U();
        DEBUG("do_bind(%d, ...) => %d (fcntl error)", sockfd, rval);
        return -1;
    }

    /* Save a refresh bound socket info. */
    info->bound.stub_listened = 0;
    info->bound.real_listened = 0;
    info->bound.addr = (struct sockaddr*)malloc(addrlen);
    info->bound.addrlen = addrlen;
    memcpy((void*)info->bound.addr, (void*)addr, addrlen);
    fd_save(sockfd, info);

    /* Success. */
    U();
    DEBUG("do_bind(%d, ...) => %d", sockfd, rval);
    return rval;
}