1

I am very new to C so sorry if I am really confused.

I am trying to have a server process fork() another process to run a C program through popen(). But as soon as I attempt to read the bytes, I get an error from my accept() in my main server process. Here is where I am getting the error.

Can someone please help.

                pipe_pointer = popen(cgi_command, "r");

                if(pipe_pointer != NULL) {
                    while(!feof(pipe_pointer)) {
                        bytes_read = fread(buf, 1, sizeof(buf), pipe_pointer);

                        send(clientSock, buf, bytes_read, 0);

                    }
                }


                pclose(pipe_pointer);

Here is the entire code

/**
 * This method processes the first line of the HTTP request and stores it in the variable that the pointer
 * buffer points to. In this case that variable is the request variable in the request_handler method. Returns 0 if nothing was received.
 **/
int process_request(int clientSock, char *buffer) {
    #define EOL "\r\n"
    #define EOL_SIZE 2

    char *bufferPointer = buffer;
    int eol_matched = 0;

    while(recv(clientSock, bufferPointer, 1, 0) != 0) {
        if(*bufferPointer == EOL[eol_matched]) {
            ++eol_matched;

            if(eol_matched == EOL_SIZE) {
                *(bufferPointer+1-EOL_SIZE) = '\0';
                return(strlen(buffer));
            }
        }
        else {
            eol_matched = 0;
        }
        bufferPointer++;
    }
    return(0);
}

int validate_get_request(char *buffer) {
    char get_string[5] = "GET /";
    if(strncasecmp(buffer, get_string, 5) != 0) {
        return(-1);
    }
    else {
        return(1);
    }
}

/**
 *This method takes a valid request and a pointer to a empty path variable, then fills out the path variable with the path of the requested file. Also it stores the requested filename in the filename variable
 **/
void get_path(char *request, char *path, char *filename) {
    char buf[1024];
    int i = 0;
    char *pointer = request;
    pointer += 4;
    while(!ISspace(pointer[i]) && pointer[i] != '\0') {
        filename[i] = pointer[i];
        i++;
    }
    filename[i] = '\0';


    if(strcmp(filename, "/") == 0) {
        strcpy(filename, "/index.html");
    }

    if(strstr(filename, ".cgi") != NULL) {
        strcpy(path, CGIROOT);
    }
    else {
        strcpy(path, DOCROOT);
    }
    strcat(path, filename);
}


