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 EPERMExplanation / 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);