diff --git a/.gitignore b/.gitignore index b7d6c6e..740a001 100644 --- a/.gitignore +++ b/.gitignore @@ -38,6 +38,7 @@ cmake_install.cmake # binaries forker +client # diff *.orig diff --git a/CMakeLists.txt b/CMakeLists.txt index 0eec95a..7bf153b 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -37,7 +37,9 @@ set(CMAKE_CXX_FLAGS_RELEASE "${warning_flags} \ set(target forker) -add_executable(${target} forker.c) +add_executable(${target} forker.cc mmaper.c child.c ChildSignalHandler.cc ParentSignalHandler.cc Parent.cc InotifyHandler.cc EchoServerHandler.cc) + +add_executable(client client.c) target_link_libraries(${target} pthread diff --git a/ChildSignalHandler.cc b/ChildSignalHandler.cc new file mode 100644 index 0000000..b9cf891 --- /dev/null +++ b/ChildSignalHandler.cc @@ -0,0 +1,39 @@ +#include "ChildSignalHandler.hh" +#include +#include +#include +#include +#include +#include +#include "common.h" +#include "child.h" + +void ChildSignalHandler::handle_exit() +{ + write_child_exit_info(); + exit(EXIT_SUCCESS); +} + +void ChildSignalHandler::handle() +{ + struct signalfd_siginfo fdsi; + ssize_t sz; + + sz = read(fd, &fdsi, sizeof(struct signalfd_siginfo)); + if (sz != sizeof(struct signalfd_siginfo)) + handle_error("read child signal handler"); + + if (fdsi.ssi_signo == SIGINT) { + fprintf(stderr, "%d: Got SIGINT from %d\n", getpid(), fdsi.ssi_pid); + handle_exit(); + } else if (fdsi.ssi_signo == SIGQUIT) { + fprintf(stderr, "%d: Got SIGQUIT from %d\n", getpid(), fdsi.ssi_pid); + handle_exit(); + } else if (fdsi.ssi_signo == SIGTERM) { + fprintf(stderr, "%d: Got SIGTERM from %d\n", getpid(), fdsi.ssi_pid); + handle_exit(); + } else { + fprintf(stderr, "%d: Read unexpected signal from %d\n", getpid(), fdsi.ssi_pid); + } +} + diff --git a/ChildSignalHandler.hh b/ChildSignalHandler.hh new file mode 100644 index 0000000..7e75b4e --- /dev/null +++ b/ChildSignalHandler.hh @@ -0,0 +1,17 @@ +#pragma once + +#include "Handler.hh" + +class ChildSignalHandler: public Handler +{ +public: + ChildSignalHandler(int fd) + : Handler(fd) + {} + + virtual void handle(); + +protected: + void handle_exit(); +}; + diff --git a/EchoServerHandler.cc b/EchoServerHandler.cc new file mode 100644 index 0000000..0467f43 --- /dev/null +++ b/EchoServerHandler.cc @@ -0,0 +1,114 @@ +#include "EchoServerHandler.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +#define PORT "3490" // the port users will be connecting to + +#define BACKLOG 10 // how many pending connections queue will hold + +// get sockaddr, IPv4 or IPv6: +static void *get_in_addr(struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*)sa)->sin_addr); + } + + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} + +EchoServerHandler::EchoServerHandler() +: Handler(-1), accept_fd(-1) +{ +} + +int EchoServerHandler::init() +{ + int sockfd; // listen on sock_fd + struct addrinfo hints, *servinfo, *p; + int yes = 1; + int rv; + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + hints.ai_flags = AI_PASSIVE; // use my IP + + if ((rv = getaddrinfo(NULL, PORT, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + exit(1); + } + + // loop through all the results and bind to the first we can + for(p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, + p->ai_protocol)) == -1) { + handle_error("socket"); + continue; + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &yes, + sizeof(int)) == -1) { + handle_error("setsockopt reuse addr"); + } + + if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEPORT, &yes, + sizeof(int)) == -1) { + handle_error("setsockopt reuse port"); + } + + if (bind(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + close(sockfd); + handle_error("server: bind"); + continue; + } + + break; + } + + freeaddrinfo(servinfo); // all done with this structure + + if (p == NULL) { + handle_error("server: failed to bind"); + } + + if (listen(sockfd, BACKLOG) == -1) { + handle_error("listen"); + } + + printf("server: waiting for connections... (pid: %u)\n", getpid()); + + accept_fd = sockfd; + + return accept_fd; +} + +void EchoServerHandler::handle() +{ + int new_fd; // new connection on new_fd + struct sockaddr_storage their_addr; // connector's address information + socklen_t sin_size; + char s[INET6_ADDRSTRLEN]; + + sin_size = sizeof their_addr; + new_fd = accept(accept_fd, (struct sockaddr *)&their_addr, &sin_size); + if (new_fd == -1) { + handle_error("accept"); + } + + inet_ntop(their_addr.ss_family, + get_in_addr((struct sockaddr *)&their_addr), + s, sizeof s); + printf("server: got connection from %s (pid: %u)\n", s, getpid()); + + if (send(new_fd, "Hello, world!", 13, 0) == -1) + perror("send"); + close(new_fd); +} + diff --git a/EchoServerHandler.hh b/EchoServerHandler.hh new file mode 100644 index 0000000..9ee569d --- /dev/null +++ b/EchoServerHandler.hh @@ -0,0 +1,17 @@ +#pragma once + +#include "Handler.hh" + +class EchoServerHandler: public Handler +{ +public: + EchoServerHandler(); + + int init(); + + virtual void handle(); + +protected: + int accept_fd; +}; + diff --git a/Handler.hh b/Handler.hh new file mode 100644 index 0000000..8988996 --- /dev/null +++ b/Handler.hh @@ -0,0 +1,15 @@ +#pragma once + +class Handler +{ +public: + Handler(int fd) + : fd(fd) + {} + + virtual void handle() = 0; + +protected: + int fd; +}; + diff --git a/InotifyHandler.cc b/InotifyHandler.cc new file mode 100644 index 0000000..c603ba1 --- /dev/null +++ b/InotifyHandler.cc @@ -0,0 +1,124 @@ +#include "InotifyHandler.hh" +#include +#include +#include +#include +#include +#include + +#include "common.h" + +InotifyHandler::InotifyHandler(Parent* parent) +: Handler(-1), inotify_fd(-1), path(NULL), parent(parent), watched_dir_fd(-1), watched_dir(NULL) +{ +} + +int InotifyHandler::init(const char* path) +{ + inotify_fd = inotify_init1(IN_NONBLOCK | IN_CLOEXEC); + if (inotify_fd == -1) + handle_error("inotify_init1"); + + watch_descriptor = inotify_add_watch(inotify_fd, path, IN_CREATE | IN_ISDIR); + if (watch_descriptor == -1) + handle_error("inotify_add_watch"); + + watched_dir = opendir(path); + if (watched_dir == NULL) + handle_error("opendir"); + + watched_dir_fd = dirfd(watched_dir); + if (watched_dir_fd < 0) + handle_error("open watched dir"); + + this->path = path; + + return inotify_fd; +} + +void InotifyHandler::handle() +{ + /* Some systems cannot read integer variables if they are not + properly aligned. On other systems, incorrect alignment may + decrease performance. Hence, the buffer used for reading from + the inotify file descriptor should have the same alignment as + struct inotify_event. */ + + char buf[8192] + __attribute__ ((aligned(__alignof__(struct inotify_event)))); + const struct inotify_event *event; + ssize_t len; + char *ptr; + + /* Loop while events can be read from inotify file descriptor. */ + + for (;;) { + + /* Read some events. */ + + len = read(inotify_fd, buf, sizeof buf); + if (len == -1 && errno != EAGAIN) { + handle_error("inotify read"); + } + + /* If the nonblocking read() found no events to read, then + it returns -1 with errno set to EAGAIN. In that case, + we exit the loop. */ + + if (len <= 0) + break; + + /* Loop over all events in the buffer */ + + for (ptr = buf; ptr < buf + len; + ptr += sizeof(struct inotify_event) + event->len) { + + event = (const struct inotify_event *) ptr; + + /* Print event type */ + + if (event->mask & IN_OPEN) + printf("IN_OPEN: "); + if (event->mask & IN_CLOSE_NOWRITE) + printf("IN_CLOSE_NOWRITE: "); + if (event->mask & IN_CLOSE_WRITE) + printf("IN_CLOSE_WRITE: "); + + /* Print the name of the watched directory */ + printf("%s/", path); + + /* Print the name of the file */ + + if (event->len) + printf("%s", event->name); + + + /* Print type of filesystem object */ + + if (event->mask & IN_ISDIR) + printf(" [directory]\n"); + else + printf(" [file]\n"); + + { + pid_t pid = 0; + int idx = 0; + int n_vals = sscanf(event->name, "child_%d_%d", &pid, &idx); + fprintf(stderr, "file: %s, n_vals:%d, pid: %d, idx:%d\n", event->name, n_vals, pid, idx); + if (n_vals == 2 && pid != 0 && idx != 0) { + time_t t = time(NULL); + struct tm *tm = localtime(&t); + printf("Child idx %d pid %d is dying: %s", idx, pid, asctime(tm)); + + unlinkat(watched_dir_fd, event->name, 0); + + parent->clear_child(pid); + if (!parent->is_exiting()) { + parent->respawn(idx); + } + } + } + } + } +} + diff --git a/InotifyHandler.hh b/InotifyHandler.hh new file mode 100644 index 0000000..5b2fde1 --- /dev/null +++ b/InotifyHandler.hh @@ -0,0 +1,25 @@ +#pragma once + +#include "Handler.hh" +#include "Parent.hh" + +#include + +class InotifyHandler: public Handler +{ +public: + InotifyHandler(Parent* parent); + + int init(const char* path); + + virtual void handle(); + +protected: + int inotify_fd; + int watch_descriptor; + const char* path; + Parent* parent; + int watched_dir_fd; + DIR* watched_dir; +}; + diff --git a/Parent.cc b/Parent.cc new file mode 100644 index 0000000..f3e6dad --- /dev/null +++ b/Parent.cc @@ -0,0 +1,185 @@ +#include "Parent.hh" +#include +#include +#include +#include +#include +#include +#include + +#include "mmaper.h" +#include "common.h" +#include "child.h" + +#include "Handler.hh" +#include "ParentSignalHandler.hh" +#include "ChildSignalHandler.hh" +#include "InotifyHandler.hh" +#include "EchoServerHandler.hh" + +#define MAX_EVENTS MAX_CHILDREN + +int Parent::get_child_idx(pid_t pid) +{ + int res = -1; + + for (int i = 0; i < MAX_CHILDREN; ++i) { + if (children[i] == pid) { + res = i; + break; + } + } + + assert(res != -1); + + return res; +} + +int Parent::find_empty_child_idx(void) +{ + int res = -1; + + for (int i = 0; i < MAX_CHILDREN; ++i) { + if (children[i] == 0) { + res = i; + break; + } + } + + assert(res != -1); + + return res; +} + +void Parent::clear_child(pid_t pid) +{ + children[get_child_idx(pid)] = 0; + --n_children; +} + +void Parent::notify_children(int sig) +{ + for (int i = 0; i < MAX_CHILDREN; ++i) { + if (children[i] != 0) { + fprintf(stderr, "%d: Sending signal to %d\n", getpid(), children[i]); + kill(children[i], sig); + } + } +} + +void Parent::add_new_child(pid_t pid) +{ + children[find_empty_child_idx()] = pid; +} + +void Parent::set_new_child(int child_idx, pid_t pid) +{ + assert(children[child_idx] == 0); + + children[child_idx] = pid; +} + +void Parent::child_fn(int idx, bool respawned) +{ + map_memory(idx, respawned); + set_child_idx(idx); + run_epoll(0); +} + +void Parent::respawn(int child_idx) +{ + pid_t pid = fork(); + if (pid == -1) + handle_error("fork respawn"); + + if (pid) { + fprintf(stderr, "Respawned %d\n", pid); + set_new_child(child_idx, pid); + ++n_children; + } else { + child_fn(child_idx, true); + } +} + +void Parent::do_forks(int num) +{ + pid_t pid; + int i; + int idx; + + for (i = 0; i < num; ++i) { + idx = find_empty_child_idx(); + pid = fork(); + if (pid == -1) + handle_error("fork"); + + if (pid) { + fprintf(stderr, "Forked %d\n", pid); + set_new_child(idx, pid); + ++n_children; + } else { + child_fn(idx, false); + } + } +} + +void Parent::run_epoll(int is_parent) +{ + struct epoll_event ev, events[MAX_EVENTS]; + int nfds, n; + + int epollfd = epoll_create1(EPOLL_CLOEXEC); + if (epollfd == -1) { + handle_error("epoll_create1"); + } + + ev.events = EPOLLIN; + if (is_parent) { + struct epoll_event inotify_ev; + + ev.data.ptr = new ParentSignalHandler(sfd, this); + set_epoll_fd(epollfd); + + InotifyHandler *ino_handler = new InotifyHandler(this); + + inotify_ev.events = EPOLLIN; + inotify_ev.data.ptr = ino_handler; + + int inotify_fd = ino_handler->init("/tmp"); + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, inotify_fd, &inotify_ev) == -1) { + handle_error("epoll_ctl: inotify_fd"); + } + } else { + ev.data.ptr = new ChildSignalHandler(sfd); + + struct epoll_event accept_ev; + + EchoServerHandler *echo_handler = new EchoServerHandler(); + + accept_ev.events = EPOLLIN; + accept_ev.data.ptr = echo_handler; + + int accept_fd = echo_handler->init(); + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, accept_fd, &accept_ev) == -1) { + handle_error("epoll_ctl: accept_fd"); + } + } + + if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sfd, &ev) == -1) { + handle_error("epoll_ctl: signalfd"); + } + + for (;;) { + nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); + if (nfds == -1) { + handle_error("epoll_wait"); + } + + for (n = 0; n < nfds; ++n) { + Handler *handler = (Handler*)events[n].data.ptr; + handler->handle(); + } + } +} diff --git a/Parent.hh b/Parent.hh new file mode 100644 index 0000000..9c45904 --- /dev/null +++ b/Parent.hh @@ -0,0 +1,46 @@ +#pragma once +#include +#include + +#define MAX_CHILDREN 2 + +class Parent +{ + friend class ParentSignalHandler; +public: + Parent() + : exiting(0), n_children(0), sfd(-1), epoll_fd(-1) + { + memset(children, 0, sizeof(children)); + } + + void set_sfd(int sfd) + { this->sfd = sfd; } + + void set_epoll_fd(int fd) + { epoll_fd = fd; } + + void notify_children(int sig); + int get_child_idx(pid_t pid); + int find_empty_child_idx(void); + void clear_child(pid_t pid); + void add_new_child(pid_t pid); + void do_forks(int num); + void respawn(int child_idx); + + void run_epoll(int is_parent); + void set_new_child(int child_idx, pid_t pid); + + bool is_exiting() + { return exiting == 1; } + +protected: + int exiting; + + pid_t children[MAX_CHILDREN]; + int n_children; + int sfd; + int epoll_fd; + + void child_fn(int idx, bool respawned); +}; diff --git a/ParentSignalHandler.cc b/ParentSignalHandler.cc new file mode 100644 index 0000000..18dabfd --- /dev/null +++ b/ParentSignalHandler.cc @@ -0,0 +1,58 @@ +#include "ParentSignalHandler.hh" +#include +#include +#include +#include +#include +#include +#include +#include +#include "common.h" + +void ParentSignalHandler::handle() +{ + struct signalfd_siginfo fdsi; + ssize_t sz; + pid_t pid; + int status; + + sz = read(fd, &fdsi, sizeof(struct signalfd_siginfo)); + if (sz != sizeof(struct signalfd_siginfo)) + handle_error("read parent signal handler"); + + if (fdsi.ssi_signo == SIGINT) { + fprintf(stderr, "%d: Got SIGINT from %d\n", getpid(), fdsi.ssi_pid); + parent->exiting = 1; + parent->notify_children(SIGQUIT); + } else if (fdsi.ssi_signo == SIGQUIT) { + fprintf(stderr, "%d: Got SIGQUIT from %d\n", getpid(), fdsi.ssi_pid); + parent->exiting = 1; + parent->notify_children(SIGQUIT); + } else if (fdsi.ssi_signo == SIGTERM) { + fprintf(stderr, "%d: Got SIGTERM from %d\n", getpid(), fdsi.ssi_pid); + parent->exiting = 1; + parent->notify_children(SIGTERM); + } else if (fdsi.ssi_signo == SIGCHLD) { + time_t t = time(NULL); + struct tm *tm = localtime(&t); + + fprintf(stderr, "%d: Got SIGCHLD from %d %s", getpid(), fdsi.ssi_pid, asctime(tm)); + do { + pid = waitpid(-1, &status, WNOHANG); + if (pid > 0) { + --parent->n_children; + fprintf(stderr, "%d: Process %d exited\n", getpid(), pid); + + if (parent->exiting) { + if (parent->n_children == 0) { + fprintf(stderr, "%d: All children exited\n", getpid()); + exit(EXIT_SUCCESS); + } + } + } + } while (pid > 0); + } else { + fprintf(stderr, "%d: Read unexpected signal from %d\n", getpid(), fdsi.ssi_pid); + } +} + diff --git a/ParentSignalHandler.hh b/ParentSignalHandler.hh new file mode 100644 index 0000000..c69b571 --- /dev/null +++ b/ParentSignalHandler.hh @@ -0,0 +1,18 @@ +#pragma once + +#include "Handler.hh" +#include "Parent.hh" + +class ParentSignalHandler: public Handler +{ +public: + ParentSignalHandler(int fd, Parent* parent) + : Handler(fd), parent(parent) + {} + + virtual void handle(); + +protected: + Parent* parent; +}; + diff --git a/child.c b/child.c new file mode 100644 index 0000000..80965f7 --- /dev/null +++ b/child.c @@ -0,0 +1,51 @@ +#include +#include +#include +#include +#include + +#include "common.h" +#include "mmaper.h" + +static int child_idx; + +void write_child_exit_info() +{ + char path[PATH_MAX]; + pid_t pid = getpid(); + + fprintf(stderr, "Child idx %d pid: %d got signal\n", child_idx, pid); + + snprintf(path, sizeof(path), "/tmp/child_%d_%d", pid, child_idx); + int fd = creat(path, S_IWUSR | S_IRUSR); + if (fd < 0) + handle_error("creat in signal handler"); + close(fd); +} + +static void signal_handler(int sig, siginfo_t *si, void *arg) +{ + map_remap_private(child_idx); + + write_child_exit_info(); + + kill(getpid(), sig); +} + +typedef void (*sighandler_t)(int); + +void set_child_idx(int idx) +{ + struct sigaction act; + + memset(&act, 0, sizeof(sigaction)); + sigemptyset(&act.sa_mask); + act.sa_flags = (int)(SA_NODEFER | SA_RESETHAND | SA_SIGINFO); + act.sa_sigaction = signal_handler; + + sigaction(SIGSEGV, &act, NULL); + + child_idx = idx; + +} + diff --git a/child.h b/child.h new file mode 100644 index 0000000..3417218 --- /dev/null +++ b/child.h @@ -0,0 +1,5 @@ +#pragma once + +extern "C" void set_child_idx(int idx); +extern "C" void write_child_exit_info(); + diff --git a/client.c b/client.c new file mode 100644 index 0000000..34094e0 --- /dev/null +++ b/client.c @@ -0,0 +1,94 @@ +/* +** client.c -- a stream socket client demo +*/ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#define PORT "3490" // the port client will be connecting to + +#define MAXDATASIZE 100 // max number of bytes we can get at once + +// get sockaddr, IPv4 or IPv6: +void *get_in_addr(struct sockaddr *sa) +{ + if (sa->sa_family == AF_INET) { + return &(((struct sockaddr_in*)sa)->sin_addr); + } + + return &(((struct sockaddr_in6*)sa)->sin6_addr); +} + +int main(int argc, char *argv[]) +{ + int sockfd, numbytes; + char buf[MAXDATASIZE]; + struct addrinfo hints, *servinfo, *p; + int rv; + char s[INET6_ADDRSTRLEN]; + + if (argc != 2) { + fprintf(stderr,"usage: client hostname\n"); + exit(1); + } + + memset(&hints, 0, sizeof hints); + hints.ai_family = AF_UNSPEC; + hints.ai_socktype = SOCK_STREAM; + + if ((rv = getaddrinfo(argv[1], PORT, &hints, &servinfo)) != 0) { + fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(rv)); + return 1; + } + + // loop through all the results and connect to the first we can + for(p = servinfo; p != NULL; p = p->ai_next) { + if ((sockfd = socket(p->ai_family, p->ai_socktype, + p->ai_protocol)) == -1) { + perror("client: socket"); + continue; + } + + if (connect(sockfd, p->ai_addr, p->ai_addrlen) == -1) { + perror("client: connect"); + close(sockfd); + continue; + } + + break; + } + + if (p == NULL) { + fprintf(stderr, "client: failed to connect\n"); + return 2; + } + + inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), + s, sizeof s); + printf("client: connecting to %s\n", s); + + freeaddrinfo(servinfo); // all done with this structure + + if ((numbytes = recv(sockfd, buf, MAXDATASIZE-1, 0)) == -1) { + perror("recv"); + exit(1); + } + + buf[numbytes] = '\0'; + + printf("client: received '%s'\n",buf); + + close(sockfd); + + return 0; +} + diff --git a/common.h b/common.h new file mode 100644 index 0000000..f87aa70 --- /dev/null +++ b/common.h @@ -0,0 +1,13 @@ +#pragma once +#include +#include +#include +#include + +#define handle_error(msg) \ + do { \ + perror(msg); \ + exit(EXIT_FAILURE); \ + } while (0) + + diff --git a/forker.c b/forker.c deleted file mode 100644 index 741f7bb..0000000 --- a/forker.c +++ /dev/null @@ -1,197 +0,0 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#define handle_error(msg) \ - do { \ - perror(msg); \ - exit(EXIT_FAILURE); \ - } while (0) - -#define MAX_CHILDREN 10 - -static pid_t children[MAX_CHILDREN]; -static int n_children; - -static int get_child_idx(pid_t pid) -{ - int res = -1; - - for (int i = 0; i < MAX_CHILDREN; ++i) { - if (children[i] == pid) { - res = i; - break; - } - } - - assert(res != -1); - - return res; -} - -static int find_empty_child_idx(void) -{ - int res = -1; - - for (int i = 0; i < MAX_CHILDREN; ++i) { - if (children[i] == 0) { - res = i; - break; - } - } - - assert(res != -1); - - return res; -} - -static void clear_child(pid_t pid) -{ - children[get_child_idx(pid)] = 0; - --n_children; -} - -#define MAX_EVENTS MAX_CHILDREN - -void run_epoll(int sfd, int parent); - -static void do_forks(int num, int sfd) -{ - pid_t pid; - int i; - - for (i = 0; i < num; ++i) { - pid = fork(); - if (pid == -1) - handle_error("fork"); - - if (pid) { - fprintf(stderr, "Forked %d\n", pid); - children[find_empty_child_idx()] = pid; - ++n_children; - } else { - run_epoll(sfd, 0); - } - } -} - -static int exiting; - -static void notify_children(int sig) -{ - for (int i = 0; i < MAX_CHILDREN; ++i) { - if (children[i] != 0) { - fprintf(stderr, "%d: Sending signal to %d\n", getpid(), children[i]); - kill(children[i], sig); - } - } -} - -void run_epoll(int sfd, int parent) -{ - struct epoll_event ev, events[MAX_EVENTS]; - int nfds, n, status; - pid_t pid; - struct signalfd_siginfo fdsi; - ssize_t sz; - - int epollfd = epoll_create1(EPOLL_CLOEXEC); - if (epollfd == -1) { - handle_error("epoll_create1"); - } - - ev.events = EPOLLIN; - ev.data.fd = sfd; - if (epoll_ctl(epollfd, EPOLL_CTL_ADD, sfd, &ev) == -1) { - handle_error("epoll_ctl: signalfd"); - } - - for (;;) { - nfds = epoll_wait(epollfd, events, MAX_EVENTS, -1); - if (nfds == -1) { - handle_error("epoll_wait"); - } - - for (n = 0; n < nfds; ++n) { - sz = read(events[n].data.fd, &fdsi, sizeof(struct signalfd_siginfo)); - if (sz != sizeof(struct signalfd_siginfo)) - handle_error("read"); - - if (fdsi.ssi_signo == SIGINT) { - fprintf(stderr, "%d: Got SIGINT from %d\n", getpid(), fdsi.ssi_pid); - } else if (fdsi.ssi_signo == SIGQUIT) { - fprintf(stderr, "%d: Got SIGQUIT from %d\n", getpid(), fdsi.ssi_pid); - if (parent) { - exiting = 1; - notify_children(SIGQUIT); - } else { - exit(EXIT_SUCCESS); - } - } else if (fdsi.ssi_signo == SIGTERM) { - fprintf(stderr, "%d: Got SIGTERM from %d\n", getpid(), fdsi.ssi_pid); - if (parent) { - exiting = 1; - notify_children(SIGTERM); - } else { - exit(EXIT_SUCCESS); - } - } else if (fdsi.ssi_signo == SIGCHLD) { - fprintf(stderr, "%d: Got SIGCHLD from %d\n", getpid(), fdsi.ssi_pid); - do { - pid = waitpid(-1, &status, WNOHANG); - if (pid > 0) { - fprintf(stderr, "%d: Process %d exited\n", getpid(), pid); - clear_child(pid); - - if (!exiting) { - do_forks(1, sfd); - } else { - if (n_children == 0) { - fprintf(stderr, "%d: All children exited\n", getpid()); - exit(EXIT_SUCCESS); - } - } - } - } while (pid > 0); - } else { - fprintf(stderr, "%d: Read unexpected signal from %d\n", getpid(), fdsi.ssi_pid); - } - } - } -} - -int main(int argc, char *argv[]) -{ - sigset_t mask; - int sfd, epollfd; - struct epoll_event ev; - - sigemptyset(&mask); - sigaddset(&mask, SIGINT); - sigaddset(&mask, SIGTERM); - sigaddset(&mask, SIGQUIT); - sigaddset(&mask, SIGCHLD); - - /* Block signals so that they aren't handled - according to their default dispositions */ - - if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) - handle_error("sigprocmask"); - - sfd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK); - if (sfd == -1) - handle_error("signalfd"); - - // forking after epoll created leades to world of pain - do_forks(MAX_CHILDREN, sfd); - - run_epoll(sfd, 1); - - return EXIT_SUCCESS; -} diff --git a/forker.cc b/forker.cc new file mode 100644 index 0000000..47c8dba --- /dev/null +++ b/forker.cc @@ -0,0 +1,45 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "Parent.hh" + + +static Parent parent; + +int main(int argc, char *argv[]) +{ + sigset_t mask; + int sfd; + + sigemptyset(&mask); + sigaddset(&mask, SIGINT); + sigaddset(&mask, SIGTERM); + sigaddset(&mask, SIGQUIT); + sigaddset(&mask, SIGCHLD); + + /* Block signals so that they aren't handled + according to their default dispositions */ + + if (sigprocmask(SIG_BLOCK, &mask, NULL) == -1) + handle_error("sigprocmask"); + + sfd = signalfd(-1, &mask, SFD_CLOEXEC | SFD_NONBLOCK); + if (sfd == -1) + handle_error("signalfd"); + + parent.set_sfd(sfd); + + // forking after epoll created leades to world of pain + parent.do_forks(MAX_CHILDREN); + + parent.run_epoll(sfd); + + return EXIT_SUCCESS; +} diff --git a/mmaper.c b/mmaper.c new file mode 100644 index 0000000..a97c4cb --- /dev/null +++ b/mmaper.c @@ -0,0 +1,111 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "common.h" +#include "mmaper.h" + +static void * mmap_addr; +static int g_idx; + +#define FILESZ (5L * 1024L * 1024L * 1024L) +//#define FILESZ (30L * 1024L * 1024L * 1024L) +//#define FILESZ (30L * 1024L) + +#define CORE_FILTER "0x3f" + +int allow_mmap_in_core() +{ + int fd = open("/proc/self/coredump_filter", O_WRONLY); + if (fd < 0) + handle_error("open"); + + ssize_t written = write(fd, CORE_FILTER, sizeof(CORE_FILTER)); + if (written != sizeof(CORE_FILTER)) + handle_error("write"); + + close(fd); + return 0; +} + +static void fill_filename(char* fname, size_t sz, int idx) +{ + snprintf(fname, sz, "/tmp/mmaped_file_%d", idx); +} + +void map_remap_private(int idx) +{ + char fname[PATH_MAX]; + fill_filename(fname, sizeof(fname), idx); + int fd = open(fname, O_CREAT | O_RDWR, S_IWUSR | S_IRUSR); + if (fd < 0) + handle_error("open"); + + fprintf(stderr, "Starting remap: 0x%p\n", mmap_addr); + + int res = munmap(mmap_addr, FILESZ); + if (res) + handle_error("munmap"); + + void* new_map = mmap(mmap_addr, FILESZ, PROT_READ, MAP_PRIVATE | MAP_FIXED, fd, 0); + if (new_map == MAP_FAILED) + handle_error("mmap private"); + + fprintf(stderr, "Ending remap: 0x%p\n", new_map); + + assert(new_map == mmap_addr); + + close(fd); +} + +int map_memory(int idx, int respawned) +{ + static char template[] = "/tmp/myfileXXXXXX"; + int res; + + g_idx = idx; + + allow_mmap_in_core(); + +#if 0 + int fd = mkstemp(template); + if (fd < 0) + handle_error("mkstemp"); +#endif + + char fname[PATH_MAX]; + fill_filename(fname, sizeof(fname), idx); + int fd = open(fname, O_CREAT | O_RDWR, S_IWUSR | S_IRUSR); + if (fd < 0) + handle_error("open"); + + res = ftruncate(fd, FILESZ); + if (res) + handle_error("ftruncate"); + +#if 1 + res = posix_fallocate(fd, 0, FILESZ); + if (res) + handle_error("posix_fallocate"); +#endif + + mmap_addr = mmap(NULL, FILESZ, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0); + if (mmap_addr == MAP_FAILED) + handle_error("mmap"); + + close(fd); + + if (!respawned) + memset(mmap_addr, 'S', FILESZ); + else + memset(mmap_addr, 'E', FILESZ); + + return 0; +} + diff --git a/mmaper.h b/mmaper.h new file mode 100644 index 0000000..032520b --- /dev/null +++ b/mmaper.h @@ -0,0 +1,11 @@ +#pragma once + +#ifdef __cplusplus +#define EXTERN extern "C" +#else +#define EXTERN +#endif + +EXTERN int map_memory(int idx, int respawned); +EXTERN void map_remap_private(int idx); +