#include <time.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <dirent.h>
#include <sys/stat.h>
#include <signal.h>
#include <sys/wait.h>
#include <pthread.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <libxml/xmlmemory.h>
#include <libxml/parser.h>
#define CONFIG_FILE "http-serv.xml"
#define BUFSIZE 4096
#define PIDPATH ".pid"
#define head503 "HTTP/1.1 503 Service unavailable\r\n"
#define head404 "HTTP/1.1 404 Not Found\r\n"
#define head200 "HTTP/1.1 200 0K\n\rContent—Type: text/html\n\rContent—Length: "
void control();
int listenfd;
int fd1[2], fd2[2];
int len503, len404, len200;
int PREFORK, MAXCHILD, port;
char errlog[256];
char accesslog[256];
int stop = 0;
char e = 'e';
char c = 'c';
int req_num = 0;
int child_num = 0;
pid_t controlpid;
typedef struct {
pid_t pid;
char status; // 'n' means new request; 'f' means finish the request
} REPORT;
//usr1信号用于测试,对于非控制进程,应该忽略该信号或者接收信号时不退出
static void
sig_usr1()
{
pid_t pid = getpid();
stop = 1;
printf("[%d] sig_usr1\n", pid);
}
//获取xml文件中的参数
int init_config(const char *path){
xmlDocPtr doc;
xmlNodePtr cur;
doc = xmlParseFile(path);
if (doc == NULL ) {
fprintf(stderr,"Document not parsed successfully. \n");
return -1;
}
cur = xmlDocGetRootElement(doc);
if (cur == NULL) {
fprintf(stderr,"empty document\n");
xmlFreeDoc(doc);
return -2;
}
if (xmlStrcmp(cur->name, (const xmlChar *) "config")) {
fprintf(stderr,"document of the wrong type, root node != config");
xmlFreeDoc(doc);
return -3;
}
cur = cur->xmlChildrenNode;
xmlChar *key;
while (cur != NULL) {
if ((!xmlStrcmp(cur->name, (const xmlChar *)"port"))) {
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
port = atoi(key);
//printf("%s: %d\n", cur->name, port);
xmlFree(key);
}
else if ((!xmlStrcmp(cur->name, (const xmlChar *)"preforknum"))) {
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
PREFORK = atoi(key);
//printf("%s: %d\n", cur->name, prefork);
xmlFree(key);
}
else if ((!xmlStrcmp(cur->name, (const xmlChar *)"maxchildnum"))) {
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
MAXCHILD = atoi(key);
//printf("%s: %d\n", cur->name, maxchild);
xmlFree(key);
}
else if ((!xmlStrcmp(cur->name, (const xmlChar *)"errlog"))) {
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
strcpy(errlog, key);
//printf("%s: %s\n", cur->name, errlog);
xmlFree(key);
}
else if ((!xmlStrcmp(cur->name, (const xmlChar *)"accesslog"))) {
key = xmlNodeListGetString(doc, cur->xmlChildrenNode, 1);
strcpy(accesslog, key);
//printf("%s: %s\n", cur->name, accesslog);
xmlFree(key);
}
cur = cur->next;
}
xmlFreeDoc(doc);
return 0;
}
int
s_pipe(int fd[2])
{
return(pipe(fd));
}
void answer(int listenfd)
{
int connfd;
char buf[BUFSIZE];
int count;
int pid = getpid();
struct sockaddr_in cliaddr;
int size = sizeof(cliaddr);
int errfd;
int accfd;
char comm;
REPORT rep;
rep.pid = pid;
time_t t;
char *c;
signal(SIGUSR1, sig_usr1);
if ((errfd = open(errlog, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
perror("open error log failed");
exit(-1);
}
dup2(errfd, STDERR_FILENO);
if ((accfd = open(accesslog, O_WRONLY | O_APPEND | O_CREAT, S_IRUSR | S_IWUSR)) < 0) {
perror("open access log failed");
exit(-1);
}
dup2(accfd, STDOUT_FILENO);
while (1) {
connfd = accept(listenfd, (struct sockaddr *)&cliaddr,(socklen_t *)&size );
if (stop == 0){
rep.status = 'n';
if (write(fd1[1], &rep, sizeof(rep)) < 0) {
perror("write pipe new failed");
exit(-1);
}
}
printf("%s - - ", inet_ntop(AF_INET,&cliaddr.sin_addr,buf,sizeof(buf)));
time(&t);
c = ctime(&t);
*(c + strlen(c) - 1) = 0;
printf("[%s] ", c);
count = read(connfd, buf, BUFSIZE);
if ((c = strstr(buf, "\r")) == NULL) {
printf("strstr failed\n");
exit(-1);
}
*c = 0;
printf("\"%s\" ", buf);
*c = '\r';
char req[10];
char filepath[256];
sscanf(buf, "%s%s", req, filepath + 1);
filepath[0] = '.';
if (strcmp("GET", req) != 0) {//503
write(connfd, head503, len503);
//goto err_out;
printf("503 0\n");
close(connfd);
//exit(-1);
goto out;
}
char content[BUFSIZE];
struct stat stbuf;
if (lstat(filepath, &stbuf) != 0) {
int err = errno;
if (err == ENOENT) {//404
write(connfd, head404, len404);
}
printf("404 0\n");
close(connfd);
//exit(-1);
goto out;
}
count = write(connfd, head200, len200);
u_int filesize = stbuf.st_size;
printf("200 %u\n", filesize);
sprintf(content, "%u\n\r\n\r", filesize);
count = write(connfd, content, strlen(content));
FILE *fp = fopen(filepath, "r");
if (fp == NULL) {
printf("open file %s failed\n", filepath);
close(connfd);
exit(-1);
}
while((count = fread(content, 1, sizeof(content), fp)) > 0) {
//printf("%s", content);
if (write(connfd, content, count) != count) {
printf("write failed\n");
}
}
fclose(fp);
out:
close(connfd);
if (stop == 0) {
rep.status = 'f';
if (write(fd1[1], &rep, sizeof(rep)) < 0) {
perror("write pipe finish failed");
exit(-1);
}
}
if (stop == 0) {
if (read(fd2[0], &comm, 1) < 1) {
perror("read pipe failed");
exit(-1);
}
//printf("[%d] reve %c from pa\n", pid, comm);
if (comm == 'e') {
//printf("[%d] exit\n", pid);
exit(0);
}
else if (comm == 'c') {
//printf("[%d] continue\n", pid);
continue;
}
else {
//printf("[%d] comm : %c illeagle\n", pid, comm);
continue;
}
}
printf("[%d] stop, exit now\n", pid);
exit(1);
}
}
int write_pid()
{
int fd;
if ((fd = open(PIDPATH, O_WRONLY | O_TRUNC | O_CREAT, S_IWUSR)) < 0){
perror("open pidfile faild");
return -1;
}
struct flock lock;
lock.l_type = F_WRLCK;
lock.l_start = 0;
lock.l_whence = SEEK_SET;
lock.l_len = 0;
if (fcntl(fd, F_SETLK, &lock) == -1) {
int err = errno;
perror("fcntl faild");
if (err == EAGAIN) {
printf("Another http-serv process is running now!\n");
}
return -1;
}
return 0;
}
void daemon_init()
{
//clear file creation mask;
umask(0);
//become a session leader
if (fork() != 0)
exit(-1);
if (setsid() < 0)
exit(-1);
//make sure can be never get the TTY control
if (fork() != 0)
exit(-1);
//may chdir here
int i;
close(0);
//for (i = 0; i < 1024; i++)
for (i = 3; i < 1024; i++)
close(i);
int fd0;//, fd1, fd2;
fd0 = open("/dev/null", O_RDWR);
//fd1 = dup(0);
//fd2 = dup(0);
if (fd0 != 0/* || fd1 != 1 || fd2 != 2*/) {
printf("init failed\n");
exit(-1);
}
}
//use by control process
void control()
{
pid_t pid;
signal(SIGCHLD, SIG_IGN);
signal(SIGUSR1, SIG_IGN);
pid_t myself = getpid();
REPORT rep;
int i, prefork;
for (i = 0, prefork = PREFORK - child_num; i < prefork; i++) {
if ((pid = fork()) < 0) {
perror("fork faild");
exit(3);
}
else if (pid == 0) {
answer(listenfd);
}
else {
printf("have create child %d\n", pid);
}
child_num++;
}
while (1) {
///printf("[%d]req_num = %d, child_num = %d\n", myself, req_num, child_num);
if (read(fd1[0], &rep, sizeof(rep)) < sizeof(rep)) {
perror("parent read pipe failed");
exit(-1);
}
//printf("parent: receive from %d\n", pid);
if (rep.status == 'n') {
req_num ++;
//printf("parent: %d have receive new request\n", rep.pid);
if (req_num >= child_num && child_num <= MAXCHILD) { //fork more new child
if ((pid = fork()) < 0) {
perror("fork faild");
exit(3);
}
else if (pid == 0) {
answer(listenfd);
}
else {
//printf("have create child %d\n", pid);
child_num ++;
}
}
}
else if (rep.status == 'f') {
req_num --;
//printf("parent: %d have finish a request\n", rep.pid);
if (child_num > (req_num + 1) && child_num > PREFORK) {//tell child to exit
if (write(fd2[1], &e, sizeof(e)) < sizeof(e)) {
perror("pa write pipe failed");
exit(-2);
}
//printf("tell child exit\n");
child_num --;
}
else {
if (write(fd2[1], &c, sizeof(c)) < sizeof(c)) {
perror("pa write pipe failed");
exit(-2);
}
//printf("tell child continue\n");
}
}
}
exit(-1);
}
int do_clean()
{
if (system("killall -s USR1 http-serv4") == -1) {
perror("system failed");
exit(-1);
}
return 0;
}
int
main(int argc, char **argv)
{
struct sockaddr_in servaddr;
pid_t pid;
signal(SIGUSR1, SIG_IGN);//父进程是用于监控控制进程的,忽略SIGUSR1
len200 = strlen(head200);
len404 = strlen(head404);
len503 = strlen(head503);
daemon_init();
if (write_pid() < 0)
return -1;
if (init_config(CONFIG_FILE) != 0) {
printf("init config failed\n");
return -2;
}
printf("port = %d, prefork = %d, maxchild = %d, errlog = %s, accesslog = %s\n", port, PREFORK, MAXCHILD, errlog, accesslog);
if (s_pipe(fd1) < 0) {
perror("pipe failed");
exit(-1);
}
if (s_pipe(fd2) < 0) {
perror("pipe failed");
exit(-1);
}
//initialize servaddr and listenfd...
bzero(&servaddr, sizeof(servaddr));
servaddr.sin_family = AF_INET;
servaddr.sin_addr.s_addr = htonl(INADDR_ANY);
servaddr.sin_port = htons(port);
listenfd = socket(AF_INET, SOCK_STREAM, 0);
bind(listenfd, (struct sockaddr *) &servaddr, sizeof(servaddr));
listen(listenfd, 1000);
if ((pid = fork()) < 0) {
perror("fork faild");
exit(3);
}
else if (pid == 0) {
control();//控制进程
}
else {
printf("have create control process %d\n", pid);
controlpid = pid;
}
for(;;) {
pid = waitpid(-1, NULL, 0);//发现控制进程crash了
printf("wait %d\n", pid);
if (pid == controlpid) {
printf("have waited child %d\n", pid);
do_clean();
if ((pid = fork()) < 0) { //重启控制进程
perror("fork faild");
exit(3);
}
else if (pid == 0) {
control(); //
重启控制进程
}
else {
printf("have create control process %d\n", pid);
controlpid = pid;
}
}
else {
printf("wait unknown process!!!\n");
exit(-3);
}
}
return 0;
}
|