Academic Integrity: tutoring, explanations, and feedback — we don’t complete graded work or submit on a student’s behalf.

CS 214: Systems Programming, Spring 2017 Assignment 3: Wherefore Art Thou, File?

ID: 3819653 • Letter: C

Question

CS 214: Systems Programming, Spring 2017 Assignment 3: Wherefore Art Thou, File? In this project you will write a remote file server that is mostly transparent to the user. You must first complete the base program'segment below, butyou arethen free to choose which other extensions you implement, with one proviso you cannot implement extension Dunless you also implement extension C. If you complete all parts, you will receive 160 credit. Base Program: (+80%) You will be providing an interface much like the standard file system calls to allow easy use of files across the network. You should write netopen. Yetread. Yetwrite and netclose.All of these calls should use the same syntax and have the same overall functionality astheir local counterparts (except where expressly exempted), but they will ship theirparameters your file server where the actual file operations will happen. To your client code, it will look like open and netopen, read and netread, write and netwrite and close and netclose work almost identically, cucept your net commands are working on files on another machine. netopen(const char pathname, int flags) The argument flags must include one of the following access modes: O RDONLY. o WRONL. or o RDWR. These request opening the file read only, write-only, or read write, respectively RETURN VALUE netopen0 returns the new file descriptor. or -1 in the caller's contenxtifan error occurred (in which case, enmo is set appropriately). In order to avoid error and disambiguate your file descriptors from the system's, make your file descriptors negative but not-I ERRORS check open manpage for definition) reqiured: EACCES EINTR EISDIR EROFS optional (you may wantineod) ENFILE EWOULDBLocK EPERM

Explanation / Answer

libnetfiles.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <sys/ioctl.h>
#include <netinet/in.h>
#include <netdb.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>

#include "netfileserver.h"
#include "libnetfiles.h"


void * get_in_addr(struct sockaddr * sa) {

   if (sa->sa_family == AF_INET) {
       return &(((struct sockaddr_in *)sa)->sin_addr);
   }

   return &(((struct sockaddr_in6*)sa)->sin6_addr);
}

