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

Hello, please help me with my systems assignment using c! The code looks quite l

ID: 3845763 • Letter: H

Question

Hello, please help me with my systems assignment using c! The code looks quite long but he adds a lot of fluff, the amount of coding is minimal. Please finish the two "Your Code Here" sections within the server_getFileByFirstLetter.c program. Thank you so much!

Purpose:

To practice:

Sockets

Low-level I/O in C

Processes and signalling

Assignment:

Please finish the server of a client-server application. The client asks the user for a letter (a character in {A-Z,a-z}), and then sends that letter to the server. The server malloc()s memory to store both the loop variable i and the file descriptor from accept() for talking to the client. It then pthread_create()s a child to handle the client and gives it the malloc()ed memory. All of the threads except the last (i == (NUM_CLIENTS_TO_SERVE-1)) should be detached threads. The last thread should be an ordinary, joinable thread, which the parent thread should join with outside the loop.

The child thread should:

Get the thread id (coming in as the loop variable) and file descriptor. It then free()s that malloc()ed memory.

Gets the letter from the client

Attempts to open the current directory ("."). If it cannot open that directory then it:

sends CANT_READ_DIR_CODE back to the client (in network endian),

prints its id and "id num: Cannot read directory ",

returns NULL.

Iterates thru the directory to looking for a file (not a directory or anything else) whose name starts with the letter obtained from the client.

If the server finds no matching file then it

sends NO_MATCH_CODE back to the client (in network endian),

prints its id and "id num: No matching file ",

returns NULL.

Attempts to open the file for reading. If it cannot open the file then it:

sends CANT_READ_FILE_CODE back to the client (in network endian),

prints its id and "id num: Cannot read file ",

returns NULL.

Prints its id and "id num: Sending , bytes "

Sends the size of the file as a uint32_t integer to the client (in network endian)

Sends the bytes of the file to the client. It should send the file in buffers of bytes of size BUFFER_LEN.

close()s what it should close.

returns NULL.

Code:

Sample output:

To properly test this program I made a directory called Dir:

I also made a file that I did have permission to read called holdYourHeadUp.txt, and one that I did not called youCantOpenMe.txt. The contents of the second file can be whatever you want, but to make it so you cannot open it say:

My directory now has:

Client output: Server output:
       
  $ ./server   Please enter port number to monopolize [1025-65535]: 2000            
  $ ./client   Machine name [localhost]? (I just pressed enter)   Port number? 2000  Please enter a letter to look for: A  No matching file found for A            
No file begins with A:
  0: No matching file            
  $ ./client   Machine name [localhost]? (I just pressed enter)   Port number? 2000  Please enter a letter to look for: D  No matching file found for D            
Dir begins with D, but it is not a file.
  1: No matching file            
  $ ./client   Machine name [localhost]? (I just pressed enter)   Port number? 2000  Please enter a letter to look for: y  Matching file found for y, but could not open            
The server cannot open youCantOpenMe.txt.
  2: Cannot read file youCantOpenMe.txt            
  $ ./client   Machine name [localhost]? (I just pressed enter)   Port number? 2000  Please enter a letter to look for: h  The file that matches h has size 437  And if it's bad  Don't let it get you down, you can take it  And if it hurts  Don't let them see you cry, you can make it    Hold your head up, woman  Hold your head up, woman  Hold your head up, woman  Hold your head high  Hold your head up, woman  Hold your head up, woman  Hold your head up, woman  Hold your head high    And if they stare  Just let them burn their eyes on you moving  And if they shout  Don't let it change a thing that you're doing            
The server can open holdYourHeadUp.txt, so it sends it to the client.
  3: Sending holdYourHeadUp.txt, 437 bytes  And if it's bad  Don't let it get you down, you can take it  And if it hurts  Don't let them see you cry, you can make it    Hold your head up, woman  Hold your head up, woman  Hold your head up, woman  Hold your head high  Hold your head up, woman  Hold your head up, woman  Hold your head up, woman  Hold your head high    And if they stare  Just let them burn their eyes on you moving  And if they shout  Don't let it change a thing that you're doing            

Explanation / Answer

//   client_getFileByFirstLetter.c          

