Exemple #1
0
void mx_recv_client_request(mx_connection_t *conn)
{
    int rsize, rbytes;

    rsize = conn->recvend - conn->recvlast;
    if (rsize == 0) {
        mx_write_log(mx_log_error, "Command line too long, connection fd(%d)", conn->fd);
        mx_free_connection(conn);
        return;
    }
    
    rbytes = read(conn->fd, conn->recvlast, rsize);
    if (rbytes == -1) {
        if (errno != EAGAIN && errno != EWOULDBLOCK) {
            mx_write_log(mx_log_debug, "Failed to read, and not due to blocking");
            mx_free_connection(conn);
            return;
        }
    } else if (rbytes == 0) {
        mx_free_connection(conn);
        return;
    }
    
    conn->recvlast += rbytes;
    mx_process_command(conn);
    
    return;
}
Exemple #2
0
static int mx_do_bgsave_queue()
{
    char tbuf[2048];

    /* database filename */
    sprintf(tbuf, "%s.%d", mx_global->bgsave_filepath, getpid());

    mx_dbfp = fopen(tbuf, "wb");
    if (!mx_dbfp) {
        mx_write_log(mx_log_error, "failed to open bgsave tempfile");
        return -1;
    }

    if (fwrite(MX_BGSAVE_HEADER, sizeof(MX_BGSAVE_HEADER) - 1, 1, mx_dbfp) != 1) {
        goto failed;
    }

    /* save ready queues */
    if (hash_foreach(mx_global->queue_table, mx_save_ready_queue) != 0 ||
        mx_save_delay_queue() != 0 || mx_save_recycle_queue() != 0)
    {
        goto failed;
    }

    /* save delay queue */
    if (fwrite(&mx_null_header, sizeof(mx_null_header), 1, mx_dbfp) != 1) {
        goto failed;
    }

    fflush(mx_dbfp);
    fsync(fileno(mx_dbfp));
    fclose(mx_dbfp);
    mx_dbfp = NULL;
    
    if (rename(tbuf, mx_global->bgsave_filepath) == -1) {
        mx_write_log(mx_log_error, "failed to rename tempfile, message(%s)", strerror(errno));
        unlink(tbuf);
        return -1;
    }
    
    return 0;

failed:
    mx_write_log(mx_log_error, "failed to write data to bgsave tempfile, message(%s)", strerror(errno));
    fclose(mx_dbfp);
    mx_dbfp = NULL;
    return -1;
}
Exemple #3
0
void mx_recv_client_body(mx_connection_t *conn)
{
    int rcount;

    rcount = read(conn->fd, conn->itemptr, conn->itembytes);
    if (rcount == -1) {
        if (errno != EAGAIN && errno != EWOULDBLOCK) {
            mx_write_log(mx_log_debug, "Failed to read, and not due to blocking");
            mx_free_connection(conn);
            return;
        }
    } else if (rcount == 0) {
        mx_free_connection(conn);
        return;
    }
    
    conn->itemptr += rcount;
    conn->itembytes -= rcount;
    
    if (conn->itembytes <= 0) {
        mx_finish_recv_body(conn);
        mx_send_reply_return(conn, "+OK");
    }
    return;
}
Exemple #4
0
void mx_send_client_response(mx_connection_t *conn)
{
    int wcount, wsize = conn->sendlast - conn->sendpos;

    wcount = write(conn->fd, conn->sendpos, wsize);
    if (wcount == -1) {
        if (errno != EAGAIN && errno != EWOULDBLOCK) {
            mx_write_log(mx_log_error, "Failed to read, and not due to blocking");
            mx_free_connection(conn);
        }
        return;
    } else if (wcount == 0) {
        mx_free_connection(conn);
        return;
    }
    
    conn->sendpos += wcount;
    
    if (conn->sendpos >= conn->sendlast) { /* finish */
        conn->sendpos = conn->sendbuf;
        conn->sendlast = conn->sendbuf;
        conn->state = mx_event_reading;
        conn->rev_handler = mx_recv_client_request;
        conn->wev_handler = NULL;
        aeDeleteFileEvent(mx_daemon->event, conn->fd, AE_WRITABLE);
    }
}
Exemple #5
0
static int mx_bgsave_queues()
{
    pid_t pid;
    
    /* bgsave working || no dirty data */
    if (mx_global->bgsave_pid != -1 ||
        mx_global->dirty <= 0)
    {
        return 0;
    }

    pid = fork();
    switch (pid) {
    case -1:
        mx_write_log(mx_log_error, "can not fork process to do background save");
        return -1;
    case 0:
        if (mx_do_bgsave_queue() != 0)
            exit(-1);
        exit(0);
    default: /* parent */
        mx_global->bgsave_pid = pid; /* save the background save process ID */
        mx_global->dirty = 0; /* clean dirty */
        break;
    }

    return 0;
}
Exemple #6
0
int mx_try_bgsave_queues()
{
    if (!mx_global->bgsave_enable) { /* background save queue feature disable */
        return 0;
    }

    if (mx_global->bgsave_pid != -1) { /* background save doing now */
        int statloc;
        pid_t pid;

        if ((pid = wait3(&statloc, WNOHANG, NULL)) != 0) { /* noblock waiting */
            int exitcode = WEXITSTATUS(statloc);
            int bysignal = WIFSIGNALED(statloc);
            char tbuf[2048];

            if (!bysignal && exitcode == 0) {
                mx_write_log(mx_log_debug, "background saving terminated with success");
                mx_global->last_bgsave_time = mx_current_time;

            } else if (!bysignal && exitcode != 0) {
                mx_write_log(mx_log_notice, "background saving failed");

            } else {
                mx_write_log(mx_log_notice, "background saving terminated by signal");
                sprintf(tbuf, "%s.%d", mx_global->bgsave_filepath, mx_global->bgsave_pid);
                unlink(tbuf);
            }

            mx_global->bgsave_pid = -1;
        }

    } else {
        if (((mx_current_time - mx_global->last_bgsave_time) > mx_global->bgsave_times && 
             mx_global->dirty > 0) || mx_global->dirty >= mx_global->bgsave_changes)
        {
            mx_write_log(mx_log_debug, "background save queue starting");
            return mx_bgsave_queues();
        }
    }

    return 0;
}
Exemple #7
0
void mx_process_handler(aeEventLoop *eventLoop, int fd, void *data, int mask)
{
    mx_connection_t *conn = (mx_connection_t *)data;
    
    if (conn->flag & MX_CONNECTION_WATCHER) {
        mx_write_log(mx_log_notice, "current client was blocked but event occur, event(%s)",
            (mask == AE_READABLE ? "read" : "write"));
        return;
    }
    
    if (conn->fd != fd ||
       (mask == AE_READABLE && conn->state != mx_event_reading) ||
       (mask == AE_WRITABLE && conn->state != mx_event_writing)) {
        if (conn->fd != fd) {
            mx_write_log(mx_log_notice, "current client socket invalid fd(%d)", conn->fd);
        } else {
            int wr = (conn->state == mx_event_reading);
            mx_write_log(mx_log_notice,
                   "current event invalid, client want [%s] but [%s]",
                   (wr ? "read" : "write"), (wr ? "write" : "read"));
        }
        return;
    }
    
    if (conn->state == mx_event_reading) {
        if (conn->rev_handler) {
            conn->rev_handler(conn);
        } else {
            mx_write_log(mx_log_notice, "not found reading event handler fd(%d)", conn->fd);
        }
    } else {
        if (conn->wev_handler) {
            conn->wev_handler(conn);
        } else {
            mx_write_log(mx_log_notice, "not found writing event handler fd(%d)", conn->fd);
        }
    }
    return;
}
Exemple #8
0
void mx_accept_connection(aeEventLoop *eventLoop, int fd, void *data, int mask)
{
    int sock, flags = 1;
    socklen_t addrlen;
    struct sockaddr addr;
    
    addrlen = sizeof(addr);
    if ((sock = accept(fd, &addr, &addrlen)) == -1) {
        if (errno != EAGAIN && errno != EWOULDBLOCK)
            mx_write_log(mx_log_notice, "Unable accept client connection");
        return;
    }
    
    if (mx_set_nonblocking(sock) == -1) {
        mx_write_log(mx_log_notice, "Unable set socket to non-blocking");
        close(sock);
        return;
    }
    
    if (!mx_create_connection(sock)) {
        mx_write_log(mx_log_notice, "Unable create client connection object");
    }
    return;
}
Exemple #9
0
mx_connection_t *mx_create_connection(int fd)
{
    mx_connection_t *conn;

    if (mx_daemon->free_connections_count > 0) {
        conn = mx_daemon->free_connections;
        mx_daemon->free_connections = conn->free_next;
        mx_daemon->free_connections_count--;
    } else {
        conn = malloc(sizeof(*conn) + MX_RECVBUF_SIZE + MX_SENDBUF_SIZE);
        if (!conn) {
            return NULL;
        }
        
        conn->recvbuf = (char *)conn + sizeof(*conn);
        conn->recvend = conn->recvbuf + MX_RECVBUF_SIZE;
        conn->sendbuf = conn->recvend;
        conn->sendend = conn->sendbuf + MX_SENDBUF_SIZE;
    }

    conn->recvpos = conn->recvbuf;
    conn->recvlast = conn->recvbuf;
    conn->sendpos = conn->sendbuf;
    conn->sendlast = conn->sendbuf;

    conn->fd = fd;
    conn->flag = 0;
    conn->state = mx_event_reading;
    conn->rev_handler = mx_recv_client_request;
    conn->wev_handler = NULL;
    conn->item = NULL;
    conn->itemptr = NULL;
    conn->itembytes = 0;
    conn->sent_recycle = 0;
    conn->recycle_id = -1;

    if (aeCreateFileEvent(mx_daemon->event, conn->fd, AE_READABLE, mx_process_handler, conn) == -1) {
        mx_write_log(mx_log_notice, "Unable create file event, client fd(%d)", conn->fd);
        mx_free_connection(conn);
        return NULL;
    }

    return conn;
}
Exemple #10
0
void mx_init_daemon()
{
    struct linger ling = {0, 0};
    struct sockaddr_in addr;
    int flags = 1;

    mx_daemon->log_fd = fopen(mx_daemon->log_file, "a+");
    if (!mx_daemon->log_file) {
        fprintf(stderr, "[failed] failed to open log file\n");
        exit(-1);
    }

    mx_daemon->fd = socket(AF_INET, SOCK_STREAM, 0);
    if (mx_daemon->fd == -1) {
        mx_write_log(mx_log_error, "Unable create listening server socket");
        exit(-1);
    }

    if (mx_set_nonblocking(mx_daemon->fd) == -1) {
        mx_write_log(mx_log_error, "Unable set socket to non-blocking");
        exit(-1);
    }

    setsockopt(mx_daemon->fd, SOL_SOCKET, SO_REUSEADDR, &flags, sizeof(flags));
    setsockopt(mx_daemon->fd, SOL_SOCKET, SO_KEEPALIVE, &flags, sizeof(flags));
    setsockopt(mx_daemon->fd, SOL_SOCKET, SO_LINGER, &ling, sizeof(ling));
#if !defined(TCP_NOPUSH)
    setsockopt(mx_daemon->fd, IPPROTO_TCP, TCP_NODELAY, &flags, sizeof(flags));
#endif

    addr.sin_family = AF_INET;
    addr.sin_port = htons(mx_daemon->port);
    addr.sin_addr.s_addr = htonl(INADDR_ANY);

    if (bind(mx_daemon->fd, (struct sockaddr *) &addr, sizeof(addr)) == -1) {
        mx_write_log(mx_log_error, "Unable bind socket");
        close(mx_daemon->fd);
        exit(-1);
    }

    if (listen(mx_daemon->fd, 1024) == -1) {
        mx_write_log(mx_log_error, "Unable listen socket");
        close(mx_daemon->fd);
        exit(-1);
    }

    mx_daemon->event = aeCreateEventLoop();
    if (!mx_daemon->event) {
        mx_write_log(mx_log_error, "Unable create EventLoop");
        exit(-1);
    }

    mx_daemon->table = hash_alloc(32);
    if (!mx_daemon->table) {
        mx_write_log(mx_log_error, "Unable create HashTable");
        exit(-1);
    }

    mx_daemon->delay_queue = mx_queue_create("__delay__", sizeof("__delay__") - 1);
    if (!mx_daemon->table) {
        mx_write_log(mx_log_error, "Unable create delay queue");
        exit(-1);
    }
    
    mx_daemon->recycle = mx_recycle_create();
    if (!mx_daemon->recycle) {
        mx_write_log(mx_log_error, "Unable create recycle");
        exit(-1);
    }

    if (aeCreateFileEvent(mx_daemon->event, mx_daemon->fd,
            AE_READABLE, mx_accept_connection, NULL) == -1) {
        mx_write_log(mx_log_error, "Unable create accpet file event");
        exit(-1);
    }

    aeCreateTimeEvent(mx_daemon->event, 1, mx_core_timer, NULL, NULL);

    time(&mx_current_time);

    return;
}
Exemple #11
0
void mx_send_client_body(mx_connection_t *conn)
{
    int wsize, wcount;
    
    switch (conn->phase) {
    case mx_send_header_phase:
    {
        wsize = conn->sendlast - conn->sendpos;
        if (wsize == 0) {
            conn->phase = mx_send_body_phase;
            goto send_body_flag;
        }
        
        wcount = write(conn->fd, conn->sendpos, wsize);
        if (wcount == -1) {
            if (errno != EAGAIN && errno != EWOULDBLOCK) {
                mx_write_log(mx_log_debug, "Failed to write, and not due to blocking");
                mx_free_connection(conn);
                return;
            }
        } else if (wcount == 0) {
            mx_free_connection(conn);
            return;
        }
        
        conn->sendpos += wcount;
        if (wcount == wsize) {
            conn->phase = mx_send_body_phase;
            goto send_body_flag;
        }
        break;
    }

    case mx_send_body_phase: {
send_body_flag:
        wcount = write(conn->fd, conn->itemptr, conn->itembytes);
        if (wcount == -1) {
            if (errno != EAGAIN && errno != EWOULDBLOCK) {
                mx_write_log(mx_log_debug, "Failed to write, and not due to blocking");
                mx_free_connection(conn);
                return;
            }
        } else if (wcount == 0) {
            mx_free_connection(conn);
            return;
        }

        conn->itemptr += wcount;
        conn->itembytes -= wcount;

        if (conn->itembytes <= 0) { /* finish sent job item */
            conn->sendpos = conn->sendbuf;
            conn->sendlast = conn->sendbuf;

            if (!conn->sent_recycle) { /* not be recycle */
                free(conn->item);      /* free current job */
            } else {
                conn->sent_recycle = 0;
                conn->recycle_id = -1;
            }

            conn->item = NULL;
            conn->itemptr = NULL;
            conn->itembytes = 0;

            conn->state = mx_event_reading;
            conn->rev_handler = mx_recv_client_request;
            aeDeleteFileEvent(mx_daemon->event, conn->fd, AE_WRITABLE);
        }
        break;
    }
    }
    return;
}
Exemple #12
0
int mx_load_queues()
{
    struct mx_job_header header;
    mx_queue_t *queue;
    mx_job_t *job;
    time_t current_time = time(NULL);
    int count = 0;
    char tbuf[128];
    FILE *fp;
    int retval;

    if (!mx_global->bgsave_filepath || 
        !(fp = fopen(mx_global->bgsave_filepath, "rb")))
    {
        return 0;
    }

    if (fread(tbuf, sizeof(MX_BGSAVE_HEADER) - 1, 1, fp) != 1) {
        goto failed;
    }

    if (strncmp(tbuf, MX_BGSAVE_HEADER, sizeof(MX_BGSAVE_HEADER) - 1) != 0) {
        mx_write_log(mx_log_debug, "(%s) was a invaild database file", mx_global->bgsave_filepath);
        fclose(fp);
        return -1;
    }

    while (1)
    {
        if (fread(&header, sizeof(header), 1, fp) != 1) {
            goto failed;
        }

        /* finish and break */
        if (header.qlen == 0 || header.jlen == 0) {
            break;
        }

        if (fread(tbuf, header.qlen, 1, fp) != 1) {
            goto failed;
        }

        tbuf[header.qlen] = 0;

        /* find the queue from queue table */
        if (hash_lookup(mx_global->queue_table, tbuf, (void **)&queue) == -1)
        {
            /* not found and create it */
            if (!(queue = mx_queue_create(tbuf, header.qlen))) {
                goto failed;
            }

            if (hash_insert(mx_global->queue_table, tbuf, queue) != 0) {
                goto failed;
            }
        }

        job = mx_job_create(queue, header.prival, header.timeout, header.jlen);
        if (!job) {
            goto failed;
        }

        if (fread(job->body, job->length, 1, fp) != 1) {
            goto failed;
        }

        job->body[job->length] = CR_CHR;
        job->body[job->length+1] = LF_CHR;

        if (job->timeout > 0 && job->timeout > current_time) {
            retval = mx_skiplist_insert(mx_global->delay_queue, job->timeout, job);

        } else {
            job->timeout = 0;
            retval = mx_skiplist_insert(queue->list, job->prival, job);
        }

        if (retval != 0) {
            goto failed;
        }

        count++;
    }

    mx_write_log(mx_log_debug, "finish load (%d)jobs from disk", count);
    fclose(fp);
    return 0;

failed:
    mx_write_log(mx_log_error, "failed to read jobs from disk, message(%s)", strerror(errno));
    fclose(fp);
    return -1;
}