--- /dev/null
+#include "config.h"
+
+/*
+ * On some systems, FD_SETSIZE is set to something lower than the
+ * actual number of files which can be opened. IRIX is one case,
+ * NetBSD is another. So here we increase FD_SETSIZE to our
+ * configure-discovered maximum *before* any system includes.
+ */
+#define CHANGE_FD_SETSIZE 1
+
+/* Cannot increase FD_SETSIZE on Linux */
+#if defined(_SQUID_LINUX_)
+#undef CHANGE_FD_SETSIZE
+#define CHANGE_FD_SETSIZE 0
+#endif
+
+/* Cannot increase FD_SETSIZE on FreeBSD before 2.2.0, causes select(2)
+ * to return EINVAL. */
+/* Marian Durkovic <marian@svf.stuba.sk> */
+/* Peter Wemm <peter@spinner.DIALix.COM> */
+#if defined(_SQUID_FREEBSD_)
+#include <osreldate.h>
+#if __FreeBSD_version < 220000
+#undef CHANGE_FD_SETSIZE
+#define CHANGE_FD_SETSIZE 0
+#endif
+#endif
+
+/* Increase FD_SETSIZE if SQUID_MAXFD is bigger */
+#if CHANGE_FD_SETSIZE && SQUID_MAXFD > DEFAULT_FD_SETSIZE
+#define FD_SETSIZE SQUID_MAXFD
+#endif
+
+#if HAVE_UNISTD_H
+#include <unistd.h>
+#endif
+#if HAVE_STDLIB_H
+#include <stdlib.h>
+#endif
+#if HAVE_STDIO_H
+#include <stdio.h>
+#endif
+#if HAVE_FCNTL_H
+#include <fcntl.h>
+#endif
+#ifdef HAVE_STRING_H
+#include <string.h>
+#endif
+#ifdef HAVE_STRINGS_H
+#include <strings.h>
+#endif
+#if HAVE_SYS_TYPES_H
+#include <sys/types.h>
+#endif
+#if HAVE_SYS_SELECT_H
+#include <sys/select.h>
+#endif
+#if HAVE_SIGNAL_H
+#include <signal.h>
+#endif
+#if HAVE_TIME_H
+#include <time.h>
+#endif
+#if HAVE_SYS_TIME_H
+#include <sys/time.h>
+#endif
+#if HAVE_SYS_SOCKET_H
+#include <sys/socket.h>
+#endif
+#if HAVE_NETINET_IN_H
+#include <netinet/in.h>
+#endif
+#if HAVE_ARPA_INET_H
+#include <arpa/inet.h>
+#endif
+#if HAVE_ERRNO_H
+#include <errno.h>
+#endif
+#if HAVE_SYS_STAT_H
+#include <sys/stat.h>
+#endif
+#if HAVE_ASSERT_H
+#include <assert.h>
+#endif
+#include <netdb.h>
+#include <sys/wait.h>
+
+#define READ_BUF_SZ 4096
+#define URL_BUF_SZ 4096
+
+struct _thing {
+ int rfd;
+ int wfd;
+ int state;
+ pid_t pid;
+ char url[URL_BUF_SZ];
+ struct _thing *next;
+};
+typedef struct _thing thing;
+
+static thing *things = NULL;
+static fd_set R1;
+static int maxfd = 0;
+static struct timeval now;
+#if DEBUG
+static int debug = 1;
+#else
+static int debug = 0;
+#endif
+
+static int
+get_url(const char *url)
+{
+ char host[URL_BUF_SZ];
+ char path[URL_BUF_SZ];
+ char request[URL_BUF_SZ];
+ char reply[READ_BUF_SZ];
+ char *t;
+ int x;
+ int s;
+ int nr = 0;
+ struct hostent *h;
+ struct sockaddr_in S;
+ unsigned short port = 80;
+ assert(!strncmp(url, "http://", 7));
+ strncpy(host, url + 7, URL_BUF_SZ);
+ if ((t = strchr(host, '/')))
+ *t = '\0';
+ if ((t = strchr(host, ':'))) {
+ *t = '\0';
+ port = (unsigned short) atoi(t + 1);
+ }
+ if ((int) port != 80)
+ return 0;
+ t = strchr(url + 7, '/');
+ strncpy(path, (t ? t : "/"), URL_BUF_SZ);
+ memset(&S, '\0', sizeof(S));
+ h = gethostbyname(host);
+ if (!h)
+ return 0;
+ memcpy(&S.sin_addr.s_addr, h->h_addr_list[0], sizeof(S.sin_addr.s_addr));
+ S.sin_port = htons(port);
+ S.sin_family = AF_INET;
+ if (debug)
+ fprintf(stderr, "%s (%s) %d %s\n", host, inet_ntoa(S.sin_addr), (int) port, path);
+ s = socket(PF_INET, SOCK_STREAM, 0);
+ x = connect(s, (struct sockaddr *) &S, sizeof(S));
+ if (x < 0) {
+ perror(host);
+ return 0;
+ }
+ snprintf(request, URL_BUF_SZ,
+ "GET %s HTTP/1.1\r\n"
+ "Accept: */*\r\n"
+ "Host: %s\r\n"
+ "Connection: close\r\n"
+ "\r\n",
+ path,
+ host);
+ x = write(s, request, strlen(request));
+ if (x < 0) {
+ perror("write");
+ return 0;
+ }
+ do {
+ x = read(s, reply, READ_BUF_SZ);
+ if (x > 0)
+ nr += x;
+ } while (x > 0);
+ close(s);
+ return nr;
+}
+
+static void
+child_main_loop(void)
+{
+ char buf[URL_BUF_SZ];
+ char *t;
+ int n;
+ if (debug)
+ fprintf(stderr, "Child PID %d entering child_main_loop\n", (int) getpid());
+ setbuf(stdin, NULL);
+ setbuf(stdout, NULL);
+ setbuf(stderr, NULL);
+ while (fgets(buf, URL_BUF_SZ, stdin)) {
+ t = strchr(buf, '\n');
+ if (t == NULL)
+ continue;
+ *t = '\0';
+ if (strncmp(buf, "http://", 7))
+ continue;
+ n = get_url(buf);
+ printf ("%d\n", n);
+ }
+}
+
+static thing *
+create_a_thing(char *argv[])
+{
+ int p2c[2];
+ int c2p[2];
+ int prfd, pwfd, crfd, cwfd;
+ pid_t pid;
+ thing *t;
+ if (pipe(p2c) < 0)
+ abort();
+ if (pipe(c2p) < 0)
+ abort();
+ prfd = p2c[0];
+ cwfd = p2c[1];
+ crfd = c2p[0];
+ pwfd = c2p[1];
+ if ((pid = fork()) < 0)
+ abort();
+ if (pid > 0) { /* parent */
+ /* close shared socket with child */
+ close(crfd);
+ close(cwfd);
+ t = calloc(1, sizeof(*t));
+ t->wfd = pwfd;
+ t->rfd = prfd;
+ if (pwfd > maxfd)
+ maxfd = pwfd;
+ if (prfd > maxfd)
+ maxfd = prfd;
+ t->pid = pid;
+ return t;
+ }
+ /* child */
+ close(prfd);
+ close(pwfd);
+ dup2(crfd, 0);
+ dup2(cwfd, 1);
+ close(crfd);
+ close(cwfd);
+ child_main_loop();
+ exit(0);
+}
+
+static void
+create_children(char *argv[])
+{
+ thing *t;
+ thing **T = &things;
+ int i;
+ for (i = 0; i < 20; i++) {
+ t = create_a_thing(argv);
+ assert(t);
+ if (debug)
+ fprintf(stderr, "Thing #%d on FD %d/%d\n",
+ i, t->rfd, t->wfd);
+ *T = t;
+ T = &t->next;
+ }
+}
+
+char *
+parent_read_url(void)
+{
+ static char buf[URL_BUF_SZ];
+ while (fgets(buf, URL_BUF_SZ, stdin)) {
+ if (strncmp(buf, "http://", 7))
+ continue;
+ return buf;
+ }
+ return NULL;
+}
+
+static thing *
+get_idle_thing(void)
+{
+ thing *t;
+ thing *n = things;
+ while ((t = n)) {
+ n = t->next;
+ if (t->state == 0)
+ break;
+ }
+ return t;
+}
+
+static void
+dispatch(thing * t, char *url)
+{
+ int x;
+ char *s;
+ assert(t->state == 0);
+ x = write(t->wfd, url, strlen(url));
+ if (x < 0)
+ perror("write");
+ if (debug)
+ fprintf(stderr, "dispatched URL to thing PID %d, %d bytes\n", (int) t->pid, x);
+ strncpy(t->url, url, URL_BUF_SZ);
+ if ((s = strchr(t->url, '\n')))
+ *s = '\0';
+ t->state = 1;
+ FD_SET(t->rfd, &R1);
+}
+
+static void
+read_reply(thing * t)
+{
+ char buf[128];
+ int i;
+ int x;
+ x = read(t->rfd, buf, 128);
+ if (x < 0) {
+ perror("read");
+ } else {
+ i = atoi(buf);
+ gettimeofday(&now, NULL);
+ printf("%d.%06d %9d %s\n", (int) now.tv_sec, (int) now.tv_usec, i, t->url);
+ }
+ t->state = 0;
+ FD_CLR(t->rfd, &R1);
+}
+
+static void
+parent_main_loop(void)
+{
+ thing *t;
+ char *url;
+ fd_set R2;
+ int x;
+ FD_ZERO(&R1);
+ for (;;) {
+ while ((t = get_idle_thing()) && (url = parent_read_url()))
+ dispatch(t, url);
+ R2 = R1;
+ x = select(maxfd + 1, &R2, NULL, NULL, NULL);
+ if (x < 0) {
+ perror("select");
+ continue;
+ }
+ for (t = things; t; t = t->next) {
+ if (t->state != 1)
+ continue;
+ if (!FD_ISSET(t->rfd, &R2))
+ continue;
+ read_reply(t);
+ }
+ }
+}
+
+static void
+sig_child(int sig)
+{
+ int status;
+ pid_t pid;
+ do {
+ pid = waitpid(-1, &status, WNOHANG);
+ } while (pid > 0 || (pid < 0 && errno == EINTR));
+ signal(sig, sig_child);
+}
+
+
+int
+main(int argc, char *argv[])
+{
+ signal(SIGCHLD, sig_child);
+ create_children(argv);
+ parent_main_loop();
+ return 0;
+}