Showing posts with label linux. Show all posts
Showing posts with label linux. Show all posts

Wednesday, November 7, 2012

Web Server in C

I implemented a web server in C language using only the standard libraries and thought it would be useful for you guys if I share the code.

The server runs on Linux and includes features like handling HTTP GET request, handling content types(txt, html, jpg, zip. rar, pdf, php etc.), sending proper HTTP error codes, serving the files from a web root, change in web root in a config file, zero copy optimization using sendfile method and php file handling. A port number should be provided as a command line argument.

After the server is up and running you can request for files using a web browser like Firefox.

For an example assume port number is "9000" and if you want to request a file called "test.php" which is in the webroot, use
http://localhost:9000/test.php 

WebServer.c
/*
 * WebServer.c
 *
 *  Created on: Nov 3, 2012
 *      Author: pavithra
 *
 * A web server in C language using only the standard libraries.
 * The port number is passed as an argument.
 *
 */

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <fcntl.h>
#include <errno.h>

#define EOL "\r\n"
#define EOL_SIZE 2

typedef struct {
 char *ext;
 char *mediatype;
} extn;

//Possible media types
extn extensions[] ={
 {"gif", "image/gif" },
 {"txt", "text/plain" },
 {"jpg", "image/jpg" },
 {"jpeg","image/jpeg"},
 {"png", "image/png" },
 {"ico", "image/ico" },
 {"zip", "image/zip" },
 {"gz",  "image/gz"  },
 {"tar", "image/tar" },
 {"htm", "text/html" },
 {"html","text/html" },
 {"php", "text/html" },
 {"pdf","application/pdf"},
 {"zip","application/octet-stream"},
 {"rar","application/octet-stream"},
 {0,0} };

/*
 A helper function
 */
void error(const char *msg) {
 perror(msg);
 exit(1);
}

/*
 A helper function
 */
int get_file_size(int fd) {
 struct stat stat_struct;
 if (fstat(fd, &stat_struct) == -1)
  return (1);
 return (int) stat_struct.st_size;
}

/*
 A helper function
 */
void send_new(int fd, char *msg) {
 int len = strlen(msg);
 if (send(fd, msg, len, 0) == -1) {
  printf("Error in send\n");
 }
}

/*
 This function recieves the buffer
 until an "End of line(EOL)" byte is recieved
 */
int recv_new(int fd, char *buffer) {
 char *p = buffer; // Use of a pointer to the buffer rather than dealing with the buffer directly
 int eol_matched = 0; // Use to check whether the recieved byte is matched with the buffer byte or not
 while (recv(fd, p, 1, 0) != 0) // Start receiving 1 byte at a time
 {
  if (*p == EOL[eol_matched]) // if the byte matches with the first eol byte that is '\r'
    {
   ++eol_matched;
   if (eol_matched == EOL_SIZE) // if both the bytes matches with the EOL
   {
    *(p + 1 - EOL_SIZE) = '\0'; // End the string
    return (strlen(buffer)); // Return the bytes recieved
   }
  } else {
   eol_matched = 0;
  }
  p++; // Increment the pointer to receive next byte
 }
 return (0);
}

/*
 A helper function: Returns the
 web root location.
 */
char* webroot() {
 // open the file "conf" for reading
 FILE *in = fopen("conf", "rt");
 // read the first line from the file
 char buff[1000];
 fgets(buff, 1000, in);
 // close the stream
 fclose(in);
 char* nl_ptr = strrchr(buff, '\n');
 if (nl_ptr != NULL)
  *nl_ptr = '\0';
 return strdup(buff);
}

/*
 Handles php requests
 */
void php_cgi(char* script_path, int fd) {
 send_new(fd, "HTTP/1.1 200 OK\n Server: Web Server in C\n Connection: close\n");
 dup2(fd, STDOUT_FILENO);
 char script[500];
 strcpy(script, "SCRIPT_FILENAME=");
 strcat(script, script_path);
 putenv("GATEWAY_INTERFACE=CGI/1.1");
 putenv(script);
 putenv("QUERY_STRING=");
 putenv("REQUEST_METHOD=GET");
 putenv("REDIRECT_STATUS=true");
 putenv("SERVER_PROTOCOL=HTTP/1.1");
 putenv("REMOTE_HOST=127.0.0.1");
 execl("/usr/bin/php-cgi", "php-cgi", NULL);
}