#include   "getFileByFirstLetter.h"
#include   <ctype.h>   // isalpha()
void   obtainUrlAndPort   (int       urlLen, char*       url, int*       portPtr) {
// I. Application validity check:
if ( (url == NULL) || (portPtr == NULL) ) {
fprintf(stderr,"BUG: NULL ptr to obtainUrlAndPort() ");
exit(EXIT_FAILURE);
}
  
if (urlLen <= 1) {
fprintf(stderr,"BUG: Bad string length to obtainUrlAndPort() ");
exit(EXIT_FAILURE);
}
  
// II. Get server name and port number:
// II.A. Get server name:
printf("Machine name [%s]? ", DEFAULT_HOSTNAME);
fgets(url,urlLen,stdin);
  
char*   cPtr   = strchr(url,' ');
  
if (cPtr != NULL) *cPtr = '';
  
if (url[0] == '') strncpy(url,DEFAULT_HOSTNAME,urlLen);
  
// II.B. Get port numbe:
char   buffer[BUFFER_LEN];
  
printf("Port number? ");
fgets(buffer,BUFFER_LEN,stdin);
  
*portPtr = strtol(buffer,NULL,10);
  
// III. Finished:
}
// PURPOSE: To attempt to connect to the server named 'url' at port 'port'.
//   Returns file-descriptor of socket if successful, or '-1' otherwise.
int   attemptToConnectToServer   (const char*   url, int       port) {
// I. Application validity check:
if (url == NULL) {
fprintf(stderr,"BUG: NULL ptr to attemptToConnectToServer() ");
exit(EXIT_FAILURE);
}
  
// II. Attempt to connect to server:
// II.A. Create a socket:
int socketDescriptor = socket(AF_INET /* AF_INET domain */, SOCK_STREAM /* Reliable TCP */, 0);
  
// II.B. Ask DNS about 'url':
struct addrinfo* hostPtr;
int status = getaddrinfo(url, NULL, NULL, &hostPtr);
  
if (status != 0) {
fprintf(stderr,gai_strerror(status));
return(-1);
}
  
// II.C. Attempt to connect to server:
struct sockaddr_in server;
// Clear server datastruct
memset(&server, 0, sizeof(server));
  
// Use TCP/IP
server.sin_family = AF_INET;
  
// Tell port # in proper network byte order
server.sin_port = htons(port);
  
// Copy connectivity info from info on server ("hostPtr")
server.sin_addr.s_addr =
((struct sockaddr_in *)hostPtr->ai_addr)->sin_addr.s_addr;
  
status = connect(socketDescriptor,(struct sockaddr*)&server,sizeof(server));
  
if (status < 0) {
fprintf(stderr,"Could not connect %s:%d ",url,port);
return(-1);
}
  
// III. Finished:
return(socketDescriptor);
}
// PURPOSE: To do the work of the application. Gets letter from user, sends
//   it to server over file-descriptor 'fd', and either prints text of
//   returned error code, or prints returned file. No return value.
void   communicateWithServer   (int   fd) {
// I. Application validity check:
  
// II. Do work of application:
// II.A. Get letter from user:
char   buffer[BUFFER_LEN+1];
  
do {
printf("Please enter a letter to look for: ");
fgets(buffer,BUFFER_LEN,stdin);
}
while ( !isalpha(buffer[0]) );
  
// II.B. Send letter to server:
write(fd,buffer,1);
  
// II.C. Get response from server:
uint32_t   fileSize;
  
read(fd,&fileSize,sizeof(fileSize));
fileSize = ntohl(fileSize);
  
// II.D. Interpret server response:
switch (fileSize) {
case NO_MATCH_CODE :
printf("No matching file found for %c. ",buffer[0]);
break;
  
case CANT_READ_FILE_CODE :
printf("Matching file found for %c, but you do not have permission to open. ",buffer[0]);
break;
  
case CANT_READ_DIR_CODE :
printf("Found directory, not file. Cannot open. ",buffer[0]);
break;
  
default : {
unsigned int   totalNumBytesRead   = 0;
int       numBytesRead;
  
printf(" The file that matches %c has size %u ",buffer[0],fileSize);
  
while ( (totalNumBytesRead < fileSize)           && ( (numBytesRead = read(fd,buffer,BUFFER_LEN)) > 0)) {
buffer[numBytesRead]   = '';
printf("%s ",buffer);
totalNumBytesRead   += (unsigned int)numBytesRead;
}
}
}
// III. Finished:
}
// PURPOSE: To do the work of the client. Ignores command line parameters.
//   Returns 'EXIT_SUCCESS' to OS on success or 'EXIT_FAILURE' otherwise.
int   main   () {
char   url[BUFFER_LEN];
int       port;
int       fd;
  
obtainUrlAndPort(BUFFER_LEN, url, &port);
fd   = attemptToConnectToServer(url, port);
  
if (fd < 0) exit(EXIT_FAILURE);
  
communicateWithServer(fd);
close(fd);
return(EXIT_SUCCESS);
}

================================================================================


//server_getFileByFirstLetter.c

#include   "getFileByFirstLetter.h"
#include   <signal.h>       // For sigaction()
#include   <sys/wait.h>   // For waitpid()
#include   <dirent.h>       // For opendir(), readdir(), closedir()
#include <unistd.h> // Had to include for R_OK permission

