#include #include #include #include #include #include #include #include #include #include #include #include #include /* gcc -W -Wall -pedantic --std=c99 -O2 -D_XOPEN_SOURCE=500 -D_POSIX_C_SOURCE=199309 */ struct options { unsigned fork_delay, proc_delay, nr_readers, nr_writers; }; static int get_options(struct options *, int, char *[]); static int daemonize(void); static int spawn_children(struct options *, pid_t **); static void reader(unsigned); static void writer(unsigned); static void kill_children(struct options *, pid_t *); static int reap_children(struct options *, pid_t **); int main(int argc, char *argv[]) { pid_t *children; struct options options = { .nr_readers = 1, .nr_writers = 1, .fork_delay = 100000, .proc_delay = 250000, }; if (get_options(&options, argc, argv) != 0) return EXIT_FAILURE; if (daemonize() != 0) return EXIT_FAILURE; if (spawn_children(&options, &children) != 0) kill_children(&options, children); if (reap_children(&options, &children) != 0) return EXIT_FAILURE; return EXIT_SUCCESS; } static int get_options(struct options *options, int argc, char *argv[]) { int c; long n; while ((c = getopt(argc, argv, "p:f:r:w:")) != -1) { if (optarg == NULL) return -1; n = strtol(optarg, NULL, 0); if (n == LONG_MAX || n < 0) return -1; switch (c) { case 'p': options->proc_delay = n; break; case 'f': options->fork_delay = n; break; case 'r': options->nr_readers = n; break; case 'w': options->nr_writers = n; break; default: return -1; } } return 0; } static int daemonize(void) { int fd; for (fd = 0; fd < 1024; ++fd) { close(fd); errno = 0; } fd = open("/dev/null", 0); if (fd < 0) return -1; if (dup2(fd, STDIN_FILENO) != STDIN_FILENO) return -1; if (dup2(fd, STDOUT_FILENO) != STDOUT_FILENO) return -1; if (dup2(fd, STDERR_FILENO) != STDERR_FILENO) return -1; if (close(fd) != 0) return -1; for (;;) { pid_t pid; pid = fork(); if (pid == 0) break; else if (pid > 0) exit(EXIT_SUCCESS); else if (errno != EAGAIN) return -1; } if (setsid() != getpid()) return -1; if (chdir("/") != 0) return -1; umask(0); return 0; } static int spawn_children(struct options *options, pid_t **pids) { unsigned i, children = options->nr_readers + options->nr_writers; *pids = calloc(children, sizeof(pid_t)); if (*pids == NULL) return -1; memset(*pids, 0, children*sizeof(pid_t)); for (i = 0; i < options->nr_readers; ++i) { for (;;) { (*pids)[i] = fork(); if ((*pids)[i] == 0) reader(options->proc_delay); else if ((*pids)[i] > 0) break; if (errno == EAGAIN) errno = 0; else return -1; } } for (i = options->nr_readers; i < children; ++i) { for (;;) { (*pids)[i] = fork(); if ((*pids)[i] == 0) writer(options->fork_delay); else if ((*pids)[i] > 0) break; if (errno == EAGAIN) errno = 0; else return -1; } } return 0; } void reader(unsigned delay) { DIR *dir; struct timespec ts = { .tv_sec = delay/1000000, .tv_nsec = (delay % 1000000)*1000, }; dir = opendir("/proc/"); if (dir == NULL) exit(EXIT_FAILURE); for (;;) { struct dirent *de; do { de = readdir(dir); } while (de != NULL); if (errno != 0) exit(EXIT_FAILURE); rewinddir(dir); if (nanosleep(&ts, NULL) != 0) exit(EXIT_FAILURE); } } void writer(unsigned delay) { struct timespec ts = { .tv_sec = delay/1000000, .tv_nsec = (delay % 1000000)*1000, }; for (;;) { pid_t pid; pid = fork(); if (pid == 0) exit(EXIT_SUCCESS); else if (pid < 0) { if (errno != EAGAIN) exit(EXIT_FAILURE); else errno = 0; } else { if (waitpid(pid, NULL, 0) != pid) exit(EXIT_FAILURE); if (nanosleep(&ts, NULL) != 0) exit(EXIT_FAILURE); } } } static int reap_children(struct options *options, pid_t **pids) { unsigned total, nr_left, i; total = nr_left = options->nr_readers + options->nr_writers; while (nr_left) { pid_t pid; pid = wait(NULL); if (pid <= 0) return -1; for (i = 0; i < total; ++i) { if ((*pids)[i] == pid) { (*pids)[i] = 0; --nr_left; break; } } if (i == total) return -1; } free(*pids); *pids = NULL; return 0; } static void kill_children(struct options *options, pid_t *pids) { unsigned i; for (i = 0; i < options->nr_readers + options->nr_writers; ++i) { kill(pids[i], SIGKILL); errno = 0; } }