int netserverinit(char * hostname) {

    // Declare socket address struct
    struct addrinfo hints, *servinfo, *p;
    char s[INET6_ADDRSTRLEN]; // 46
    int sockfd, flag;

    // Fill in struct with zeroes
   bzero((char *)&hints, sizeof(hints));

   // Manually initialize the address information
    hints.ai_family = AF_UNSPEC;           // IPv4 or IPv6
    hints.ai_socktype = SOCK_STREAM;       // Sets as TCP

    // Automatically initialize the address information from host
    if ((flag = getaddrinfo(hostname, SERV_TCP_PORT_STR, &hints, &servinfo)) != 0) {
        fprintf(stderr, "Client: %s ", gai_strerror(flag));
        return -1;
    }

    // Loop through server information for the appropriate address information to start a socket
   for (p = servinfo; p != NULL; p = p->ai_next) {

       // Attempt to open socket with address information
       if ((sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
           perror("Client");
           continue;
       }

       // Connect to the server
       if (connect(sockfd, p->ai_addr, p->ai_addrlen) < 0) {

           // Close socket
           close(sockfd);
           continue;
       }

       // Successful connection
       break;
   }

   // Check if socket was not bound
   if (p == NULL) {
       return -1;
   }

   // Get IP address from socket address
   inet_ntop(p->ai_family, get_in_addr((struct sockaddr *)p->ai_addr), s, sizeof(s));

   // Free server information
   freeaddrinfo(servinfo);

   return sockfd;
}

void writeCommand(int s_fd, int type, int flag, int size, int status) {

   // Declare integer buffer
   int iBuf[4];

   // Write in packet
   iBuf[0] = htonl(type);
   iBuf[1] = htonl(flag);
   iBuf[2] = htonl(size);
   iBuf[3] = htonl(status);
   writen(s_fd, (char *)&iBuf, 16);
}

void readCommandServer(int sockfd, Command_packet * packet) {

   // Declare integer buffer
   int iBuf[4];

   // Read bytes
   readn(sockfd, (char *)&iBuf, 16);
   packet->type = ntohl(iBuf[0]);
   packet->flag = ntohl(iBuf[1]);
   packet->size = ntohl(iBuf[2]);
   packet->status = ntohl(iBuf[3]);
}

void * readCommand(int sockfd) {

   // Allocate memory for command packet struct
   Command_packet * packet = (Command_packet *)malloc(sizeof(Command_packet));

   // Declare integer buffer
   int iBuf[4];

   // Read bytes
   readn(sockfd, (char *)(&iBuf[0]), 16);
   packet->type = ntohl(iBuf[0]);
   packet->flag = ntohl(iBuf[1]);
   packet->size = ntohl(iBuf[2]);
   packet->status = ntohl(iBuf[3]);
  
   return (void *)packet;
}

int readn(int fd, char * ptr, int nbytes) {
  
   // Declare and initialize counters
   int nleft, nread;
   nleft = nbytes;

   // Loop through reading bytes until EOF is found
   while (nleft > 0) {
       nread = read(fd, ptr, nleft);

       if (nread < 0) {           // Error
           return(nread);
       } else if (nread == 0) {   // EOF
           break;
       }
       nleft-=nread;
       ptr+=nread;
   }

   // Return the number of bytes successfully read
   return (nbytes-nleft);
}

int writen(int fd, char * ptr, int nbytes) {

   // Declare and initialize counters
   int nleft, nwritten;
   nleft = nbytes;

   // Loop through writing bytes until all bytes are written
   while (nleft > 0) {
       nwritten = write(fd, ptr, nleft);

       if (nwritten < 1) {       // Error
           return (nwritten);
       }
       nleft-=nwritten;
       ptr+=nwritten;
   }

   // Return the number of bytes successfully written
   return (nbytes-nleft);
}

int netopen(const char * pathname, int sockfd, int flags) {

   // Send command to server
   writeCommand(sockfd, 1, flags, strlen(pathname), 0);

   // Write the filename to the socket
   errno = 0;
   writen(sockfd, (char *)pathname, strlen(pathname));
   if (errno != 0) {
       perror("Client");
       errno = 0;
   }

   // Receive response from server
   Command_packet * cPack = (Command_packet *)readCommand(sockfd);

   // Get file descriptor index and free command packet
   errno = cPack->flag;
   int fd = cPack->status;
   free(cPack);

   if (fd < 0) {
       perror("Client");
   }

   // Return file descriptor index received from server
   return fd;
}

int netclose(int sockfd, int fd) {

   // Send command to server
   writeCommand(sockfd, 2, 0, 0, fd);

   // Receive response from server
   Command_packet * cPack = (Command_packet *)readCommand(sockfd);

   // Get status and free command packet
   errno = 0;
   errno = cPack->flag;
   int stat = cPack->status;
   free(cPack);

   if (stat < 0) {
       perror("Client");
   }

   // Return status received from server
   return stat;
}

ssize_t netread(int sockfd, int fd, void * buf, size_t nbyte) {

   // Send command to server
   writeCommand(sockfd, 3, 0, nbyte, fd);

   // Read character into buffer
   errno = 0;
   readn(sockfd, (char *)buf, nbyte);
   if (errno != 0) {
       perror("Client");
       errno = 0;
   }

   // Receive response from server
   Command_packet * cPack = (Command_packet *)readCommand(sockfd);

   // Get status and free command packet
   int size = cPack->size;
   errno = cPack->flag;
   free(cPack);

   // Check if the number of bytes read is correct
   if (size != nbyte) {
       perror("Client");
   }

   // Return size of read from server
   return size;
}

ssize_t netwrite(int sockfd, int fd, const void * buf, size_t nbyte) {

   // Send command to server
   writeCommand(sockfd, 4, 0, nbyte, fd);

   // Send buffer to be written to server
   errno = 0;
   writen(sockfd, (char *)buf, nbyte);
   if (errno != 0) {
       perror("Client");
       errno = 0;
   }

   // Receive response from server
   Command_packet * cPack = (Command_packet *)readCommand(sockfd);

   // Get status and free command packet
   int size = cPack->size;
   errno = cPack->flag;
   free(cPack);

   // Check if the number of bytes written is correct
   if (size != nbyte) {
       perror("Client");
   }

   // Return status received from server
   return size;
}

libnetfiles.h

// Structs
typedef struct command_packet {
   int type;       // type of command (open, close, read, write)
   int flag;       // ???
   int size;       // number of bytes in read or write
   int status;       // file descriptor
} Command_packet;

// Macros
#define SERV_TCP_PORT_STR "9000"
//#define HOST_NOT_FOUND 132 /* Host was not found in netserverinit */

// Functions
void * get_in_addr(struct sockaddr *sa);
int netserverinit(char * hostname);
void writeCommand(int sockfd, int type, int flag, int size, int status);
void readCommandServer(int sockfd, Command_packet * packet);
void * readCommand(int sockfd);
int readn(int fd, char * ptr, int nbytes);
int writen(int fd, char * ptr, int nbytes);
int netopen(const char *pathname, int sockfd, int flags);
int netclose(int sockfd, int fd);
ssize_t netread(int sockfd, int fildes, void *buf, size_t nbyte);
ssize_t netwrite(int sockfd, int fildes, const void *buf, size_t nbyte);

netcliet.c

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

#include "libnetfiles.h"

#define LOOP_BACK_ADDR "127.0.0.1"
#define IP_SIZE 50
#define IN_FILENAME_MAX 50
#define BUFFER_MAX 50

void get_server_ip(char * ip_str) {

   // Declare interface address structs
   struct ifaddrs *addrs, *temp;

   // Initialize addrs struct
   getifaddrs(&addrs);
   temp = addrs;

   // Traverse interface address linked list
   while (temp) {

       // Check if interface address is an IPv4
        if (temp->ifa_addr && temp->ifa_addr->sa_family == AF_INET) {

           // Fill in temp socket address
            struct sockaddr_in *t_sockaddr = (struct sockaddr_in *)temp->ifa_addr;

            // If temp socket address is not the LOOP_BACK_ADDR, copy into ip_str
            if (strcmp(LOOP_BACK_ADDR, inet_ntoa(t_sockaddr->sin_addr)) != 0) {
               strcpy(ip_str, inet_ntoa(t_sockaddr->sin_addr));
            }
        }

        // Go to next interface address struct
       temp = temp->ifa_next;
   }

   // Free interface address linked list
   freeifaddrs(addrs);
}

/* MAIN */
int main(int argc, char *argv[]) {

   // Declare buffers
   char ip_str[IP_SIZE];
   char filename[IN_FILENAME_MAX];
   char message[BUFFER_MAX];

   // Copy command line arguments to buffers
   strcpy(ip_str, argv[1]);
   strcpy(filename, argv[2]);
   strcpy(message, argv[3]);
  
   // Initialize server connection
   int sockfd = netserverinit(ip_str);

   // Open file
   int fd = netopen(filename, sockfd, O_RDWR);

   // Allocate size for buffer
   char * buf = (char *)malloc(sizeof(char)*BUFFER_MAX);
   int flag;

   if (fd > -1) {

       // Read from file
       netread(sockfd, fd, buf, BUFFER_MAX);

       // Write to file
       netwrite(sockfd, fd, message, BUFFER_MAX);

       // Close file
       flag = netclose(sockfd, fd);
   }

   // Free allocated memory
   free(buf);

   return 0;
}

netfileserver.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <netdb.h>
#include <ifaddrs.h>
#include <fcntl.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <pthread.h>
#include <errno.h>
#include <signal.h>

#include "netfileserver.h"
#include "libnetfiles.h"

// Structs
typedef struct thread_data {
   int * cli_fd;
   int client_id;
   Command_packet * cPtr;
} Thread_data;

// Functions
int executeClientCommands(Thread_data * td);

// Macros
#define SERV_TCP_PORT "9000"
#define BACKLOG 5
#define THREAD_MAX 100
#define LOOP_BACK_ADDR "127.0.0.1"
#define OPEN_FILES_MAX 100

// Globals
Open_File_Data openFiles[THREAD_MAX][OPEN_FILES_MAX];
int client_id = 0;
pthread_mutex_t m_lock = PTHREAD_MUTEX_INITIALIZER;
sig_atomic_t serv_sockfd;

void intHandler(int signum) {
   fcntl(serv_sockfd, F_SETFL, O_NONBLOCK);
}

int get_server_ip(char * ip_str) {

   // Declare interface address structs
   struct ifaddrs *addrs, *temp;

   // Initialize addrs struct
   if (getifaddrs(&addrs) < 0) {
       perror("Server");
       return -1;
   }
   temp = addrs;

   // Traverse interface address linked list
   while (temp) {

       // Check if interface address is an IPv4
        if (temp->ifa_addr && temp->ifa_addr->sa_family == AF_INET) {

           // Fill in temp socket address
            struct sockaddr_in *t_sockaddr = (struct sockaddr_in *)temp->ifa_addr;

            // If temp socket address is not the LOOP_BACK_ADDR, copy into ip_str
            if (strcmp(LOOP_BACK_ADDR, inet_ntoa(t_sockaddr->sin_addr)) != 0) {
               strcpy(ip_str, inet_ntoa(t_sockaddr->sin_addr));
               printf("%s ", ip_str);
               break;
            }
        }
        // Go to next interface address struct
       temp = temp->ifa_next;
   }

   // Free interface address linked list
   freeifaddrs(addrs);

   return 0;
}

int executeClientCommands(Thread_data * td) {

   // Initialize socket and client ID
   int * sockfd = td->cli_fd;
   Command_packet * cPtr = td->cPtr;

   // Loop through incoming client commands
   while (1) {

       // Receive command packet from client
       bzero(cPtr, sizeof(Command_packet));
       readCommandServer(*sockfd, cPtr);

       // Declare and initialize buffers, counters, and file descriptors
       int i, status, nbytes;
       int cli_id = client_id;
       int cmd_type = cPtr->type;
       int flag = cPtr->flag;
       int wr_size = cPtr->size;
       int fd_index = cPtr->status;

       // Check if file index is within the limit
       if (fd_index < 0 || fd_index > OPEN_FILES_MAX-1) {
           writeCommand(*sockfd, 0, 9, -1, -1);
           break;
       }

       // Allocate space for buffer
       pthread_mutex_lock(&m_lock);
       char * buf = (char *)malloc(sizeof(char)*(wr_size+1));
       pthread_mutex_unlock(&m_lock);
       FILE * fd;

       switch (cmd_type) {
           case 1: // Open

               // Zero buffer
               bzero(buf, (wr_size+1));

               // Read filename
               nbytes = readn(*sockfd, buf, wr_size);

               // Error check readn
               if (nbytes != wr_size) {
                   writeCommand(*sockfd, 0, errno, 0, -1);
                   free(buf);
                   break;
               }

               // Open file according to flag
               switch (flag) {
                   case O_RDONLY:
                       fd = fopen(buf, "r");
                       break;
                   case O_WRONLY:
                       fd = fopen(buf, "w");
                       break;
                   case O_RDWR:
                       fd = fopen(buf, "a+");
                       break;
                   default:
                       fd = NULL;
                       break;
               }
              
               // Error check file
               if (fd == NULL) {
                   writeCommand(*sockfd, 0, errno, 0, -1);
                   free(buf);
                   break;
               }

               // Loop through array to find first open fd_index
               for (i = 0; i < OPEN_FILES_MAX; i++) {
                   if (!openFiles[cli_id][i].isActive) {
                       break;
                   }
               }

               // Initialize open file array at corresponding fd_index
               openFiles[cli_id][i].fp = fd;
               openFiles[cli_id][i].isActive = 1;

               // Send response to client
               writeCommand(*sockfd, 0, 0, 0, i);

               // Free buffer
               free(buf);

               break;

           case 2: // Close

               // Check if file is active
               if (!openFiles[cli_id][fd_index].isActive) {
                   writeCommand(*sockfd, 0, 9, 0, -1);
                   break;
               }  

               // Close active file
               status = fclose(openFiles[cli_id][fd_index].fp);

               // Set file as inactive in open file array
               openFiles[cli_id][fd_index].isActive = 0;
              
               // Send message to client
               writeCommand(*sockfd, 0, 0, 0, status);
               break;

           case 3: // Read

               // Check if file is active
               if (!openFiles[cli_id][fd_index].isActive) {
                   writeCommand(*sockfd, 0, 9, -1, 0);
                   break;
               }

               // Get file descriptor and jump to front of file
               fd = openFiles[cli_id][fd_index].fp;
               fseek(fd, 0, SEEK_SET);

               // Zero out buffer
               bzero(buf, wr_size+1);
              
               // Read one byte at time from file across the network
               for (i = 0; i < wr_size && !feof(fd); i++) {
                   buf[i] = fgetc(fd);
               }

               // Send the bytes read to the client
               nbytes = writen(*sockfd, buf, wr_size);

               // Error check writen
               if (nbytes != wr_size){
                   writeCommand(*sockfd, 0, errno, -1, 0);
                   break;
               }

               // Send message to client
               writeCommand(*sockfd, 0, 0, wr_size, 0);

               // Free buffer
               free(buf);

               break;

           case 4: // Write

               // Check if file is active
               if (!openFiles[cli_id][fd_index].isActive) {
                   writeCommand(*sockfd, 0, 9, -1, 0);
                   break;
               }

               // Get file descriptor
               fd = openFiles[cli_id][fd_index].fp;

               // Zero out buffer
               bzero(buf, wr_size+1);

               // Read the bytes to be written from the client
               nbytes = readn(*sockfd, buf, wr_size);

               // Error check readn
               if (nbytes != wr_size){
                   writeCommand(*sockfd, 0, errno, -1, 0);
                   break;
               }

               // Write one byte at time from file across the network
               for (i = 0; i < wr_size; i++) {
                   fputc(buf[i], fd);
                   fflush(fd);
               }

               // Send message to client
               writeCommand(*sockfd, 0, 0, wr_size, 0);

               // Free buffer
               free(buf);
               break;

           default:
               // tell client to close the socket
               free(buf);
               close(*sockfd);
               return 1;
       }
   }
   return 1;
}

int main(int argc, char *argv[]) {

   // Initialize signal handler
   signal(SIGINT, intHandler);

   // Initialize openFiles array
   int i, j;
   for (i = 0; i < THREAD_MAX; i++) {
       for (j = 0; j < OPEN_FILES_MAX; j++) {
           openFiles[i][j].isActive = 0;
       }
   }

   // Declare and allocate memory for IP address of server
   char * ip_str = (char *)malloc(sizeof(char)*50);

   // Initialize IP address of server
   if (get_server_ip(ip_str) < 0) {
       return 0;
   }

   // Declare socket descriptors and client length
   int cli_fd;
   socklen_t cli_len;

   // Declare address information struct
   struct addrinfo hints, cli_addr, *servinfo, *p;

   // Fill in struct with zeroes
   bzero((char *) &hints, sizeof(hints));

   // Manaually initialize address information struct
   hints.ai_family = AF_UNSPEC;                   // IPv4 or IPv6
   hints.ai_socktype = SOCK_STREAM;               // Sets as TCP

   // Automatically initialize address information
   if (getaddrinfo(ip_str, SERV_TCP_PORT, &hints, &servinfo) != 0) {
       perror("Server");
       exit(1);
   }

   // Loop through server information for the appropriate address information to start a socket
   for (p = servinfo; p != NULL; p = p->ai_next) {

       // Attempt to open socket with address information
       if ((serv_sockfd = socket(p->ai_family, p->ai_socktype, p->ai_protocol)) < 0) {
           continue;
       }

       // Binds server socket to a specific port to prepare for listen()
       if (bind(serv_sockfd, p->ai_addr, p->ai_addrlen) < 0) {
           continue;
       }

       // Successful connection
       break;
   }

   // Check if socket was not bound
   if (p == NULL) {
       perror("Server");
   }

   // Free server information
   freeaddrinfo(servinfo);

   // Server waits for incoming connection requestions; serv_sockfd will be the socket to satisfy these requests
   if (listen(serv_sockfd, BACKLOG) < 0) {
       perror("Server");
   }

   // Initialize thread data
   pthread_t clientThreads[THREAD_MAX];
   Thread_data * td[THREAD_MAX];
   int flag = 0;
   i = 0;

   // Sits on accept, waiting for new clients
   while (i < THREAD_MAX) {

       // Initialize client socket size
       cli_len = sizeof(cli_addr);

       // Accept client socket
       cli_fd = accept(serv_sockfd, (struct sockaddr *) &cli_addr, &cli_len);

       // Error check accept()
       if (cli_fd < 0) {
           break;
       } else {
           td[i] = (Thread_data *)malloc(sizeof(Thread_data));
           td[i]->cli_fd = (int *)malloc(sizeof(int));
           *(td[i]->cli_fd) = cli_fd;
           td[i]->client_id = client_id;
           td[i]->cPtr = (Command_packet *)malloc(sizeof(Command_packet));
       }

       // Open thread for client and pass in client file descriptor
       if ((flag = pthread_create(&clientThreads[i], NULL, (void *)executeClientCommands, (Thread_data *)td[i]))) {
           continue;
       } else if (flag == 0) {
           i++;
           client_id++;
       }
   }

   // Join threads
   for (j = 0, flag = 0; j < i; j++) {

       // Join threads
       flag = pthread_join(clientThreads[j], NULL);
       free(td[i]);

       // Erroring checking
       if (flag) {
           fprintf(stderr, "Server: pthread_join() exited with status %d ", flag);
       }
   }

   // Free IP address of server
   free(ip_str);

   // Close socket
   close(serv_sockfd);

   return 0;
}

netfileserver.h

// Structs
typedef struct open_file_data {
   FILE * fp;
   int isActive;
} Open_File_Data;

// Functions
void intHandler(int signum);
int get_server_ip(char * ip_str);