int _ribified_pipe2(int pipefd[2], int flags) { if (0 > pipe2(pipefd, flags | O_NONBLOCK)) return -1; if (0 == ribs_epoll_add(pipefd[0], EPOLLIN | EPOLLET, event_loop_ctx) && 0 == ribs_epoll_add(pipefd[1], EPOLLOUT | EPOLLET, event_loop_ctx)) return 0; int my_error = errno; close(pipefd[0]); close(pipefd[1]); errno = my_error; return -1; }
int _ribified_socket(int domain, int type, int protocol) { int sockfd = socket(domain, type | SOCK_NONBLOCK, protocol); if (0 > sockfd) return sockfd; if (0 > ribs_epoll_add(sockfd, EPOLLIN | EPOLLOUT | EPOLLET, event_loop_ctx)) return close(sockfd), -1; return sockfd; }
int ribs_timer_create(void (*handler)(int)) { int tfd = timerfd_create(CLOCK_MONOTONIC, TFD_NONBLOCK); if (0 > tfd) return LOGGER_PERROR("timerfd_create"), -1; struct ribs_context *ctx = ribs_context_create(1024 * 1024, sizeof(void (*)(void)), _ribs_timer_wrapper); void (**ref)(int) = (void (**)(int))ctx->reserved; *ref = handler; if (0 > ribs_epoll_add(tfd, EPOLLIN, ctx)) return LOGGER_PERROR("epoll_add"), close(tfd), -1; return tfd; }
int _ribified_connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen) { int flags=fcntl(sockfd, F_GETFL); if (0 > fcntl(sockfd, F_SETFL, flags | O_NONBLOCK)) return LOGGER_PERROR("fcntl"), -1; int res = connect(sockfd, addr, addrlen); if (res < 0 && errno != EINPROGRESS) { return res; } return ribs_epoll_add(sockfd, EPOLLIN | EPOLLOUT | EPOLLET | EPOLLRDHUP, event_loop_ctx); }
int ribs_epoll_add(int fd, uint32_t events, struct ribs_context* ctx) { struct epoll_event ev = { .events = events, .data.fd = fd }; if (0 > epoll_ctl(ribs_epoll_fd, EPOLL_CTL_ADD, fd, &ev)) return LOGGER_PERROR("epoll_ctl"), -1; epoll_worker_set_fd_ctx(fd, ctx); return 0; } struct ribs_context* small_ctx_for_fd(int fd, void (*func)(void)) { void *ctx=ribs_context_create(SMALL_STACK_SIZE, func); if (NULL == ctx) return LOGGER_PERROR("ribs_context_create"), NULL; if (0 > ribs_epoll_add(fd, EPOLLIN, ctx)) return NULL; return ctx; }
static void http_server_accept_connections(void) { struct http_server **server_ref = (struct http_server **)current_ctx->reserved; struct http_server *server = *server_ref; for (;; yield()) { struct sockaddr_in new_addr; socklen_t new_addr_size = sizeof(struct sockaddr_in); int fd = accept4(server->fd, (struct sockaddr *)&new_addr, &new_addr_size, SOCK_CLOEXEC | SOCK_NONBLOCK); if (0 > fd) continue; if (0 > ribs_epoll_add(fd, EPOLLIN | EPOLLOUT | EPOLLET | EPOLLRDHUP, server->idle_ctx)) { close(fd); continue; } timeout_handler_add_fd_data(&server->timeout_handler, epoll_worker_fd_map + fd); } }
int epoll_worker_init(void) { struct rlimit rlim; if (0 > getrlimit(RLIMIT_NOFILE, &rlim)) return LOGGER_PERROR("getrlimit(RLIMIT_NOFILE)"), -1; epoll_worker_fd_map = calloc(rlim.rlim_cur, sizeof(struct epoll_worker_fd_data)); ribs_epoll_fd = epoll_create1(EPOLL_CLOEXEC); if (ribs_epoll_fd < 0) return LOGGER_PERROR("epoll_create1"), -1; /* block some signals */ sigset_t set; sigemptyset(&set); sigaddset(&set, SIGPIPE); if (-1 == sigprocmask(SIG_BLOCK, &set, NULL)) return LOGGER_PERROR("sigprocmask"), -1; #ifdef UGLY_GETADDRINFO_WORKAROUND sigemptyset(&set); sigaddset(&set, SIGRTMIN); if (-1 == sigprocmask(SIG_BLOCK, &set, NULL)) return LOGGER_PERROR("sigprocmask"), -1; /* sigrtmin to context */ int sfd = signalfd(-1, &set, SFD_NONBLOCK); if (0 > sfd) return LOGGER_PERROR("signalfd"), -1; if (NULL == small_ctx_for_fd(sfd, sigrtmin_to_context)) return -1; #endif event_loop_ctx = ribs_context_create(SMALL_STACK_SIZE, event_loop); /* pipe to conetxt */ int pipefd[2]; if (0 > pipe2(pipefd, O_NONBLOCK)) return LOGGER_PERROR("pipe"), -1; if (NULL == small_ctx_for_fd(pipefd[0], pipe_to_context)) return -1; queue_ctx_fd = pipefd[1]; return ribs_epoll_add(queue_ctx_fd, EPOLLOUT | EPOLLET, event_loop_ctx); }
struct http_client_context *http_client_pool_create_client(struct http_client_pool *http_client_pool, struct in_addr addr, uint16_t port, struct ribs_context *rctx) { int cfd; struct http_client_key key = { .addr = addr, .port = port }; uint32_t ofs = hashtable_lookup(&ht_persistent_clients, &key, sizeof(struct http_client_key)); struct list *head; if (ofs > 0 && !list_empty(head = client_heads + *(uint32_t *)hashtable_get_val(&ht_persistent_clients, ofs))) { struct list *client = list_pop_head(head); cfd = client - client_chains; struct epoll_worker_fd_data *fd_data = epoll_worker_fd_map + cfd; TIMEOUT_HANDLER_REMOVE_FD_DATA(fd_data); } else { cfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); if (0 > cfd) return LOGGER_PERROR("socket"), NULL; const int option = 1; if (0 > setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option))) return LOGGER_PERROR("setsockopt SO_REUSEADDR"), close(cfd), NULL; if (0 > setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option))) return LOGGER_PERROR("setsockopt TCP_NODELAY"), close(cfd), NULL; struct sockaddr_in saddr = { .sin_family = AF_INET, .sin_port = htons(port), .sin_addr = addr }; if (0 > connect(cfd, (struct sockaddr *)&saddr, sizeof(saddr)) && EINPROGRESS != errno) return LOGGER_PERROR("connect"), close(cfd), NULL; if (0 > ribs_epoll_add(cfd, EPOLLIN | EPOLLOUT | EPOLLET, event_loop_ctx)) return close(cfd), NULL; } struct ribs_context *new_ctx = ctx_pool_get(&http_client_pool->ctx_pool); struct epoll_worker_fd_data *fd_data = epoll_worker_fd_map + cfd; fd_data->ctx = new_ctx; ribs_makecontext(new_ctx, rctx ? rctx : current_ctx, http_client_fiber_main); struct http_client_context *cctx = (struct http_client_context *)new_ctx->reserved; cctx->fd = cfd; cctx->pool = http_client_pool; cctx->key = (struct http_client_key){ .addr = addr, .port = port }; vmbuf_init(&cctx->request, 4096); vmbuf_init(&cctx->response, 4096); timeout_handler_add_fd_data(&http_client_pool->timeout_handler, fd_data); return cctx; }
static int _set_signals(void) { struct sigaction sa = { .sa_handler = signal_handler, .sa_flags = 0 }; sigaction(SIGTERM, &sa, NULL); sigaction(SIGINT, &sa, NULL); sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset, SIGCHLD); if (0 > sigprocmask(SIG_BLOCK, &sigset, NULL)) return LOGGER_PERROR("sigprocmask"), -1; sigfd = signalfd(-1, &sigset, SFD_NONBLOCK | SFD_CLOEXEC); if (0 > sigfd) return LOGGER_PERROR("signalfd"), -1; if (NULL == (sigfd_ctx = ribs_context_create(SIG_CHLD_STACK_SIZE, 0, _handle_sig_child))) return -1; return 0; } static int _init_subprocesses(const char *pidfilename, int num_forks) { if (0 >= num_forks) { num_forks = sysconf(_SC_NPROCESSORS_CONF); if (0 > num_forks) exit(EXIT_FAILURE); } LOGGER_INFO("num forks = %d", num_forks); num_instances = num_forks; daemon_instance = 0; children_pids = calloc(num_forks - 1, sizeof(pid_t)); int i; for (i = 1; i < num_forks; ++i) { LOGGER_INFO("starting sub-process %d", i); pid_t pid = fork(); if (0 > pid) return LOGGER_PERROR("fork"), -1; if (0 == pid) { daemon_instance = i; if (0 > prctl(PR_SET_PDEATHSIG, SIGTERM)) return LOGGER_PERROR("prctl"), -1; if (0 > _set_signals()) return LOGGER_ERROR("failed to set signals"), -1; LOGGER_INFO("sub-process %d started", i); return 0; } else children_pids[i-1] = pid; } if (pidfilename && 0 > _set_pidfile(pidfilename)) return LOGGER_ERROR("failed to set pidfile"), -1; if (0 > _set_signals()) return LOGGER_ERROR("failed to set signals"), -1; return 0; } static int ribs_server_init_daemon(const char *pidfilename, const char *logfilename, int num_forks) { if (0 > pipe2(child_is_up_pipe, O_CLOEXEC)) return LOGGER_ERROR("failed to create pipe"), -1; pid_t pid = fork(); if (pid < 0) return LOGGER_PERROR("ribs_server_init_daemon, fork"), -1; if (pid > 0) { close(child_is_up_pipe[1]); /* close the write side */ /* wait for child to come up */ uint8_t t; int res; LOGGER_INFO("waiting for the child process [%d] to start...", pid); if (0 >= (res = read(child_is_up_pipe[0], &t, sizeof(t)))) { if (0 > res) LOGGER_PERROR("pipe"); LOGGER_ERROR("child process failed to start"); return -1; } LOGGER_INFO("child process started successfully"); exit(EXIT_SUCCESS); } close(child_is_up_pipe[0]); /* close the read side */ umask(0); if (0 > setsid()) return LOGGER_PERROR("daemonize, setsid"), -1; int fdnull = open("/dev/null", O_RDWR); if (0 > fdnull) return LOGGER_PERROR("/dev/null"), -1; dup2(fdnull, STDIN_FILENO); if (logfilename) { if (0 > _logger_init(logfilename)) return -1; } else { dup2(fdnull, STDOUT_FILENO); dup2(fdnull, STDERR_FILENO); } close(fdnull); LOGGER_INFO("child process started"); return _init_subprocesses(pidfilename, num_forks); } int ribs_server_init(int daemonize, const char *pidfilename, const char *logfilename, int num_forks) { if (0 > hashtable_init(&ht_pid_to_ctx, 0)) return -1; if (daemonize) return ribs_server_init_daemon(pidfilename, logfilename, num_forks); /* if (logfilename && 0 > _logger_init(logfilename)) */ /* exit(EXIT_FAILURE); */ return _init_subprocesses(pidfilename, num_forks); } int ribs_server_signal_children(int sig) { if (0 >= num_instances || 0 != daemon_instance) return 0; int i, res = 0; for (i = 0; i < num_instances-1; ++i) { if (0 < children_pids[i] && 0 > kill(children_pids[i], sig)) { LOGGER_PERROR("kill [%d] %d", sig, children_pids[i]); res = -1; } } return res; } void ribs_server_start(void) { daemon_finalize(); if (0 <= sigfd && 0 > ribs_epoll_add(sigfd, EPOLLIN, sigfd_ctx)) return LOGGER_ERROR("ribs_epoll_add: sigfd"); epoll_worker_loop(); if (0 >= num_instances || 0 != daemon_instance) return; LOGGER_INFO("sending SIGTERM to sub-processes"); ribs_server_signal_children(SIGTERM); LOGGER_INFO("waiting for sub-processes to exit"); int i, status; for (i = 0; i < num_instances-1; ++i) { if (0 < children_pids[i] && 0 > waitpid(children_pids[i], &status, 0)) LOGGER_PERROR("waitpid %d", children_pids[i]); } LOGGER_INFO("sub-processes terminated"); }
int http_server_init_acceptor(struct http_server *server) { if (0 > ribs_epoll_add(server->fd, EPOLLIN, server->accept_ctx)) return -1; return timeout_handler_init(&server->timeout_handler); }
int sendemail2(struct sendemail_mta *mta, struct email *email) { if (NULL == mta) { mta = global_mta; } int cfd = socket(PF_INET, SOCK_STREAM | SOCK_NONBLOCK, IPPROTO_TCP); if (0 > cfd) return LOGGER_PERROR("sendemail: socket"), -1; const int option = 1; if (0 > setsockopt(cfd, SOL_SOCKET, SO_REUSEADDR, &option, sizeof(option))) return LOGGER_PERROR("sendemail: setsockopt SO_REUSEADDR"), close(cfd), -1; if (0 > setsockopt(cfd, IPPROTO_TCP, TCP_NODELAY, &option, sizeof(option))) return LOGGER_PERROR("sendemail: setsockopt TCP_NODELAY"), close(cfd), -1; if (0 > connect(cfd, (struct sockaddr *)&mta->saddr, sizeof(mta->saddr)) && EINPROGRESS != errno) return LOGGER_PERROR("sendemail: connect"), close(cfd), -1; if (0 > ribs_epoll_add(cfd, EPOLLIN | EPOLLOUT | EPOLLET, current_ctx)) return close(cfd), -1; struct vmbuf response = VMBUF_INITIALIZER; struct vmbuf request = VMBUF_INITIALIZER; vmbuf_init(&response, 4096); vmbuf_init(&request, 4096); int code = -1; size_t ofs = 0; READ_AND_CHECK(220); vmbuf_sprintf(&request, "EHLO %s\r\n", mta->myhost); SEND_DATA; READ_AND_CHECK(250); vmbuf_sprintf(&request, "MAIL FROM:<%s>\r\n", email->from); SEND_DATA; READ_AND_CHECK(250); struct rcptlist *rcpt = &email->rcpt; while (rcpt) { vmbuf_sprintf(&request, "RCPT TO:<%s>\r\n", rcpt->to); SEND_DATA; READ_AND_CHECK(250); rcpt = rcpt->next; } vmbuf_strcpy(&request, "DATA\r\n"); SEND_DATA; READ_AND_CHECK(354); struct iovec iov[2]= { [0] = { .iov_base = email->data, .iov_len = strlen(email->data) },