const int LO_LEGAL_PORT = 1025;
const int HI_LEGAL_PORT = 65535;
const int ERROR_FD = -1;
const int NUM_CLIENTS_TO_SERVE = 4;
typedef int BOOL;
void sigChildHandler(int sig) {
   int status;
pid_t pid;

   while ( (pid = waitpid(-1, &status, WNOHANG)) > 0) printf("Zombie child %d finished. ", pid);
   return;
  
   // III. Finished:
}
int getServerFileDescriptor(int port, const char* progName) {
// I. Application validity check:
if (progName == NULL) {
fprintf(stderr,"Error: NULL ptr to file descriptor. ");
exit(EXIT_FAILURE);
}
  
// II. Attempt to get socket file descriptor and bind it to 'port':
// YOUR CODE HERE
  
int socketDesc = socket(AF_INET, /*AF_INET domain*/ SOCK_STREAM, /*Reliable TCP*/ 0); //Create socket
struct sockaddr_in socketInf; //Make sockaddr_in struct
memset(&socketInf,'',sizeof(socketInf)); //Populate with zeroes
socketInf.sin_family = AF_INET; //Using TCP/IP
socketInf.sin_port = htons(port); //Let port know in network endian
socketInf.sin_addr.s_addr = INADDR_ANY; //Allow machine to connect
int status = bind(socketDesc, (struct sockaddr*)&socketInf, sizeof(socketInf)); //Bind socket with port
  
if (status < 0) {
fprintf(stderr,"Error: Could not bind to port %d ",port);
exit(EXIT_FAILURE);
}
  
listen(socketDesc, 5);
  
// III. Finished:
  
return(socketDesc);
}

// PURPOSE: To install 'sigChildHandler()' as the signal simple handler for
//   the 'SIGCHLD' signal. Tells OS to restarts system calls if receives
//   'SIGCHLD'.
void installSigChildHandler() {
// I. Application validity check:

// II. Install 'sigChildHandler()' as the 'SIGCHLD' handler:
// YOUR CODE HERE
  
struct sigaction actn; //Make new action struct
memset(&actn,'',sizeof(struct sigaction));
sigemptyset(&actn.sa_mask);
actn.sa_flags = SA_NOCLDSTOP | SA_RESTART;;
actn.sa_handler = sigChildHandler;
sigaction(SIGCHLD,&actn,NULL); //Install 'sigChildHandler()' as 'SIGCHLD' handler
  
// III. Finished:
}

// PURPOSE: To ask the user which port to attempt to monopolize, and to return
//   entered port number.
int getPort() {
   // I. Application validity check:

   // II. Get port number
   int port;

   do {
       char buffer[BUFFER_LEN];
       printf("Please enter port number to monopolize [%d-%d]: ", LO_LEGAL_PORT, HI_LEGAL_PORT);
       fgets(buffer, BUFFER_LEN, stdin);
       port = strtol(buffer, NULL, 10);
   } while ((port < LO_LEGAL_PORT) || (port > HI_LEGAL_PORT));

   // III. Finished:
   return (port);
}