/*
 This function parses the HTTP requests,
 arrange resource locations,
 check for supported media types,
 serves files in a web root,
 sends the HTTP error codes.
 */
int connection(int fd) {
 char request[500], resource[500], *ptr;
 int fd1, length;
 if (recv_new(fd, request) == 0) {
  printf("Recieve Failed\n");
 }
 printf("%s\n", request);
 // Check for a valid browser request
 ptr = strstr(request, " HTTP/");
 if (ptr == NULL) {
  printf("NOT HTTP !\n");
 } else {
  *ptr = 0;
  ptr = NULL;

  if (strncmp(request, "GET ", 4) == 0) {
   ptr = request + 4;
  }
  if (ptr == NULL) {
   printf("Unknown Request ! \n");
  } else {
   if (ptr[strlen(ptr) - 1] == '/') {
    strcat(ptr, "index.html");
   }
   strcpy(resource, webroot());
   strcat(resource, ptr);
   char* s = strchr(ptr, '.');
   int i;
   for (i = 0; extensions[i].ext != NULL; i++) {
    if (strcmp(s + 1, extensions[i].ext) == 0) {
     fd1 = open(resource, O_RDONLY, 0);
     printf("Opening \"%s\"\n", resource);
     if (fd1 == -1) {
      printf("404 File not found Error\n");
      send_new(fd, "HTTP/1.1 404 Not Found\r\n");
      send_new(fd, "Server : Web Server in C\r\n\r\n");
      send_new(fd, "<html><head><title>404 Not Found</head></title>");
      send_new(fd, "<body><p>404 Not Found: The requested resource could not be found!</p></body></html>");
      //Handling php requests
     } else if (strcmp(extensions[i].ext, "php") == 0) {
      php_cgi(resource, fd);
      sleep(1);
      close(fd);
      exit(1);
     } else {
      printf("200 OK, Content-Type: %s\n\n",
        extensions[i].mediatype);
      send_new(fd, "HTTP/1.1 200 OK\r\n");
      send_new(fd, "Server : Web Server in C\r\n\r\n");
      if (ptr == request + 4) // if it is a GET request
        {
       if ((length = get_file_size(fd1)) == -1)
        printf("Error in getting size !\n");
       size_t total_bytes_sent = 0;
       ssize_t bytes_sent;
       while (total_bytes_sent < length) {
        //Zero copy optimization
        if ((bytes_sent = sendfile(fd, fd1, 0,
          length - total_bytes_sent)) <= 0) {
         if (errno == EINTR || errno == EAGAIN) {
          continue;
         }
         perror("sendfile");
         return -1;
        }
        total_bytes_sent += bytes_sent;
       }

      }
     }
     break;
    }
    int size = sizeof(extensions) / sizeof(extensions[0]);
    if (i == size - 2) {
     printf("415 Unsupported Media Type\n");
     send_new(fd, "HTTP/1.1 415 Unsupported Media Type\r\n");
     send_new(fd, "Server : Web Server in C\r\n\r\n");
     send_new(fd, "<html><head><title>415 Unsupported Media Type</head></title>");
     send_new(fd, "<body><p>415 Unsupported Media Type!</p></body></html>");
    }
   }

   close(fd);
  }
 }
 shutdown(fd, SHUT_RDWR);
}

int main(int argc, char *argv[]) {
 int sockfd, newsockfd, portno, pid;
 socklen_t clilen;
 struct sockaddr_in serv_addr, cli_addr;

 if (argc < 2) {
  fprintf(stderr, "ERROR, no port provided\n");
  exit(1);
 }
 sockfd = socket(AF_INET, SOCK_STREAM, 0);
 if (sockfd < 0)
  error("ERROR opening socket");
 bzero((char *) &serv_addr, sizeof(serv_addr));
 portno = atoi(argv[1]);
 serv_addr.sin_family = AF_INET;
 serv_addr.sin_addr.s_addr = INADDR_ANY;
 serv_addr.sin_port = htons(portno);
 if (bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr)) < 0)
  error("ERROR on binding");
 listen(sockfd, 5);
 clilen = sizeof(cli_addr);
 /*
  Server runs forever, forking off a separate
  process for each connection.
  */
 while (1) {
  newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
  if (newsockfd < 0)
   error("ERROR on accept");
  pid = fork();
  if (pid < 0)
   error("ERROR on fork");
  if (pid == 0) {
   close(sockfd);
   connection(newsockfd);
   exit(0);
  } else
   close(newsockfd);
 } /* end of while */
 close(sockfd);
 return 0; /* we never get here */
}
Make sure you have installed php and there's exist a "conf" file consisted of the webroot before running this.