void send_ok_header(int clientSock, char *mime) {
    char buf[1024];
    //Create OK Header, then add the file

    strcpy(buf, "HTTP/1.0 200 OK\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, SERVER);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "Content-Type: ");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, mime);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "\r\n\r\n");
    send(clientSock, buf, strlen(buf), 0);
}

void send_not_found(int clientSock, char *mime) {
    char buf[1024];
    //Create not found Header, then add the file
    strcpy(buf, "HTTP/1.0 404 NOT FOUND\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, SERVER);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "Content-Type: ");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, mime);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "\r\n\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "404 Sorry this webpage cannot be found\n");
    send(clientSock, buf, strlen(buf), 0);
}

void send_unsupported(int clientSock, char *mime) {
    char buf[1024];
    //Create not found Header, then add the file
    strcpy(buf, "HTTP/1.0 415 UNSUPPORTED MEDIA TYPE\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, SERVER);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "Content-Type: ");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, mime);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "\r\n\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "415 Sorry this media type is not supported!!!\n");
    send(clientSock, buf, strlen(buf), 0);
}

void send_unimplemented(int clientSock, char *mime) {
    char buf[1024];
    //Create not found Header, then add the file
    strcpy(buf, "HTTP/1.0 501 UNIMPLEMENTED\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, SERVER);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "Content-Type: ");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, mime);
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "\r\n\r\n");
    send(clientSock, buf, strlen(buf), 0);
    strcpy(buf, "501 Sorry this server only implements valid GET request\n");
    send(clientSock, buf, strlen(buf), 0);
}



/**
 This method takes the filename and fills the variable mime with its appropriate MIME type. Returns -1 if there is an unrecognized filename
 **/
int get_mime_type(char *filename, char *mime, int *is_cgi) {
    //WHY DOES THIS START WITH SOME WEIRD INITIAL VALUE ON THE 2ND TIME A REQUEST IS MADE?????
    char extension[50] = "";
    int return_val = -1;
    char *pointer;
    int filename_len = sizeof(filename);
    int i = 0;

    pointer = strstr(filename, ".");
    if(pointer != NULL) {
        while(pointer[i] != '\0') {
            extension[i] = pointer[i];
            i++;
        }
    }


    if(strcasecmp(extension, ".html") == 0 || strcasecmp(extension, ".htm") == 0) {
        strcpy(mime, "text/html");
        return_val = 1;
    }

    else if(strcasecmp(extension, ".jpeg") == 0 || strcasecmp(extension, ".jpg") == 0) {
        strcpy(mime, "image/jpeg");
        return_val = 1;
    }

    else if(strcasecmp(extension, ".gif") == 0) {
        strcpy(mime, "image/gif");
        return_val = 1;
    }

    else if(strcasecmp(extension, ".txt") == 0) {
        strcpy(mime, "text/plain");
        return_val = 1;
    }

    //set the cgi variable to 1
    else if(strcasecmp(extension, ".cgi") == 0) {
        strcpy(mime, "text/html");
        *is_cgi = 1;
        return_val = 1;
    }



    return(return_val);
}



/**
 * This is the function that the handler thread executes to handle each request
 */
void request_handler(int clientSock) {
    FILE *pipe_pointer = NULL;
    int i = 0;
    int is_cgi = 0;
    char buf[1024], path[1024], request[1024], mime[20], filename[50], *query_string[50] = {NULL}, *query_pointer, cgi_command[1024], cgibuf[1024];
    long bytes_read;

    //Send the actual html file with header
    FILE *requestedfile = NULL;

    //Take a request and fill it in the request variable
    if(process_request(clientSock, request) != 0) {
        //If it is a get request
        if(validate_get_request(request) != -1) {


            query_pointer = strtok(request, " ?=&");
            strcpy(request, query_pointer);
            while(query_pointer != NULL) {
                query_string[i] = malloc(strlen(query_pointer) + 1);
                strcpy(query_string[i], query_pointer);
                query_pointer = strtok(NULL, " ?=&");
                i++;
            }
            query_string[i] = NULL;


            get_path(request, path, filename);

            access(path, R_OK);

            //if the file extension is unsupported throw a 415 code
            if(get_mime_type(filename, mime, &is_cgi) != 1) {
                send_unsupported(clientSock, mime);
            }
            else if(errno == ENOENT) { //filetype is recognized, but file cannot be found
                send_not_found(clientSock, mime);
            }
            else if(errno == EACCES) { //filetype and file found, but no read access to the file
                //fix this to a 403 error
                perror("no access");
            }
            else { //PUT THE CGI code somewhere in this else statement

                if(is_cgi == 1) {
                    i = 1;
                    //Create command to open cgi script
                    strcpy(cgi_command, "(cd ");
                    strcat(cgi_command, CGIROOT);
                    strcat(cgi_command, "; .");
                    strcat(cgi_command, filename);
                    strcat(cgi_command, " ");


                    while(query_string[2*i + 1] != NULL) {
                        strcat(cgi_command, query_string[2*i + 1]);
                        i++;
                    }
                    strcat(cgi_command, ")");

                    free(*query_string);





                    pipe_pointer = popen(cgi_command, "r");

                    if(pipe_pointer != NULL) {
                        while(!feof(pipe_pointer)) {

                            bytes_read = fread(cgibuf, 1, sizeof(cgibuf), pipe_pointer);

                            send(clientSock, cgibuf, bytes_read, 0);

                        }
                    }


                    pclose(pipe_pointer);
                }
                else {
                    //Open the file and start to send it
                    requestedfile = fopen(path, "rb");

                    //Send header
                    send_ok_header(clientSock, mime);

                    //Read the file by 1024 byte increments and send it
                    while(!feof(requestedfile)) {
                        bytes_read = fread(buf, 1, sizeof(buf), requestedfile);
                        send(clientSock, buf, bytes_read, 0);

                    }
                }
            }

        }
        else { //not a get request

            send_unimplemented(clientSock, mime);

        }
    }
    //Close the client socket when the request is fulfilled
    //thread will die after this
    close(clientSock);
}

int main(int argc, const char * argv[])
{
    int server_sock_desc;
    struct sockaddr_in name;

    int client_sock_desc;
    struct sockaddr_in client_name;
    socklen_t addr_size;

    pthread_t handler_thread;

    //connection setup
    server_sock_desc = socket(PF_INET, SOCK_STREAM, 0);
    if(server_sock_desc != -1) {
        memset(&name, 0, sizeof(name));
        name.sin_family = AF_INET;
        name.sin_port = htons(PORTNUMBER);
        name.sin_addr.s_addr = htonl(INADDR_ANY);
        int bind_result = bind(server_sock_desc, (struct sockaddr *) &name, sizeof(name));
        if(bind_result == 0) {
            if(listen(server_sock_desc, BACKLOG) < 0) {
                perror("listen failed");
            }

            addr_size = sizeof(client_name);

            //Server Loop will continue to run listening for clients connecting to the server
            while(1) {

                //new client attempting to connect to the server
                client_sock_desc = accept(server_sock_desc, (struct sockaddr *) &client_name, &addr_size);
                if(client_sock_desc == -1) {
                    perror("accept failed");
                }

                //connection starts here

                //create a thread for the new clients request to be handled
                if(pthread_create(&handler_thread, NULL, request_handler, client_sock_desc) != 0) {
                    perror("pthread_create failed");
                }
            }
        }
        else {
            perror("bind failed");
        }
    }
    else {
        perror("socket failed");
    }

}

After fread() is executed

program execution immediately jumps to this loop and gets stuck on the second line

while(recv(clientSock, bufferPointer, 1, 0) != 0) {
        if(*bufferPointer == EOL[eol_matched]) {
            ++eol_matched;

            if(eol_matched == EOL_SIZE) {
                *(bufferPointer+1-EOL_SIZE) = '\0';
                return(strlen(buffer));
            }
        }
        else {
            eol_matched = 0;
        }
        bufferPointer++;
    }
user1367263
  • 53
  • 1
  • 6
  • What error are you getting? BTW, your use of `feof()` is wrong, see http://stackoverflow.com/questions/5431941/while-feof-file-is-always-wrong – Barmar Mar 02 '13 at 08:13
  • You say this is where you're getting the error, but there's no `accept()` there. – Barmar Mar 02 '13 at 08:15
  • my accept is in the main program. i am getting a very weird error so its extremely hard to trace. as soon as fread() happens, my the program execution goes to a random line in my code – user1367263 Mar 02 '13 at 08:34
  • Is there a reason you refuse to tell us what the error is? We can't help you with "a very weird error". – Barmar Mar 02 '13 at 08:37
  • the error i am getting is my program execution goes to a random line in my code as soon as fread() is executed – user1367263 Mar 02 '13 at 08:40
  • Sounds like a wayward pointer problem. How is `buf` declared? If it's a pointer, did you allocate memory for the buffer? – Barmar Mar 02 '13 at 08:43
  • just posted entire code so you can see it – user1367263 Mar 02 '13 at 08:43
  • i just started C this weekend so sorry if the code is very confusing. i still dont fully understand all the concepts yet of memory management and pointers – user1367263 Mar 02 '13 at 08:45
  • Actually, for someone who just started C this weekend, it's pretty good. – Barmar Mar 02 '13 at 08:59
  • Did you read the question about `feof()` I linked to? That could very well be part of the problem. Also, are you sure `cgi_command` is running properly? It's hard for me to follow all the logic that constructs that. I think you need to single-step it in a debugger to see what's going on -- I think it's asking a bit much for someone to read all that code and find your error. – Barmar Mar 02 '13 at 09:05
  • cgi_command runs correctly. I try running the exact same command on my terminal and it runs fine. im trying to see if the feof() is causing the error but i do not think so, it seems to work fine, and the error even happens if I remove that part from the code. Everytime I single step it through the debugger it just has accept() throw an error, prints it to my stdout and then jumps to that line of code. im a java programmer so this is all very foreign to me. – user1367263 Mar 02 '13 at 09:16
  • sorry for being difficult, i am really trying my best to explain, but this error is unlike anything i seen before, and i this is my first time really multithreading. so here let me try again. when i fread() executes i believe the main thread throws the accept error – user1367263 Mar 02 '13 at 09:22
  • Stop saying that it's weird, unlike anything, etc. Just tell me the F'ing error! – Barmar Mar 02 '13 at 09:24
  • and i believe you are right, another thread (not the main thread, or the thread that was executing fread) does actually jump to prcoess_request() and gets stuck there. – user1367263 Mar 02 '13 at 09:27
  • The error accept() throws to stdout is "accept failed: Interrupted system call" – user1367263 Mar 02 '13 at 09:28
  • That's helpful. Since popen() uses fork(), there's a SIGCHLD signal when the child process exits. That interrupts accept(). In general, when a system call gets an EINTR error, you should simply loop back and call it again. – Barmar Mar 02 '13 at 09:30
  • dont know if it helps. but the thread in process_request() that gets stuck says EXC_BAD_ACESS(code=1, address=0x100383000) – user1367263 Mar 02 '13 at 09:31
  • Are you sure the client is sending a proper CRLF newline at the end of the request line? `process_request()` is very trusting, and doesn't do any buffer overflow checking. – Barmar Mar 02 '13 at 09:37
  • ah i think im understanding a little, but i am still a little confused on the solution. i tried catching it in the main() function by entering if(errno = EINTR) { //do something }. but it doesnt seem to ever be getting to that part of the code – user1367263 Mar 02 '13 at 09:41
  • yes I am positive. same thing happens when I enter the request in the browser – user1367263 Mar 02 '13 at 09:42
  • `if (errno = EINTR)` should be `if (errno == EINTR)` – Barmar Mar 02 '13 at 09:42
  • sorry thats what I meant. (errno == EINTR) never happens. – user1367263 Mar 02 '13 at 09:48

1 Answers1

2

Change:

            if(client_sock_desc == -1) {
                perror("accept failed");
            }

to:

            if(client_sock_desc == -1) {
                if (errno == EINTR) {
                    continue;
                }
                perror("accept failed");
                exit(1);
            }

to go back to the accept() in case of an interrupt. In case of any other error, you need to terminate -- you were simply falling through and starting the request_handler thread anyway.

Barmar
  • 741,623
  • 53
  • 500
  • 612