// PURPOSE: To do the work of handling the client. Communication with the
//   client take place using file-descriptor 'fd'. No return value.
void handleClient(int fd) {
   // I. Application validity check:

   if (fd < 0) {
       fprintf(stderr, "Error: Illegal file descriptor. ");
       exit(EXIT_FAILURE);
   }

   // II. Handle the client:
   // YOUR CODE HERE

   DIR* dir = NULL; //Create directory
   FILE* fPtr = NULL; //Create file pointer
   struct dirent *dirEntry = NULL; //Create directory entry
char buffer[BUFFER_LEN + 1]; //Create buffer

   uint32_t fSize = 0;
   uint32_t response = 0;
  
   struct stat fStat;
   BOOL   foundFile = 0;
   memset(buffer, 0, sizeof(buffer));
   memset(&fStat, 0, sizeof(fStat));

   printf(" ");
   read(fd, buffer, 1); //Get char from client
   dir = opendir(".");
   if(NULL == dir) {
       response = htonl(CANT_READ_DIR_CODE); //Send CANT_READ_DIR_CODE back to client
       write(fd, &response, sizeof(response));
       printf("Cannot read directory. ");
       return;
   }
  
   BOOL regFile = 0;
  
   while((dirEntry = readdir(dir)) != NULL ) { //Read directory
       stat(dirEntry->d_name, &fStat);
       if(dirEntry->d_name[0] == buffer[0]) {
           foundFile = 1;
           if(S_ISREG(fStat.st_mode)) {
               regFile = 1;
               break;
           }
       }
   }

   if(foundFile) { //Check file permissions
       if(regFile) {
           foundFile = 0;
           int readPerm = access(dirEntry->d_name, R_OK);
           if(readPerm == -1) {
               response = htonl(CANT_READ_FILE_CODE); //Send CANT_READ_FILE_CODE to client
               write(fd, &response, sizeof(response));
               printf("Cannot read file %s .", dirEntry->d_name);
               return;
           } else foundFile = 1;
       } else {
response = htonl(CANT_READ_DIR_CODE); //Send CANT_READ_DIR_CODE back to client
           write(fd, &response, sizeof(response));
           printf("%c is the right character but it's not a file. ",buffer[0]);
           printf("No matching file. ");
           return;
       }
   } else {
       response = htonl(NO_MATCH_CODE); //Send NO_MATCH_CODE to client
       write(fd, &response, sizeof(response));
       printf("No file begins with %c: ",buffer[0]);
       printf("No matching file. ");
       return;
   }

   if(foundFile) { //All good, send file to client
       uint32_t ffd = -1;
       fPtr = fopen(dirEntry->d_name, "r"); //Find file in current directory
       ffd = fileno(fPtr);
       fseek(fPtr, 0L, SEEK_END);
       fSize = ftell(fPtr);
       fclose(fPtr);
       fPtr = fopen(dirEntry->d_name, "r");
       ffd = fileno(fPtr);
       printf("Opening %s and sending to client. ",dirEntry->d_name);
       printf("Sending %d bytes to %s. ", fSize, dirEntry->d_name);

       fSize = htonl(fSize);
       write(fd, &fSize, sizeof(fSize));
       fSize = ntohl(fSize);

       uint32_t totalBytesSent = 0;

       int numBytesRead = 0;
       int writtenBytes = 0;
       while(totalBytesSent < fSize) {
           writtenBytes = 0;
           numBytesRead = read(ffd, buffer, BUFFER_LEN);
           while(writtenBytes < numBytesRead) writtenBytes += write(fd, buffer + writtenBytes, numBytesRead - writtenBytes);
           buffer[writtenBytes] = '';
           printf("%s", buffer);
           totalBytesSent += numBytesRead;
       }
   }
   // III. Finished:
}

// PURPOSE: To serve the clients using file-descriptor 'listenFd' to tell
//   when a client has connected. Each client is handled by its own child
//   process. Both the parent and the child processes close unnecesary
//   file-descriptorors. Serves 'NUM_CLIENTS_TO_SERVE' clients, then quits.
//   No return value.
void doServer(int listenFd) {
   // I. Application validity check:
   if (listenFd < 0) {
       fprintf(stderr, "Error: Illegal file descriptor. ");
       exit(EXIT_FAILURE);
   }
  
   // II. Do the work of the server:
listen(listenFd, NUM_CLIENTS_TO_SERVE);
   int i, clientLen;
   pid_t zombieChildProcess;
   int clientFd;
  
for (i = 0; i < NUM_CLIENTS_TO_SERVE; i++) {
  
       // YOUR CODE HERE
  
       if ((clientFd = accept(listenFd, NULL, NULL)) < 0) {
           if (errno == EINTR) continue;
           else perror("Could not accept request from client. ");
       } else {
           if ((zombieChildProcess = fork()) == 0) {
               handleClient(clientFd);
               close(clientFd);
               exit(0);
           }
           close(clientFd);
       }
   }

   // III. Finished:
}

int main(int argc, char* argv[]) {
   // I. Application validity check:

   // II. Do server:
   int port = getPort();
   int socketFd = getServerFileDescriptor(port, argv[0]);

   installSigChildHandler();
   doServer(socketFd);
   printf(" ");
   // III. Finished:
   return (EXIT_SUCCESS);
}

===========================================================================

//getFileByFirstLetter.h          

#include   <stdlib.h>
#include   <stdio.h>
#include   <string.h>
#include   <sys/socket.h>   // For socket()
#include   <netinet/in.h>   // For sockaddr_in and htons()
#include   <netdb.h>   // For getaddrinfo()
#include   <errno.h>   // For errno var
#include   <sys/stat.h>   // For open(), read(),write(), stat()
#include   <fcntl.h>   // and close()

#define       BUFFER_LEN       256
#define       NO_MATCH_CODE       ((uint32_t) -1)
#define       CANT_READ_FILE_CODE   ((uint32_t) -2)
#define       CANT_READ_DIR_CODE   ((uint32_t) -3)
#define DEFAULT_HOSTNAME "localhost.localdomain"
//#define       DEFAULT_HOSTNAME   "server.gentech.private" //Had to change default hostname to match my computer

===================================================================================