Enjoy!

Saturday, December 4, 2010

exec() Family System Calls

The exec family of functions shall replace the current process image with a new process image.

execl()

Synopsis

int execl(const char *path, const char *arg0, const char *arg1, const char *arg2, ... const char *argn, (char *) 0);

A command (with the path to the command e.g /bin/ls) and the required arguments are passed to the function."arg0" is the command to be executed. Function arguments are null terminated strings(The list of arguments is terminated by NULL).

Example

#include <unistd.h>
 main()
 {
    execl("/bin/ls", "/bin/ls", "-r", "-t", "-l", (char *) 0);
 }


execlp()

A command (path to the command will be resolved by the function itself) and the required arguments are passed to the function."arg0" is the command to be executed.

Example

#include <unistd.h>
 main()
 {
    execlp("ls", "ls", "-r", "-t", "-l", (char *) 0);
 }


execv()

Synopsis

int execv(const char *path, char *const argv[]);

Same as the execl function except now the arguments will be passed into a null terminated char pointer array."arg[0]" is the command to be executed.

Example

#include <unistd.h>
 main()
 {
    char *args[] = {"/bin/ls", "-r", "-t", "-l", (char *) 0 };
  
    execv("/bin/ls", args);
 
 }

Thursday, December 2, 2010

fork() System Call

System call fork() is used to create processes, the newly created process will be the child of the calling process parent. Unix will make an exact copy of the parent's address space and give it to the child. Therefore, the parent and child processes have separate address spaces.

fork() returns a process ID AKA PID, there are three instances;

  1. if PID < 0, fork() returns a negative value, the creation of a child process was unsuccessful.


  2. if PID ==0, fork() returns a zero to the newly created child process.


  3. if PID > 0, fork() returns a positive value, the process ID of the child process, to the parent.


After a successful call of fork(),unix makes two identical copies of address spaces, one for the parent and the other for the child. Both processes will start their execution at the next statement following the fork() call.



Since both processes have identical but separate address spaces;
  • Variables initialized before the fork() call have the same values in both address spaces.
  • Modifications done in each process will be independent.
Example
#include <signal.h>
#include <stdio.h>

void main() 
{
 int pid;

 printf("Parent process ID %d \n",getpid());

 pid = fork();

  if (pid==0) {
   printf("Child process ID %d \n",getpid());
    int x;
    printf("Enter Value : ");
    scanf("%d",&x);
    printf("Entered Value : %d",x);
  } else if (pid > 0) {
   sleep(5);// parent stops execution for 5 seconds
   printf("Parent process ID %d \n",getpid());

  }

}

Friday, October 15, 2010

lex and yacc for Ubuntu

Ubuntu by default does not have lex and yacc installed. Here is the simplest way to install lex and yacc in Ubuntu. This method uses the following packages

flex (for lex)
bison (for yacc)

Open a terminal and type the following command

sudo apt-get install flex bison

After the installation type the following two commands on the console.

which lex
which yacc

They should return /usr/bin/lex and /usr/bin/yacc respectively if lex and yacc
installed properly

Thursday, October 14, 2010

JDK and JRE on Ubuntu



Here is a step by step procedure to install JDK and JRE on ubuntu operating system. Note that there should be an internet connection for this method of installation.

  1. Open a terminal.
  2. Install JDK and JRE using the following command.
  3. apt-get install sun-java6-jdk sun-java6-jre
    
  4. During the installation process it will ask whether to proceed or not. Give y or n (y and n for yes and no respectively).
  5. Go through the wizard like interface and complete the installation.
  6. On the command line type following and confirm JDK is working. This Should return the java version which has been installed for a successful installation.
  7. java -version
    

Monday, July 26, 2010

Linux Shell Scripting part 2

Wild cards * and ?

The character * is a wildcard, and matches against one or more character(s) in a file (or directory) name.

