Cli development using ipc shared memory shmget


Advertisements

 | 
Share

Project background

    Command line interface(Cli) development is one kind of project that is required for any product to provide interface to the product customers. It makes the product more user friendly and demand for the product is increased few times more once Cli is developed.
    In this project you will get hands on experience on how to use IPC shared memory to communicate data between two different linux processes. The command to be entered by the user, the response to the command given by the server along with the few other parameters are shared between client and server processes using IPC shared memory.
    Project is executed in raspberry pi with debian linux operating system loaded on it. However you can execute this project in any linux distribution including linux virtual machines. Following are the learnings from this project:

Project learning summary

  • Writing modular c programs for projects
  • Shared memory creation by name, memory mapping shared memory data
  • Data syncronization between linux processes using shared memory KEY

Project contents

Client program development, compilation

Client program development.
client.c: client program to take user input command and get response from server.

/*
 * Program: client.c
 * Purpose: Client side code for cli development project
 */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include  <sys/types.h>
#include  <sys/ipc.h>
#include  <sys/shm.h>

#define CMD_LENGTH 100 /* Max command length */
#define KEY 0x5444 /* Shared memory key */
#define SIZE 4096 /* Size of the shared memory */

void getCmd(char *);

struct cli {
  int wr_update;
  int rd_update;
  int ack;
  char cmd[100];  /* pointer to shared memory obect */
  char response[100];
};
int main(){
  struct cli *p;

  /* name of the shared memory object */
  const char* name = "OS";
  /* shared memory file descriptor */
  int shm_fd;
  /* create the shared memory object */
  shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
  /* configure the size of the shared memory object */
  ftruncate(shm_fd, SIZE);
  /* memory map the shared memory object */
  shm_fd = shmget(KEY, sizeof(struct cli), IPC_CREAT | 0666);
  if (shm_fd < 0) {
    printf("shmget error\n");
    exit(1);
  }
  p = shmat(shm_fd, NULL, 0);  /* attach */
  if (p == 0) {
    printf("*** shmat error (server) ***\n");
    exit(1);
  }
  p->wr_update = 0;
  p->rd_update = 1;
  p->ack = 0; 
  while (1) {
    char cmd[20];
    getCmd(cmd);
    printf("Input command:%s\n",cmd);
    if(strcasecmp(cmd,"exit\n") == 0) {
      printf("Terminating client program\n");
      exit(0);
    } else {
      if(p->wr_update == 0 && p->rd_update == 1) {
        /* write to the shared memory object */
        sprintf(p->cmd, "%s\n", cmd);
        p->wr_update = 1;
        p->rd_update = 0;
        newConn:
        if(p->ack == 1) {
          printf("%s", p->response);
          memset(p->response, 0, sizeof(p->response));
          p->ack = 0;
        } else {
          goto newConn;
        }
      }
    }
  }
  /* remove semaphores*/
  shmdt(p);
  close(shm_fd);
  return 0;
}
void getCmd(char *name){
  printf("Enter command to execute or exit to exit the cli :");
  fgets(name,CMD_LENGTH,stdin);
}
    Let's go through the client code written in client.c. We have defined a structure cli which is shared between client and server processes. String cmd is declared inside struct cli to take command from user. String response is declared inside struct cli to collect response from server and then send it to the client. wr_update, rd_update, ack are the three parameters used to syncronize the events between client and server processes. Then we have created a shared memory with name "OS" and memory mapped it using shmget using a shared memory KEY.
    To start with we have initialized wr_update to 0, rd_update to 1, ack bit to 0. In a while loop we are waiting for user command using a function getCmd which uses fgets to take user input. If user enters exit command program terminates through exit(0).
    For any other command other than exit, program writes the command into the cmd string which is shared by the server program. Once the write is completed, it updates the wr_update status to 1, rd_update status to 0. We will program the server later in this project such a way that it will execute the command once the update is received.
    The last thing we are doing in client program is waiting for ack bit to be 1. This update will be received from server. Once server executes the command and writes the response it updates ack bit from 0 to 1. After that update only we will go and retrieve the response.
Client program compilation.
    Execute the client program using following command.
gcc client.c -o CLIENT -lrt

Server program development, compilation

Server program development.

server.c: server program to receive user command from client, execute it and send the response back to the client program.

/*
 * Program: server.c
 * Purpose: server side code for cli development project 
 */
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <signal.h>
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <string.h>
#include <fcntl.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdint.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <semaphore.h>
#include  <sys/types.h>
#include  <sys/ipc.h>
#include  <sys/shm.h>

struct cli
{
  int wr_update;
  int rd_update;
  int ack;
  char cmd[100];  /* command space */
  char response[100]; /* response space */
};
#define KEY 0x5444
/*********************************** Main ************************************/
int main(void) {
  struct cli *p;
  int numCharacters;   /* number of character received per command line */
  int ret;

  /* shared memory file descriptor */
  int shm_fd;

  shm_fd = shmget(KEY, sizeof(struct cli), IPC_CREAT | 0666);
  if (shm_fd < 0) {
    printf("%s",strerror(errno));
    exit(1);
  }
  p = shmat(shm_fd, NULL, 0);  /* attach */
  if (p == 0) {
    printf("*** shmat error (server) ***\n");
    exit(1);
  }
  p->wr_update = 0;
  p->rd_update = 0;
    p->ack = 0;
  printf("Server:Waiting for input command\n");
  while (1) {
    if(p->wr_update == 1 && p->rd_update == 0){
      printf("Condition passed\n");
      /* read from the shared memory object */
      printf("Server:command received:%s", p->cmd);
      numCharacters = strlen(p->cmd);
      if (numCharacters > 0) {
        /* parse command and validate command arguments here */
        strcat(p->cmd,"executed successfully\n");
        /* send response */
        sprintf(p->response, "%s\n", p->cmd);
        p->wr_update = 0;
        p->rd_update = 1;
        p->ack = 1;
      } else {
        break;
      }
    }
  }
  /* remove semaphores*/
  shmdt(p);
  shmctl(shm_fd,IPC_RMID,0);
  return 0;
}
    Let's go through the server code written in server.c. structure cli is shared between client and server processes. In server.c program wr_update, rd_update are initialized to 0 first. Once the program gets the update of wr_update as 1, rd_update as 0 it executes the command and writes the response using the response buffer. Once it completes writing on to the response buffer it changes the ack bit to 1.
    The client program keeps retrieving the response buffer till the ack bit status changes from 0 to 1. When client gets the ack bit as 1 it reads the command output from response buffer.
Server program compilation.
    Execute the server program using following command.

gcc server.c -o SERVER -lrt

Project execution, functionality testing

Project execution, functionality
    Execute the server program by executing the SERVER executable as shown in following figure.
server program execution output
course thumb
Cli project server execution.
    Execute the client program by executing the CLIENT executable as shown in following figure.
client program execution output
course thumb
Cli project client execution.
    Execute the server program first. Then execute the client program in another session. Now if you execute any command the server will respond with appropriate response.

Project exercises, extensions

Project exercise 1.
    This is a debugging exercise. In this project enter a command with length more than 100 characters. You will notice that program will give segmentation fault. Figure out the reason and fix the problem so that it can accept commands upto 200 characters long.
Project exercise 2.
    In the server program write additional functions to parse the command string from client into different arguments and validate each of the argument. It should validate to make sure total number of arguments matches for the respective command. It should also validate the type(int,double etc...) of each argument.

  | 
Share

Articles


Linux software

More Articles Categories

Technical articles

Prepare for software jobs


Test your skills