csocketsnetwork-programmingserver

How to Shutdown the server using poll() Gracefully


I have written a database server and client, and i'm using a poll loop

#include <stdio.h>
#include <stdbool.h>
#include <getopt.h>
#include <stdlib.h>
#include <stdlib.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/types.h>
#include <netdb.h>
#include <string.h>
#include <stdio.h>
#include <unistd.h>
#include <time.h>
#include <sys/select.h>
#include <poll.h>
#include "common.h"
#include "file.h"
#include "parse.h"
#include "srvpoll.h"

#define BACKLOG 256

client CLIENTS[256];

void print_usage(char *argv[]) {
    printf("Usage: %s -n -f <database file>\n",argv[0]);
    printf("\t -n - create new database file\n");
    printf("\t -f - required path ro database file\n");
}

void poll_loop(int dbfd,unsigned short port,struct dbheader_t **dbhdr,struct employee_t **employees)
{
    init_clients(CLIENTS);
    int nfds=1;
    struct pollfd fds[257];
    memset(&fds,'\0',sizeof(fds));
    struct sockaddr_in mysocket;
    memset(&mysocket,0,sizeof(struct sockaddr_in));
    mysocket.sin_family=AF_INET;
    mysocket.sin_port=htons(port);
    mysocket.sin_addr.s_addr=inet_addr("127.0.0.1");
    int sockfd,new_fd;
    if((sockfd = socket(AF_INET,SOCK_STREAM,0))==-1)
    {
        perror("socket");
        exit(EXIT_FAILURE);
    }
    int yes=1;
    setsockopt(sockfd,SOL_SOCKET,SO_REUSEADDR,&yes,sizeof yes);
    if(bind(sockfd,(struct sockaddr *)&mysocket,sizeof(struct sockaddr_in))==-1)
    {
        perror("bind");
        exit(EXIT_FAILURE);
    }
    if(listen(sockfd,BACKLOG)==-1)
    {
        perror("listen");
        exit(EXIT_FAILURE);
    }
    fds[0].fd=sockfd;
    fds[0].events=POLLIN;
    while(1)
    {
        int ii=1;
        for(int i=0;i<256;++i)
        {
            if(CLIENTS[i].fd!=-1)
            {
                fds[ii].fd=CLIENTS[i].fd;
                fds[ii].events=POLLIN;
                ++ii;
            }
        }
        int n_events= poll(fds,nfds,60000);
        if(n_events==-1)
        {
            perror("no events");
            exit(EXIT_FAILURE);
        }
        if(n_events==0)
        {
            printf("server is writing files there is no user connected\n");
            return;
        }
        if(fds[0].revents& POLLIN)
        {
            if((new_fd=accept(sockfd,NULL,NULL))==-1)
            {
                perror("accept");
                continue;
            }
            int slot=freeSlot(CLIENTS);
            if(slot==-1)
            {
                printf("server full");
                close(new_fd);
            }
            else
            {
                CLIENTS[slot].fd=new_fd;
                CLIENTS[slot].state=STATE_CONNECTED;
                nfds++;
                printf("Slot %d has fd %d\n",slot,CLIENTS[slot].fd);
               
            }
            n_events--;
        }
        for(int i=1;i<=nfds&&n_events>0;++i)
        {
            if(fds[i].revents&POLLIN)
            {
                n_events--;
                int fd=fds[i].fd;
                int client_slot=find_fd(CLIENTS,fd);
                if(client_slot==-1)
                {
                    printf("client does not exist\n");
                }
                ssize_t bytes_read=read(fd,CLIENTS[client_slot].buffer,sizeof(CLIENTS[client_slot].buffer));
                if(bytes_read<=0)
                {
                    printf("Client %d has disconnected or error",client_slot);
                    close(fd);
                    CLIENTS[client_slot].fd=-1;
                    memset(CLIENTS[client_slot].buffer,'\0',sizeof(CLIENTS[client_slot].buffer));
                    nfds--;
                }
                else
                {
                    if(CLIENTS[client_slot].state==STATE_CONNECTED||CLIENTS[client_slot].state!=STATE_MSG)
                    {
                        CLIENTS[client_slot].state=STATE_HELLO;
                    }
                    handle_client_fsm(dbfd,dbhdr,employees,&CLIENTS[client_slot]);
             
                }
            }
        }
    }
}

