#define _GNU_SOURCE
#include <signal.h>
#include <pthread.h>
#include <unistd.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <strings.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netdb.h>
#include <syslog.h>
#include <stdarg.h>
#include <sys/resource.h>
#define LISTENQ 1024 /* 2nd argument to listen() */
#define MAXLINE 4096 /* max text line length */
#define MAXN 16384 /* max # bytes to request from server */
typedef void Sigfunc(int);
int daemon_proc;
void err_doit(int errnoflag, int level, const char *fmt, va_list ap)
{
int errno_save, n;
char buf[MAXLINE + 1];
errno_save = errno;
#ifdef HAVE_VSNPRINTF
vsnprintf(buf, MAXLINE, fmt, ap);
#else
vsprintf(buf, fmt, ap);
#endif
n = strlen(buf);
if (errnoflag)
snprintf(buf + n, MAXLINE - n, ": %s", strerror(errno_save));
strcat(buf, "\n");
if (daemon_proc) {
syslog(level, "%s", buf);
} else {
fflush(stdout);
fputs(buf, stderr);
fflush(stderr);
}
return;
}
void err_quit(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(0, LOG_ERR, fmt, ap);
va_end(ap);
exit(1);
}
void err_sys(const char *fmt, ...)
{
va_list ap;
va_start(ap, fmt);
err_doit(1, LOG_ERR, fmt, ap);
va_end(ap);
exit(1);
}
void pr_cpu_time(void)
{
double user, sys;
struct rusage myusage, childusage;
if (getrusage(RUSAGE_SELF, &myusage) < 0)
err_sys("getrusage error");
if (getrusage(RUSAGE_CHILDREN, &childusage) < 0)
err_sys("getrusage error");
user = (double)myusage.ru_utime.tv_sec +
myusage.ru_utime.tv_usec / 1000000.0;
user += (double)childusage.ru_utime.tv_sec +
childusage.ru_utime.tv_usec / 1000000.0;
sys = (double)myusage.ru_stime.tv_sec +
myusage.ru_stime.tv_usec / 1000000.0;
sys += (double)childusage.ru_stime.tv_sec +
childusage.ru_stime.tv_usec / 1000000.0;
printf("\nuser time = %g, sys time = %g\n", user, sys);
}
void Setsockopt(int fd, int level, int optname, const void *optval,
socklen_t optlen)
{
if (setsockopt(fd, level, optname, optval, optlen) < 0)
err_sys("setsockopt error");
}
void Close(int fd)
{
if (close(fd) == -1)
err_sys("close error");
}
void Listen(int fd, int backlog)
{
char *ptr;
if ((ptr = getenv("LISTENQ")) != NULL)
backlog = atoi(ptr);
if (listen(fd, backlog) < 0)
err_sys("listen error");
}
int tcp_listen(const char *host, const char *serv, socklen_t * addrlenp)
{
int listenfd, n;
const int on = 1;
struct addrinfo hints, *res, *ressave;
bzero(&hints, sizeof(struct addrinfo));
hints.ai_flags = AI_PASSIVE;
hints.ai_family = AF_UNSPEC;
hints.ai_socktype = SOCK_STREAM;
if ((n = getaddrinfo(host, serv, &hints, &res)) != 0)
err_quit("tcp_listen error for %s, %s: %s",
host, serv, gai_strerror(n));
ressave = res;
do {
listenfd =
socket(res->ai_family, res->ai_socktype, res->ai_protocol);
if (listenfd < 0)
continue;
Setsockopt(listenfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on));
if (bind(listenfd, res->ai_addr, res->ai_addrlen) == 0)
break;
Close(listenfd);
} while ((res = res->ai_next) != NULL);
if (res == NULL)
err_sys("tcp_listen error for %s, %s", host, serv);
Listen(listenfd, LISTENQ);
if (addrlenp)
*addrlenp = res->ai_addrlen;
freeaddrinfo(ressave);
return (listenfd);
}
int Tcp_listen(const char *host, const char *serv, socklen_t * addrlenp)
{
return (tcp_listen(host, serv, addrlenp));
}
void *Malloc(size_t size)
{
void *ptr;
if ((ptr = malloc(size)) == NULL)
err_sys("malloc error");
return (ptr);
}
Sigfunc *signal(int signo, Sigfunc * func)
{
struct sigaction act, oact;
act.sa_handler = func;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
if (signo == SIGALRM) {
#ifdef SA_INTERRUPT
act.sa_flags |= SA_INTERRUPT;
#endif
} else {
#ifdef SA_RESTART
act.sa_flags |= SA_RESTART;
#endif
}
if (sigaction(signo, &act, &oact) < 0)
return (SIG_ERR);
return (oact.sa_handler);
}
Sigfunc *Signal(int signo, Sigfunc * func)
{
Sigfunc *sigfunc;
if ((sigfunc = signal(signo, func)) == SIG_ERR)
err_sys("signal error");
return (sigfunc);
}
int Accept(int fd, struct sockaddr *sa, socklen_t * salenptr)
{
int n;
again:
if ((n = accept(fd, sa, salenptr)) < 0) {
#ifdef EPROTO
if (errno == EPROTO || errno == ECONNABORTED)
#else
if (errno == ECONNABORTED)
#endif
goto again;
else
err_sys("accept error");
}
return (n);
}
static int read_cnt;
static char *read_ptr;
static char read_buf[MAXLINE];
static ssize_t my_read(int fd, char *ptr)
{
if (read_cnt <= 0) {
again:
if ((read_cnt = read(fd, read_buf, sizeof(read_buf))) < 0) {
if (errno == EINTR)
goto again;
return (-1);
} else if (read_cnt == 0)
return (0);
read_ptr = read_buf;
}
read_cnt--;
*ptr = *read_ptr++;
return (1);
}
ssize_t readline(int fd, void *vptr, int maxlen)
{
ssize_t n, rc;
char c, *ptr;
ptr = vptr;
for (n = 1; n < maxlen; n++) {
if ((rc = my_read(fd, &c)) == 1) {
*ptr++ = c;
if (c == '\n')
break;
} else if (rc == 0) {
*ptr = 0;
return (n - 1);
} else
return (-1);
}
*ptr = 0;
return (n);
}
ssize_t Readline(int fd, void *ptr, size_t maxlen)
{
ssize_t n;
if ((n = readline(fd, ptr, maxlen)) < 0)
err_sys("readline error");
return (n);
}
ssize_t
writen(int fd, const void *vptr, size_t n)
{
size_t nleft;
ssize_t nwritten;
const char *ptr;
ptr = vptr;
nleft = n;
while (nleft > 0) {
if ((nwritten = write(fd, ptr, nleft)) <= 0) {
if (nwritten < 0 && errno == EINTR)
nwritten = 0;
else
return (-1);
}
nleft -= nwritten;
ptr += nwritten;
}
return (n);
}
void Writen(int fd, void *ptr, int nbytes)
{
if (writen(fd, ptr, nbytes) != nbytes)
err_sys("writen error");
}
void web_child(int sockfd)
{
int ntowrite;
ssize_t nread;
char line[MAXLINE], result[MAXN];
for (;;) {
if ((nread = Readline(sockfd, line, MAXLINE)) == 0)
return;
ntowrite = atol(line);
if ((ntowrite <= 0) || (ntowrite > MAXN))
err_quit("client request for %d bytes", ntowrite);
Writen(sockfd, result, ntowrite);
}
}
void Pthread_detach(pthread_t tid)
{
int n;
if ((n = pthread_detach(tid)) == 0)
return;
errno = n;
err_sys("pthread_detach error");
}
void Pthread_create(pthread_t * tid, const pthread_attr_t * attr,
void *(*func) (void *), void *arg)
{
int n;
if ((n = pthread_create(tid, attr, func, arg)) == 0)
return;
errno = n;
err_sys("pthread_create error");
}
typedef struct {
pthread_t thread_tid;
long thread_count;
} Thread;
Thread *tptr;
#define MAXNCLI 32
int clifd[MAXNCLI], iget, iput;
pthread_mutex_t clifd_mutex;
pthread_cond_t clifd_cond;
void *Calloc(size_t n, size_t size)
{
void *ptr;
if ((ptr = calloc(n, size)) == NULL)
err_sys("calloc error");
return (ptr);
}
void Pthread_mutex_lock(pthread_mutex_t * mptr)
{
int n;
if ((n = pthread_mutex_lock(mptr)) == 0)
return;
errno = n;
err_sys("pthread_mutex_lock error");
}
void Pthread_mutex_unlock(pthread_mutex_t * mptr)
{
int n;
if ((n = pthread_mutex_unlock(mptr)) == 0)
return;
errno = n;
err_sys("pthread_mutex_unlock error");
}
void Pthread_cond_wait(pthread_cond_t * cptr, pthread_mutex_t * mptr)
{
int n;
if ((n = pthread_cond_wait(cptr, mptr)) == 0)
return;
errno = n;
err_sys("pthread_cond_wait error");
}
void *thread_main(void *arg)
{
int connfd;
printf("thread %d starting\n", (int)arg);
for (;;) {
Pthread_mutex_lock(&clifd_mutex);
while (iget == iput)
Pthread_cond_wait(&clifd_cond, &clifd_mutex);
connfd = clifd[iget];
if (++iget == MAXNCLI)
iget = 0;
Pthread_mutex_unlock(&clifd_mutex);
tptr[(int)arg].thread_count++;
web_child(connfd);
Close(connfd);
}
}
void thread_make(int i)
{
Pthread_create(&tptr[i].thread_tid, NULL, &thread_main, (void *)i);
return;
}
pthread_mutex_t mlock = PTHREAD_MUTEX_INITIALIZER;
static int nthreads;
pthread_mutex_t clifd_mutex = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t clifd_cond = PTHREAD_COND_INITIALIZER;
void sig_int(int signo)
{
int i;
pr_cpu_time();
for (i = 0; i < nthreads; i++)
printf("thread %d, %ld connections\n", i, tptr[i].thread_count);
exit(0);
}
void Pthread_cond_signal(pthread_cond_t * cptr)
{
int n;
if ((n = pthread_cond_signal(cptr)) == 0)
return;
errno = n;
err_sys("pthread_cond_signal error");
}
int main(int argc, char **argv)
{
int i, listenfd, connfd;
socklen_t addrlen, clilen;
struct sockaddr *cliaddr;
if (argc == 3)
listenfd = Tcp_listen(NULL, argv[1], &addrlen);
else if (argc == 4)
listenfd = Tcp_listen(argv[1], argv[2], &addrlen);
else
err_quit("usage: serv08 [ <host> ] <port#> <#threads>");
cliaddr = Malloc(addrlen);
nthreads = atoi(argv[argc - 1]);
tptr = Calloc(nthreads, sizeof(Thread));
iget = iput = 0;
for (i = 0; i < nthreads; i++)
thread_make(i);
Signal(SIGINT, sig_int);
for (;;) {
clilen = addrlen;
connfd = Accept(listenfd, cliaddr, &clilen);
Pthread_mutex_lock(&clifd_mutex);
clifd[iput] = connfd;
if (++iput == MAXNCLI)
iput = 0;
if (iput == iget)
err_quit("iput = iget = %d", iput);
Pthread_cond_signal(&clifd_cond);
Pthread_mutex_unlock(&clifd_mutex);
}
}