static inline int _mk_event_del(struct mk_event_ctx *ctx, struct mk_event *event) { int ret; struct kevent ke = {0, 0, 0, 0, 0, 0}; if (event->mask & MK_EVENT_READ) { EV_SET(&ke, event->fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); ret = kevent(ctx->kfd, &ke, 1, NULL, 0, NULL); if (ret < 0) { mk_libc_error("kevent"); return ret; } } if (event->mask & MK_EVENT_WRITE) { EV_SET(&ke, event->fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); ret = kevent(ctx->kfd, &ke, 1, NULL, 0, NULL); if (ret < 0) { mk_libc_error("kevent"); return ret; } } return 0; }
static inline int _mk_event_timeout_create(struct mk_event_ctx *ctx, int expire, void *data) { int fd; int ret; struct mk_event *event; struct kevent ke; /* * We just need a file descriptor number, we don't care from where it * comes from. */ fd = open("/dev/null", 0); if (fd == -1) { mk_libc_error("open"); return -1; } event = data; event->fd = fd; event->type = MK_EVENT_NOTIFICATION; event->mask = MK_EVENT_EMPTY; EV_SET(&ke, fd, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, expire, event); ret = kevent(ctx->kfd, &ke, 1, NULL, 0, NULL); if (ret < 0) { close(fd); mk_libc_error("kevent"); return -1; } event->mask = MK_EVENT_READ; return fd; }
/* Register a timeout file descriptor */ static inline int _mk_event_timeout_create(mk_event_ctx_t *ctx, int expire) { int ret; int timer_fd; struct itimerspec its; /* expiration interval */ its.it_interval.tv_sec = expire; its.it_interval.tv_nsec = 0; /* initial expiration */ its.it_value.tv_sec = time(NULL) + expire; its.it_value.tv_nsec = 0; timer_fd = timerfd_create(CLOCK_REALTIME, 0); if (timer_fd == -1) { mk_libc_error("timerfd"); return -1; } ret = timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &its, NULL); if (ret < 0) { mk_libc_error("timerfd_settime"); return -1; } /* register the timer into the epoll queue */ ret = _mk_event_add(ctx, timer_fd, MK_EVENT_READ); if (ret != 0) { close(timer_fd); return ret; } return timer_fd; }
static inline int _mk_event_add(struct mk_event_ctx *ctx, int fd, int type, uint32_t events, void *data) { int ret; int set = MK_FALSE; struct mk_event *event; struct kevent ke = {0, 0, 0, 0, 0, 0}; event = (struct mk_event *) data; if (event->mask == MK_EVENT_EMPTY) { event->fd = fd; event->type = type; } /* Read flag */ if ((event->mask ^ MK_EVENT_READ) && (events & MK_EVENT_READ)) { EV_SET(&ke, fd, EVFILT_READ, EV_ADD, 0, 0, event); set = MK_TRUE; //printf("[ADD] fd=%i READ\n", fd); } else if ((event->mask & MK_EVENT_READ) && (events ^ MK_EVENT_READ)) { EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, event); set = MK_TRUE; //printf("[DEL] fd=%i READ\n", fd); } if (set == MK_TRUE) { ret = kevent(ctx->kfd, &ke, 1, NULL, 0, NULL); if (ret < 0) { mk_libc_error("kevent"); return ret; } } /* Write flag */ set = MK_FALSE; if ((event->mask ^ MK_EVENT_WRITE) && (events & MK_EVENT_WRITE)) { EV_SET(&ke, fd, EVFILT_WRITE, EV_ADD, 0, 0, event); set = MK_TRUE; //printf("[ADD] fd=%i WRITE\n", fd); } else if ((event->mask & MK_EVENT_WRITE) && (events ^ MK_EVENT_WRITE)) { EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, event); set = MK_TRUE; //printf("[DEL] fd=%i WRITE\n", fd); } if (set == MK_TRUE) { ret = kevent(ctx->kfd, &ke, 1, NULL, 0, NULL); if (ret < 0) { mk_libc_error("kevent"); return ret; } } event->mask = events; return 0; }
static inline int _mk_event_channel_create(struct mk_event_ctx *ctx, int *r_fd, int *w_fd, void *data) { int ret; int fd[2]; struct mk_event *event; ret = pipe(fd); if (ret < 0) { mk_libc_error("pipe"); return ret; } event = data; event->fd = fd[0]; event->type = MK_EVENT_NOTIFICATION; event->mask = MK_EVENT_EMPTY; ret = _mk_event_add(ctx, fd[0], MK_EVENT_NOTIFICATION, MK_EVENT_READ, event); if (ret != 0) { close(fd[0]); close(fd[1]); return ret; } *r_fd = fd[0]; *w_fd = fd[1]; return 0; }
/* * It register certain events for the file descriptor in question, if * the file descriptor have not been registered, create a new entry. */ static inline int _mk_event_add(mk_event_ctx_t *ctx, int fd, int events) { int op; int ret; struct mk_event_fd_state *fds; struct epoll_event event = {0, {0}}; /* Verify the FD status and desired operation */ fds = mk_event_get_state(fd); if (fds->mask == MK_EVENT_EMPTY) { op = EPOLL_CTL_ADD; } else { op = EPOLL_CTL_MOD; } event.data.fd = fd; event.events = EPOLLERR | EPOLLHUP | EPOLLRDHUP; if (events & MK_EVENT_READ) { event.events |= EPOLLIN; } if (events & MK_EVENT_WRITE) { event.events |= EPOLLOUT; } ret = epoll_ctl(ctx->efd, op, fd, &event); if (ret < 0) { mk_libc_error("epoll_ctl"); return -1; } fds->mask = events; return ret; }
static inline void *_mk_event_loop_create(int size) { mk_event_ctx_t *ctx; /* Main event context */ ctx = mk_mem_malloc_z(sizeof(mk_event_ctx_t)); if (!ctx) { return NULL; } /* Create the epoll instance */ ctx->efd = epoll_create1(EPOLL_CLOEXEC); if (ctx->efd == -1) { mk_libc_error("epoll_create"); mk_mem_free(ctx); return NULL; } /* Allocate space for events queue */ ctx->events = mk_mem_malloc_z(sizeof(struct epoll_event) * size); if (!ctx->events) { close(ctx->efd); mk_mem_free(ctx); return NULL; } ctx->queue_size = size; return ctx; }
static inline void *_mk_event_loop_create(int size) { struct mk_event_ctx *ctx; /* Main event context */ ctx = mk_mem_malloc_z(sizeof(struct mk_event_ctx)); if (!ctx) { return NULL; } /* Create the epoll instance */ ctx->kfd = kqueue(); if (ctx->kfd == -1) { mk_libc_error("kqueue"); mk_mem_free(ctx); return NULL; } /* Allocate space for events queue */ ctx->events = mk_mem_malloc_z(sizeof(struct kevent) * size); if (!ctx->events) { close(ctx->kfd); mk_mem_free(ctx); return NULL; } ctx->queue_size = size; return ctx; }
/* Register a timeout file descriptor */ static inline int _mk_event_timeout_create(struct mk_event_ctx *ctx, time_t sec, long nsec, void *data) { int ret; int timer_fd; struct itimerspec its; struct mk_event *event; mk_bug(!data); memset(&its, '\0', sizeof(struct itimerspec)); /* expiration interval */ its.it_interval.tv_sec = sec; its.it_interval.tv_nsec = nsec; /* initial expiration */ its.it_value.tv_sec = time(NULL) + sec; its.it_value.tv_nsec = 0; timer_fd = timerfd_create(CLOCK_REALTIME, 0); if (timer_fd == -1) { mk_libc_error("timerfd"); return -1; } ret = timerfd_settime(timer_fd, TFD_TIMER_ABSTIME, &its, NULL); if (ret < 0) { mk_libc_error("timerfd_settime"); return -1; } event = data; event->fd = timer_fd; event->type = MK_EVENT_NOTIFICATION; event->mask = MK_EVENT_EMPTY; /* register the timer into the epoll queue */ ret = _mk_event_add(ctx, timer_fd, MK_EVENT_NOTIFICATION, MK_EVENT_READ, data); if (ret != 0) { close(timer_fd); return ret; } return timer_fd; }
static inline int _mk_event_timeout_create(struct mk_event_ctx *ctx, int expire, void *data) { int fd; int ret; struct mk_event *event; struct kevent ke; /* * We just need a file descriptor number, we don't care from where it * comes from. */ fd = open("/dev/null", 0); if (fd == -1) { mk_libc_error("open"); return -1; } event = data; event->fd = fd; event->type = MK_EVENT_NOTIFICATION; event->mask = MK_EVENT_EMPTY; #ifdef NOTE_SECONDS /* FreeBSD or LINUX_KQUEUE defined */ EV_SET(&ke, fd, EVFILT_TIMER, EV_ADD, NOTE_SECONDS, expire, event); #else /* Other BSD have no NOTE_SECONDS & specify milliseconds */ EV_SET(&ke, fd, EVFILT_TIMER, EV_ADD, 0, expire * 1000, event); #endif ret = kevent(ctx->kfd, &ke, 1, NULL, 0, NULL); if (ret < 0) { close(fd); mk_libc_error("kevent"); return -1; } /* * FIXME: the timeout event is not triggered when using libkqueue, need * to confirm how it behave on native OSX. */ event->mask = MK_EVENT_READ; return fd; }
int mk_socket_create() { int sockfd; if ((sockfd = socket(AF_INET6, SOCK_STREAM, 0)) == -1) { mk_libc_error("socket"); return -1; } return sockfd; }
int mk_socket_reset(int socket) { int status = 1; if (setsockopt(socket, SOL_SOCKET, SO_REUSEADDR, &status, sizeof(int)) == -1) { mk_libc_error("socket"); exit(EXIT_FAILURE); } return 0; }
static inline int _mk_event_del(struct mk_event_ctx *ctx, int fd) { int ret; struct kevent ke = {0, 0, 0, 0, 0, 0}; EV_SET(&ke, fd, EVFILT_READ, EV_DELETE, 0, 0, NULL); ret = kevent(ctx->kfd, &ke, 1, NULL, 0, NULL); if (ret < 0) { mk_libc_error("kevent"); return ret; } EV_SET(&ke, fd, EVFILT_WRITE, EV_DELETE, 0, 0, NULL); ret = kevent(ctx->kfd, &ke, 1, NULL, 0, NULL); if (ret < 0) { mk_libc_error("kevent"); return ret; } return 0; }
/* Delete an event */ static inline int _mk_event_del(mk_event_ctx_t *ctx, int fd) { int ret; ret = epoll_ctl(ctx->efd, EPOLL_CTL_DEL, fd, NULL); MK_TRACE("[FD %i] Epoll, remove from QUEUE_FD=%i, ret=%i", fd, ctx->efd, ret); if (ret < 0) { mk_libc_error("epoll_ctl"); } return ret; }
int mk_utils_worker_spawn(void (*func) (void *), void *arg, pthread_t *tid) { pthread_attr_t thread_attr; pthread_attr_init(&thread_attr); pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE); if (pthread_create(tid, &thread_attr, (void *) func, arg) < 0) { mk_libc_error("pthread_create"); return -1; } return 0; }
int mk_kernel_version() { int a, b, c; int len; int pos; char *p, *t; char *tmp; struct utsname uts; if (uname(&uts) == -1) { mk_libc_error("uname"); } len = strlen(uts.release); /* Fixme: this don't support Linux Kernel 10.x.x :P */ a = (*uts.release - '0'); /* Second number */ p = (uts.release) + 2; pos = mk_string_char_search(p, '.', len - 2); if (pos <= 0) { /* Some Debian systems uses a different notation, e.g: 3.14-2-amd64 */ pos = mk_string_char_search(p, '-', len - 2); if (pos <= 0) { return -1; } } tmp = mk_string_copy_substr(p, 0, pos); if (!tmp) { return -1; } b = atoi(tmp); mk_mem_free(tmp); /* Last number (it needs filtering) */ t = p = p + pos + 1; do { t++; } while (isdigit(*t)); tmp = mk_string_copy_substr(p, 0, t - p); if (!tmp) { return -1; } c = atoi(tmp); mk_mem_free(tmp); MK_TRACE("Kernel detected: %i.%i.%i", a, b, c); return MK_KERNEL_VERSION(a, b, c); }
pthread_t mk_utils_worker_spawn(void (*func) (void *), void *arg) { pthread_t tid; pthread_attr_t thread_attr; pthread_attr_init(&thread_attr); pthread_attr_setdetachstate(&thread_attr, PTHREAD_CREATE_JOINABLE); if (pthread_create(&tid, &thread_attr, (void *) func, arg) < 0) { mk_libc_error("pthread_create"); exit(EXIT_FAILURE); } return tid; }
/* * This routine creates a timer, since this select(2) backend aims to be used * in very old systems to be compatible, we cannot trust timerfd_create(2) * will be available (e.g: Cygwin), so our workaround is to create a pipe(2) * and a thread, this thread writes a byte upon the expiration time is reached. */ static inline int _mk_event_timeout_create(struct mk_event_ctx *ctx, time_t sec, long nsec, void *data) { int ret; int fd[2]; struct mk_event *event; struct fd_timer *timer; timer = mk_mem_alloc(sizeof(struct fd_timer)); if (!timer) { return -1; } ret = pipe(fd); if (ret < 0) { mk_mem_free(timer); mk_libc_error("pipe"); return ret; } event = (struct mk_event *) data; event->fd = fd[0]; event->type = MK_EVENT_NOTIFICATION; event->mask = MK_EVENT_EMPTY; _mk_event_add(ctx, fd[0], MK_EVENT_NOTIFICATION, MK_EVENT_READ, data); event->mask = MK_EVENT_READ; /* Compose the timer context, this is released inside the worker thread */ timer->fd = fd[1]; timer->sec = sec; timer->nsec = nsec; timer->run = MK_TRUE; event->data = timer; /* Now the dirty workaround, create a thread */ ret = mk_utils_worker_spawn(_timeout_worker, timer, &timer->tid); if (ret < 0) { close(fd[0]); close(fd[1]); mk_mem_free(timer); return -1; } return fd[0]; }
int mk_socket_create(int domain, int type, int protocol) { int fd; #ifdef SOCK_CLOEXEC fd = socket(domain, type | SOCK_CLOEXEC, protocol); #else fd = socket(domain, type, protocol); fcntl(fd, F_SETFD, FD_CLOEXEC); #endif if (fd == -1) { mk_libc_error("socket"); return -1; } return fd; }
/* * It register certain events for the file descriptor in question, if * the file descriptor have not been registered, create a new entry. */ static inline int _mk_event_add(struct mk_event_ctx *ctx, int fd, int type, uint32_t events, void *data) { int op; int ret; struct mk_event *event; struct epoll_event ep_event; /* Verify the FD status and desired operation */ event = (struct mk_event *) data; if (event->mask == MK_EVENT_EMPTY) { op = EPOLL_CTL_ADD; event->fd = fd; event->status = MK_EVENT_REGISTERED; event->type = type; } else { op = EPOLL_CTL_MOD; if (type != MK_EVENT_UNMODIFIED) { event->type = type; } } ep_event.events = EPOLLERR | EPOLLHUP | EPOLLRDHUP; ep_event.data.ptr = data; if (events & MK_EVENT_READ) { ep_event.events |= EPOLLIN; } if (events & MK_EVENT_WRITE) { ep_event.events |= EPOLLOUT; } ret = epoll_ctl(ctx->efd, op, fd, &ep_event); if (ret < 0) { mk_libc_error("epoll_ctl"); return -1; } event->mask = events; return ret; }
static inline int _mk_event_channel_create(mk_event_ctx_t *ctx, int *r_fd, int *w_fd) { int fd; int ret; fd = eventfd(0, EFD_CLOEXEC); if (fd == -1) { mk_libc_error("eventfd"); return -1; } ret = _mk_event_add(ctx, fd, MK_EVENT_READ); if (ret != 0) { close(fd); return ret; } *w_fd = *r_fd = fd; return 0; }
static inline void *_mk_event_loop_create(int size) { int efd; struct mk_event_ctx *ctx; /* Main event context */ ctx = mk_mem_alloc_z(sizeof(struct mk_event_ctx)); if (!ctx) { return NULL; } /* Create the epoll instance */ #ifdef EPOLL_CLOEXEC efd = epoll_create1(EPOLL_CLOEXEC); #else efd = epoll_create(1); if (efd > 0) { if (fcntl(efd, F_SETFD, FD_CLOEXEC) == -1) { perror("fcntl"); } } #endif if (efd == -1) { mk_libc_error("epoll_create"); mk_mem_free(ctx); return NULL; } ctx->efd = efd; /* Allocate space for events queue */ ctx->events = mk_mem_alloc_z(sizeof(struct epoll_event) * size); if (!ctx->events) { close(ctx->efd); mk_mem_free(ctx); return NULL; } ctx->queue_size = size; return ctx; }
/* * This routine creates a timer, since this select(2) backend aims to be used * in very old systems to be compatible, we cannot trust timerfd_create(2) * will be available (e.g: Cygwin), so our workaround is to create a pipe(2) * and a thread, this thread writes a byte upon the expiration time is reached. */ static inline int _mk_event_timeout_create(struct mk_event_ctx *ctx, int expire, void *data) { int ret; int fd[2]; pid_t tid; struct mk_event *event; struct fd_timer *timer; timer = mk_mem_malloc(sizeof(struct fd_timer)); if (!timer) { return -1; } ret = pipe(fd); if (ret < 0) { mk_mem_free(timer); mk_libc_error("pipe"); return ret; } event = (struct mk_event *) data; event->fd = fd[0]; event->type = MK_EVENT_NOTIFICATION; event->mask = MK_EVENT_EMPTY; _mk_event_add(ctx, fd[0], MK_EVENT_NOTIFICATION, MK_EVENT_READ, data); event->mask = MK_EVENT_READ; /* Compose the timer context, this is released inside the worker thread */ timer->fd = fd[1]; timer->expiration = expire; /* Now the dirty workaround, create a thread */ mk_utils_worker_spawn(_timeout_worker, timer); return fd[0]; }
int mk_socket_open(char *path, int async) { int ret; int socket_fd; struct sockaddr_un address; socket_fd = mk_socket_create(PF_UNIX, SOCK_STREAM, 0); if (socket_fd == -1) { return -1; } memset(&address, '\0', sizeof(struct sockaddr_un)); address.sun_family = AF_UNIX; snprintf(address.sun_path, sizeof(address.sun_path), "%s", path); if (async == MK_TRUE) { mk_socket_set_nonblocking(socket_fd); } ret = connect(socket_fd, (struct sockaddr *) &address, sizeof(struct sockaddr_un)); if (ret == -1) { if (errno == EINPROGRESS) { return socket_fd; } else { #ifdef TRACE mk_libc_error("connect"); #endif close(socket_fd); return -1; } } return socket_fd; }
/* * Initialize the global Event structure used by threads to access the * global file descriptor table. */ int mk_event_initalize() { int i; int ret; mk_event_fdt_t *efdt; struct rlimit rlim; /* * Event File Descriptor Table (EFDT) * ---------------------------------- * The main requirement for this implementation is that we need to maintain * a state of each file descriptor registered events, such as READ, WRITE, * SLEEPING, etc. This is required not by Monkey core but is a fundamental * piece to let plugins perform safe operations over file descriptors and * their events. * * The EFDT is created in the main process context and aims to be used by * every Worker thread. Once a connection arrives and it's notified to the * Worker, this last one will register the file descriptor status on the * EFDT. * * The EFDT is a fixed size array that contains entries for each possible * file descriptor number assigned for a TCP connection. In order to make * sure the assigned number can be used as an index of the array, we have * verified that the Linux Kernel always assigns a number in a range as * defined in __alloc_fd() on file file.c: * * start: > 2 * * end : rlim.rlim.cur * * The maximum number assigned is always the process soft limit for * RLIMIT_NOFILE, so basically we are safe trusting on this model. * * Note: as we make sure each file descriptor number is only handled by one * thread, there is no race conditions. */ efdt = mk_mem_malloc_z(sizeof(mk_event_fdt_t)); if (!efdt) { mk_err("Event: could not allocate memory for event FD Table"); return -1; } /* * Despites what config->server_capacity says, we need to prepare to handle * a high number of file descriptors as process limit allows. */ ret = getrlimit(RLIMIT_NOFILE, &rlim); if (ret == -1) { mk_libc_error("getrlimit"); return -1; } efdt->size = rlim.rlim_cur; efdt->states = mk_mem_malloc_z(sizeof(struct mk_event_fd_state) * efdt->size); if (!efdt->states) { mk_err("Event: could not allocate memory for events states on FD Table"); return -1; } /* mark all file descriptors as available */ for (i = 0; i < efdt->size; i++) { efdt->states[i].fd = -1; efdt->states[i].mask = MK_EVENT_EMPTY; } mk_events_fdt = efdt; return 0; }