]> mj.ucw.cz Git - misc.git/commitdiff
Added a simple preforking server.
authorMartin Mares <mj@ucw.cz>
Wed, 5 Sep 2007 15:47:03 +0000 (17:47 +0200)
committerMartin Mares <mj@ucw.cz>
Wed, 5 Sep 2007 15:47:03 +0000 (17:47 +0200)
Makefile
prefork.c [new file with mode: 0644]

index ae05d46715b2e094cad476ae11b98317805a52bb..c806aa287969e6c7dd6ed10106db84acacadd71a 100644 (file)
--- 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 (file)
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 <stdio.h>
+#include <stdarg.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <sys/wait.h>
+#include <sys/socket.h>
+#include <netinet/in.h>
+
+#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--;
+    }
+}