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