From: Martin Mares Date: Wed, 5 Sep 2007 15:47:03 +0000 (+0200) Subject: Added a simple preforking server. X-Git-Url: http://mj.ucw.cz/gitweb/?a=commitdiff_plain;h=8e4914883fe0e2e7ca12d832e6e97b466b8333df;p=misc.git Added a simple preforking server. --- diff --git a/Makefile b/Makefile index ae05d46..c806aa2 100644 --- a/Makefile +++ b/Makefile @@ -9,6 +9,7 @@ all: parrot: parrot.c xclipcat: xclipcat.c xclipsend: xclipsend.c +prefork: prefork.c batt: CFLAGS+=$(shell xosd-config --cflags) batt: LDFLAGS+=$(shell xosd-config --libs) diff --git a/prefork.c b/prefork.c new file mode 100644 index 0000000..5385fac --- /dev/null +++ b/prefork.c @@ -0,0 +1,123 @@ +/* + * A simple preforking daemon, inspired by the logic in Apache 1.3 + * Martin Mares, September 2007 + */ + +#include +#include +#include +#include +#include +#include +#include + +#define NUM_CHILDREN 10 + +static int listening_sk; + +/*** Logging functions ***/ + +static void +vmsg(char *msg, va_list args) +{ + /* + * This is a little bit tricky, because we need to be sure that logging + * is atomic with respect to other processes. Fortunately, it is sufficient + * to prepare everything in a buffer and flush it with a single write(). + */ + char buf[1024]; + int cnt = vsnprintf(buf, sizeof(buf)-1, msg, args); + buf[cnt++] = '\n'; + write(2, buf, cnt); +} + +static void +msg(char *msg, ...) +{ + va_list args; + va_start(args, msg); + vmsg(msg, args); + va_end(args); +} + +static void +die(char *msg, ...) +{ + va_list args; + va_start(args, msg); + vmsg(msg, args); + va_end(args); + exit(1); +} + +/*** Child process ***/ + +static void +child(void) +{ + int count = 0; + for (;;) + { + struct sockaddr_in sa; + socklen_t len = sizeof(sa); + int sk = accept(listening_sk, (struct sockaddr *) &sa, &len); + if (sk < 0) + die("Accept failed: %m"); + + /* Handle the connection somehow */ + write(sk, "Hello, world!\n", 14); + close(sk); + + /* After processing a certain number of requests, exit. This makes small memory leaks harmless. */ + if (count++ >= 100) + exit(0); + } +} + +/*** Master process ***/ + +int +main(void) +{ + /* Set up the listening socket */ + int one = 1; + listening_sk = socket(PF_INET, SOCK_STREAM, IPPROTO_TCP); + if (listening_sk < 0) + die("Cannot create listening socket: %m"); + if (setsockopt(listening_sk, SOL_SOCKET, SO_REUSEADDR, &one, sizeof(one)) < 0) + die("Cannot set SO_REUSEADDR: %m"); + struct sockaddr_in sa = { .sin_family = AF_INET, .sin_port = htons(9999) }; + if (bind(listening_sk, (struct sockaddr *) &sa, sizeof(sa)) < 0) + die("Cannot bind: %m"); + if (listen(listening_sk, 256) < 0) + die("Cannot listen: %m"); + + /* Main loop */ + pid_t pid; + int children = 0; + int status; + for (;;) + { + if (children < NUM_CHILDREN) + { + pid = fork(); + if (pid < 0) + { + /* Temporary resource shortage, better sleep for a while */ + msg("Fork failed: %m"); + sleep(1); + } + else if (pid) + children++; + else + child(); + continue; + } + pid = wait(&status); + if (pid < 0) + die("Wait failed: %m"); + if (!(WIFEXITED(status) && !WEXITSTATUS(status))) + msg("Child %d exited with status %x", (int)pid, status); + children--; + } +}