Chinaunix首页 | 论坛 | 博客
  • 博客访问: 507730
  • 博文数量: 83
  • 博客积分: 6010
  • 博客等级: 准将
  • 技术积分: 1169
  • 用 户 组: 普通用户
  • 注册时间: 2007-04-29 22:34
文章分类

全部博文(83)

文章存档

2011年(3)

2010年(29)

2009年(30)

2008年(21)

我的朋友

分类: LINUX

2009-04-02 17:39:10

该程序在一个进程池的服务器程序的基础上,加上了:
1,从xml文件中读取参数
2,自动重启:当控制进程(即下面自命名的control进程)crash之后,程序会自动重启该控制进程。而crash掉的控制进程的子进程也要进行善后处理,这是这个功能的难点,而根据不同的应用环境复杂程度也不同,本程序中会通知crash掉控制进程的子进程处理完当前正在处理的任务后直接退出,而不用再听取新的指示。
3,本程序通过SIGUSR1信号来kill掉控制进程,以便测试:对于控制进程,接收到该信号直接退出(系统默认);对于非控制进程,应该忽略该信号或者接收信号时不退出
4,添加了日志功能
 

#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;
}

阅读(925) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~