# Zkouška 4.2.2016 - TCP server s měřením času příkazů

<{ForumPost(poster="Jenda_", timestamp=2016-02-04 04:10:49)}>
Už to tu někde bylo v mírně těžší variantě (kdy příkaz mohl mít parametry).  
  
Napište TCP server.  
  
Klient se připojí a zadá příkaz (jméno programu, bez parametrů). Server příkaz spustí, změří, jak dlouho běžel, pošle tuto informaci zpátky a spojení ukončí. V případě, že příkaz nedoběhne do nastaveného timeoutu, server jej zabije.  
  
Důležité je zabíjet celou process grupu (kterou si musíte vytvořit), protože jinak tam zůstanou viset zaseknuté děti.  
  
Řešení bylo možné forkem na každého klienta nebo přes select.  

    #include <sys/types.h>
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <string.h>
    #include <sys/socket.h>
    #include <netdb.h>
    #include <arpa/inet.h>
    
    #include <signal.h>
    
    #include <time.h>
    
    #include <sys/wait.h>
    
    #define	nclients 5
    #define	rsize 64
    
    typedef struct cl {int free;pid_t pid;
    } cl;
    
    int timeout = 5;
    int clientfd = -1;
    unsigned datestart = 0;
    
    int sfd;
    
    cl clients[nclients];
    
    pid_t grandchild;
    
    // write N bytes to fd
    void writen(int fd, char * buf, int n) {int pos = 0;while (pos < n) {ssize_t j = write(fd, buf+pos, n-pos);if (j <= 0) {fprintf(stderr, "error at %i
    ", pos);exit(1);}pos += j;}
    }
    
    void printdelay() {
    #define	mlen (sizeof (unsigned) + 10)char * msg = malloc(mlen);unsigned ctime = (unsigned)time(NULL);
    int rlen = snprintf(msg, mlen, "%i sec
    ", (ctime-datestart));printf("signalling: '%s'
    ", msg);writen(clientfd, msg, rlen);shutdown(clientfd, SHUT_RDWR);close(clientfd);exit(0);
    }
    
    void child_handler(int sig) {printf("alarm/int handler (%i), killing %i
    ", sig, -grandchild);
    pid_t pgroup = getpgid(grandchild);
    kill(-pgroup, SIGTERM);
    while (waitpid(-1, NULL, WNOHANG) > 0) {}
    shutdown(clientfd, SHUT_RDWR);close(clientfd);exit(2);
    }
    
    void parent_handler(int sig) {printf("Got INT
    ");
    // kill all our childrenfor (int i = 0; i < nclients; i++) {if (!clients[i].free) {kill(clients[i].pid, SIGINT);}}
    // grab generated zombieswhile (waitpid(-1, NULL, WNOHANG) > 0) {printf("killing zombie
    ");}
    shutdown(sfd, SHUT_RDWR);close(sfd);
    // dieexit(0);
    }
    
    int main(int argc, char *argv[]) {
    struct addrinfo hints;struct addrinfo *result, *rp;int s;
    if (argc != 3) {fprintf(stderr, "Usage: %s port timeout
    ", argv[0]);exit(EXIT_FAILURE);}
    timeout = atoi(argv[2]);
    memset(&hints, 0, sizeof (struct addrinfo));hints.ai_family = AF_UNSPEC;	/* Allow IPv4 or IPv6 */hints.ai_socktype = SOCK_STREAM;hints.ai_flags = AI_PASSIVE;hints.ai_protocol = 0;	/* Any protocol */hints.ai_canonname = NULL;hints.ai_addr = NULL;hints.ai_next = NULL;
    s = getaddrinfo(NULL, argv[1], &hints, &result);// s = getaddrinfo("localhost", argv[1], &hints, &result);if (s != 0) {fprintf(stderr, "getaddrinfo: %s
    ", gai_strerror(s));exit(EXIT_FAILURE);}
    /*  getaddrinfo() returns a list of address structures.    Try each address until we successfully bind(2).    If socket(2) (or bind(2)) fails, we (close the socket    and) try the next address. */
    for (rp = result; rp != NULL; rp = rp->ai_next) {
    if (rp->ai_family == AF_INET6) {char * buf = malloc(INET6_ADDRSTRLEN);struct sockaddr_in * saddr = (struct sockaddr_in *)    rp->ai_addr;if (inet_ntop(AF_INET6, &(saddr->sin_addr), buf,    INET6_ADDRSTRLEN)) {printf("Trying (v6) %s
    ", buf);} else {printf("ntop err
    ");}free(buf);} else if (rp->ai_family == AF_INET) {char * buf = malloc(INET_ADDRSTRLEN);struct sockaddr_in * saddr = (struct sockaddr_in *)    rp->ai_addr;if (inet_ntop(AF_INET, & (saddr->sin_addr), buf,    INET_ADDRSTRLEN)) {printf("Trying (v4) %s
    ", buf);} else {printf("ntop err
    ");}free(buf);}
    // see https://sourceware.org/bugzilla/show_bug.cgi?id=9981if (rp->ai_family == AF_INET) {// they don't have the fix in Debian yet// just remove it if you expect sane behavior of// getaddrinfocontinue;}
    sfd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
    int enable = 1;if (setsockopt(sfd, SOL_SOCKET, SO_REUSEADDR, &enable,    sizeof (int)) < 0) {perror("setsockopt(SO_REUSEADDR) failed");}
    if (sfd == -1) {continue;}
    if (bind(sfd, rp->ai_addr, rp->ai_addrlen) == 0) {printf(" ...bound OK
    ");break;}
    close(sfd);}
    if (rp == NULL) {	/* No address succeeded */fprintf(stderr, "Could not bind
    ");exit(EXIT_FAILURE);}
    freeaddrinfo(result);	/* No longer needed */
    // prepare client pid arrayfor (int i = 0; i < nclients; i++) {clients[i].free = 1;}
    // register handler to kill children on INTstruct sigaction act;act.sa_handler = parent_handler;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGINT, &act, NULL);
    if (listen(sfd, nclients) == -1) {perror("cannot listen
    ");}
    // now we are listening for connections
    while (1) {int newsockfd = accept(sfd, NULL, NULL);if (newsockfd < 0) {perror("cannot accept");}
    printf("New connection
    ");
    // get out of zombiespid_t died;int status;while ((died = waitpid(-1, &status, WNOHANG))) {if (died <= 0) {break;}for (int i = 0; i < nclients; i++) {if (clients[i].pid == died) {printf("finished %i
    ", died);clients[i].free = 1;}}}
    int ret = -1;for (int i = 0; i < nclients; i++) {if (clients[i].free) {ret = i;}}if (ret == -1) {printf("No free client
    ");close(newsockfd);}
    pid_t child;child = fork();
    if (child < 0) {perror("cannot fork");}
    if (child == 0) { // child
    // start a new process group//setpgid(getpid(), getpid());setsid();
    // read the requestchar * request = (char *)    calloc(1, rsize * sizeof (char));int i;for (i = 0; i < rsize; i++) {ssize_t j = read(newsockfd, request + i, 1);if (j <= 0 || request[i] == '
    ') {break;}}
    if (i >= rsize) {char ble[] = "Request too long
    ";writen(newsockfd, ble, sizeof (ble));close(newsockfd);exit(2);}
    printf("Going to exec: '%s'
    ", request);
    grandchild = fork();
    if (grandchild < 0) {perror("cannot fork grandchild");}
    if (grandchild == 0) { // grandchildexecl(request, request, NULL);printf("exec fail
    ");close(newsockfd);exit(3);// not reached}
    clientfd = newsockfd;datestart = (unsigned)time(NULL);
    // parent// register sighandler, set alarmstruct sigaction act;act.sa_handler = child_handler;sigemptyset(&act.sa_mask);act.sa_flags = 0;sigaction(SIGALRM, &act, NULL);sigaction(SIGINT, &act, NULL);alarm(timeout);
    // waitwait(&status);alarm(0);printdelay();} else {clients[ret].free = 0;clients[ret].pid = child;unsigned curtime = time(NULL);unsigned to = curtime+timeout;printf("forked %i, expire %i
    ", child, to);close(newsockfd);}
    }
    }
    


<{/ForumPost}>