Examples:

ls * will show all the files inside the current directory

ls *.s will show only the files which has .s extension

ls a* will show only the files which begin with a

ls ab* will show only the files which begin with ab

ls ab*.c will show only the files which begin with ab and have the .c extension

The character ? is also a wild cars which matches exactly one character in a file(or directory)name.

Examples:

ls ? will show only the files where their file name is one character long

ls bo? Will show only the files where their file name is 3 character long and starts with bo

Command line arguments

Consider the following command

cp ~/abc ~/Desktop

In this command there are two arguments. What if someone try to run this command without any of those arguments? the shell will probably give an error indicating arguments are missing.This point is crucial when someone uses commands which necessarily need arguments in their shell script. In that case they should run their shell script provided with those arguments.

Example :

Suppose our shell script name is comLineArgs.

Refresh memory on how to run it.

./comLineArgs

If someone needs to give the inputs to their shell scripts externally they can give them as command line arguments.

./comLine Args aaa bbb

The command line arguments which are used at a time can be also be accessed inside the sell script simply as variables.

Shell script name can be accessed as $0

Suppose there is n number of arguments. They can be accessed as $1, $2, $3……. $n

If someone needs to find the number of arguments $# can be used.

$* represents all the arguments.

Example Shell script describing how to access command line arguments.

echo "Total number of command line argument are $#"
echo "$0 is script name"
echo "$1 is first argument"
echo "$2 is second argument"
echo "All of them are :- $* or $@"

Input output redirection

Redirecting the output

> symbol is used to redirect the output of a command.

Example:

cat > file1

The above command creates a new file file1 or overwrites the existing file file1. Cat command reads the inputs given by the keyboard and redirects its output which is normally printed on the screen into the file1.

>> symbol is used to redirect the output of a command as well.

Example:

cat >> file1

Same thing as the > symbol but won’t overwrite the file adds more lines to the end of the file.

Redirecting the input

sort command sorts the contents of list1. Note that here inputs for the sort command are provided by the list1 file.

Pipes

Pipe is a temporary storage that output of a one command is stored and passes to another command as its input.

Example:

ls -l | wc -l

Output of ls command is given as input to wc command So that it will print number of files in current directory.

Saturday, July 24, 2010

Linux Shell Scripting part 1

Shell is a command line interpreter that executes commands read from the keyboard or from a file. It is not part of system kernel, but uses the system kernel to execute programs.

To find all available shells in your system type following command:

$ cat /etc/shells

To find your current shell type following command

$ echo $SHELL

Shell Scripting is series of command written in a plain text file and executing this text file without entering the commands one by one. Any text editor can be used for shell scripting.

Writing a simple shell script “first”

#
# My first shell script
#
clear
echo "My first shell script"

Explanation

1. # is used to represent a comment, which can be used to further explain what is happening in the script.

2. clear is clear command which clears the screen

3. echo is used to print on console

Setting execute permission for the script “first”

Syntax:
chmod permission script-name

Example:
$ chmod 755 first

Running the script “first”

$ ./first

Vriables in shell

1. System variables

Created and maintained by linux itself. Defined in capital letters.

Example: $HOME, $SHELL, $USERNAME

Using system variables

echo $HOME

2. User defined variables

Created and maintained by the user. Defined in simple letters.

Syntax: variable name=value

Example: name=abc

using user defined variables

name=abc

echo $name – this will print abc

echo name – this will print name

To access the user defined variables it is essential to write them followed by the $ sign.

Rules for Naming variable name

1. Variable name must begin with a letter or a number or an underscore, followed by one or more letters or numbers. Never use ?, * etc, for naming variable names.

2. When assigning value to variable never put spaces on either side of the equal sign
age=10 correct
age =10 incorrect
age= 10 incorrect
age = 10 incorrect

3. Variables are case-sensitive

Shell Arithmetic

Use to perform arithmetic operations.

Syntax:
expr operand1 operator operand2

Always put a space between the operand and the operator.

Examples:
expr 1 + 3
expr 2 - 1
expr 10 / 2
expr 20 % 3
expr 10 \* 3 multiplication use \* and not * since its wild card.
echo `expr 6 + 3` should be within back quotes(find back quote under ~) or it won’t print 9. If one uses single or double quotes it will print a + b instead of 9.