int main(int argc, char *argv[]) {
    if(signal(SIGINT, handle_signal)==SIG_ERR)
    {
        perror("signal");
        return 1;
    }
    char *filepath =NULL;
    char *addstring = NULL;
    char *remove_emp= NULL;
    char *update_emp= NULL;
    char *portarg=NULL;
    unsigned short port=0;
    int c;
    bool newfile=false;
    int dbfd=-1;
    bool list_emp=false;
    struct dbheader_t *dbhd=NULL;
    struct employee_t *employees = NULL;
    while((c= getopt(argc,argv,"nf:a:lr:u:p:"))!=-1)
    {
        switch(c) {
        case 'n':
            newfile=true;
            break;
        case 'f':
            filepath = optarg;
            break;
        case 'a':
            addstring = optarg;
            break;
        case 'l':
            list_emp=true;
            break;
        case 'r':
            remove_emp=optarg;
            break;
        case 'u':
            update_emp=optarg;
            break;
        case 'p':
            portarg=optarg;
            port=atoi(portarg);
            if(port==0)
            {
                printf("bad port");
            }
            break;
        case '?':
            printf("unknown usage\n");
            break;
        default:
            return -1;
        }
    }
    if(filepath==NULL)
    {
        printf("Filepath is a required argument\n");
        print_usage(argv);
        return 0;
    }
    if(newfile)
    {
        dbfd = create_db_file(filepath);
        if (dbfd == STATUS_ERROR)
        {
            printf("unable to create database file\n");
            return -1;
        }
        if(create_db_header(dbfd,&dbhd)==STATUS_ERROR)
        {
            printf("Failed to create database header\n");
            return -1; 
        }
    }
    else
    {
        dbfd = open_db_file(filepath);
        if(dbfd== STATUS_ERROR)
        {
            printf("unable to open database file\n");
            return -1;
        }
        if(validate_db_header(dbfd,&dbhd) == STATUS_ERROR)
        {
            printf("Database not valid! \n");
            return STATUS_ERROR;
        }
    }
    if(read_employees(dbfd,dbhd,&employees)!=STATUS_SUCCESS)
    {
        printf("Failed to read employees\n");
        return STATUS_ERROR;
    }
    if(addstring)
    {
        add_employee(&dbhd,&employees,addstring);
    }
    if(remove_emp)
    {
        remove_employee(&dbhd,&employees,remove_emp);
    }
    if(update_emp)
    {
        update_employee(dbhd,employees,update_emp);
    }
    if(list_emp)
    {
        list_employees(dbhd,employees);
    }
    poll_loop(dbfd,port,&dbhd,&employees);
    output_file(dbfd,dbhd,employees);
    free(dbhd);
    free(employees);
    printf("Newfile: %d\n", newfile);
    printf("Filepath: %s\n", filepath);
    return 0;
}

in the poll_loop function I'm handling clients using a while(1) loop and then waiting 60 seconds in the poll() function if no client is connecting then the function returns and server starts to write to disk, but i want to be able to kill the poll_loop whenever i want and run the rest of the functions and write to the disk without breaking the whole program. or how could i write automatic system that closes the poll_loop function and writes to disk for example every 60 seconds or so. which solution is better and it is more standard?

while(1) {
    int ii=1;
    for(int i=0;i<256;++i)
    {
        if(CLIENTS[i].fd!=-1)
        {
            fds[ii].fd=CLIENTS[i].fd;
            fds[ii].events=POLLIN;
            ++ii;
        }
    }
    int n_events= poll(fds,nfds,60000);
    if(n_events==-1)
    {
        perror("no events");
        exit(EXIT_FAILURE);
    }
    if(n_events==0)
    {
        printf("server is writing files there is no user connected\n");
        return;
    }

Which way do you suggest I should approach to solve this problem? Thanks for your patience.


Solution

  • Use pipe() or socketpair() or equivalent to add an extra descriptor to your fds array, and then you can write to that pipe/pair when you want to wake up poll() while it is waiting for socket activity. Your loop can then read from the pipe/pair and exit if needed.