Skip to content
Snippets Groups Projects
Commit 52c21eea authored by Joey Le's avatar Joey Le
Browse files

Answered questions and did a few more functions and changed stuff

parent 4ec5ab7a
Branches
No related tags found
No related merge requests found
1. How does the remote client determine when a command's output is fully received from the server, and what techniques can be used to handle partial reads or ensure complete message transmission? 1. How does the remote client determine when a command's output is fully received from the server, and what techniques can be used to handle partial reads or ensure complete message transmission?
_answer here_ _It determines by specific things, so for this one we use a null terminator or eof to see when a message is over. We can buffer incoming data, or predefined limiters, or length prefixing_
2. This week's lecture on TCP explains that it is a reliable stream protocol rather than a message-oriented one. Since TCP does not preserve message boundaries, how should a networked shell protocol define and detect the beginning and end of a command sent over a TCP connection? What challenges arise if this is not handled correctly? 2. This week's lecture on TCP explains that it is a reliable stream protocol rather than a message-oriented one. Since TCP does not preserve message boundaries, how should a networked shell protocol define and detect the beginning and end of a command sent over a TCP connection? What challenges arise if this is not handled correctly?
_answer here_ _It should frame the message, so that it has proper lengths, or delimiters, and EOF characters and stuff that like. It needs proper handling so that the commands may be interpreted correctly_
3. Describe the general differences between stateful and stateless protocols. 3. Describe the general differences between stateful and stateless protocols.
_answer here_ _Stateful protocols maintain information across multiple requests, while stateless protcols don't._
4. Our lecture this week stated that UDP is "unreliable". If that is the case, why would we ever use it? 4. Our lecture this week stated that UDP is "unreliable". If that is the case, why would we ever use it?
_answer here_ _UDP is low latency and a very fast so it's very useful when you need information quickly._
5. What interface/abstraction is provided by the operating system to enable applications to use network communications? 5. What interface/abstraction is provided by the operating system to enable applications to use network communications?
_answer here_ _Operating systems use socket interfaces, and that allows these applications to perform network communications over protocols._
\ No newline at end of file \ No newline at end of file
...@@ -74,6 +74,7 @@ int start_server(char *ifaces, int port, int is_threaded){ ...@@ -74,6 +74,7 @@ int start_server(char *ifaces, int port, int is_threaded){
*/ */
int stop_server(int svr_socket){ int stop_server(int svr_socket){
return close(svr_socket); return close(svr_socket);
} }
/* /*
...@@ -130,9 +131,14 @@ int boot_server(char *ifaces, int port){ ...@@ -130,9 +131,14 @@ int boot_server(char *ifaces, int port){
} }
addr.sin_family = AF_INET; addr.sin_family = AF_INET;
addr.sin_addr.s_addr = inet_addr(ifaces); if (inet_pton(AF_INET, ifaces, &addr.sin_addr) <= 0) {
perror("inet_pton");
close(svr_socket);
return ERR_RDSH_SERVER;
}
addr.sin_port = htons(port); addr.sin_port = htons(port);
ret = bind(svr_socket, (const struct sockaddr *) &addr, sizeof(struct sockaddr_in)); ret = bind(svr_socket, (const struct sockaddr *) &addr, sizeof(struct sockaddr_in));
if (ret == -1 ) { if (ret == -1 ) {
...@@ -149,6 +155,7 @@ int boot_server(char *ifaces, int port){ ...@@ -149,6 +155,7 @@ int boot_server(char *ifaces, int port){
ret = listen(svr_socket, 20); ret = listen(svr_socket, 20);
if (ret == -1) { if (ret == -1) {
perror("listen"); perror("listen");
close(svr_socket);
return ERR_RDSH_COMMUNICATION; return ERR_RDSH_COMMUNICATION;
} }
...@@ -197,16 +204,58 @@ int boot_server(char *ifaces, int port){ ...@@ -197,16 +204,58 @@ int boot_server(char *ifaces, int port){
* connections, and negative values terminate the server. * connections, and negative values terminate the server.
* *
*/ */
int process_cli_requests(int svr_socket) { int process_cli_requests(int svr_socket) {
int cli_socket; int cli_socket;
char buffer[RDSH_COMM_BUFF_SZ];
ssize_t bytes_received;
int rc = OK; int rc = OK;
while (1) { while (1) {
// TODO use the accept syscall to create cli_socket // Accept a client connection
// and then exec_client_requests(cli_socket) cli_socket = accept(svr_socket, NULL, NULL);
if (cli_socket == -1) {
perror("accept");
rc = ERR_RDSH_COMMUNICATION;
break;
}
// Receive data from the client
bytes_received = recv(cli_socket, buffer, sizeof(buffer), 0);
if (bytes_received < 0) {
perror("recv");
close(cli_socket);
rc = ERR_RDSH_COMMUNICATION;
break;
} else if (bytes_received == 0) {
// Client disconnected
close(cli_socket);
continue;
}
// Check for special commands
if (strncmp(buffer, "stop-server", strlen("stop-server")) == 0) {
printf("Client requested server to stop\n");
rc = OK_EXIT;
close(cli_socket);
break;
}
// Handle client requests
rc = exec_client_requests(cli_socket);
if (rc < 0) {
fprintf(stderr, "Error processing client requests: %d\n", rc);
close(cli_socket);
break;
}
// Close the client socket
close(cli_socket);
printf("Client socket closed properly\n");
} }
stop_server(cli_socket); // Clean up and stop the server
stop_server(svr_socket);
return rc; return rc;
} }
...@@ -251,35 +300,73 @@ int process_cli_requests(int svr_socket){ ...@@ -251,35 +300,73 @@ int process_cli_requests(int svr_socket){
* ERR_RDSH_COMMUNICATION: A catch all for any socket() related send * ERR_RDSH_COMMUNICATION: A catch all for any socket() related send
* or receive errors. * or receive errors.
*/ */
int exec_client_requests(int cli_socket) { int exec_client_requests(int cli_socket) {
char *io_buff;
int io_size; int io_size;
command_list_t cmd_list; command_list_t cmd_list;
int rc; int rc = OK;
int cmd_rc;
int last_rc;
char *io_buff;
// Allocate a buffer for I/O operations
io_buff = malloc(RDSH_COMM_BUFF_SZ); io_buff = malloc(RDSH_COMM_BUFF_SZ);
if (io_buff == NULL) { if (io_buff == NULL) {
return ERR_RDSH_SERVER; return ERR_RDSH_SERVER; // Return server error if allocation fails
} }
while (1) { while (1) {
// TODO use recv() syscall to get input // Receive input from the client
io_size = recv(cli_socket, io_buff, RDSH_COMM_BUFF_SZ, 0);
if (io_size < 0) {
perror("recv");
free(io_buff);
return ERR_RDSH_COMMUNICATION; // Return communication error
} else if (io_size == 0) {
// Client disconnected
free(io_buff);
return OK; // Return OK to accept another client
}
// TODO build up a cmd_list // Null-terminate the received data
io_buff[io_size] = '\0';
// Check for special commands
if (strncmp(io_buff, "exit", strlen("exit")) == 0) {
// Client sent the `exit` command
free(io_buff);
return OK; // Return OK to accept another client
} else if (strncmp(io_buff, "stop-server", strlen("stop-server")) == 0) {
// Client sent the `stop-server` command
free(io_buff);
return OK_EXIT; // Return OK_EXIT to stop the server
}
// TODO rsh_execute_pipeline to run your cmd_list // Parse the input into a command list
rc = parse_pipeline(io_buff, &cmd_list);
if (rc != OK) {
// Handle parsing error
send_message_string(cli_socket, CMD_ERR_RDSH_EXEC);
send_message_eof(cli_socket);
continue; // Continue to the next iteration
}
// TODO send appropriate respones with send_message_string // Execute the command pipeline
// - error constants for failures rc = rsh_execute_pipeline(cli_socket, &cmd_list);
// - buffer contents from execute commands if (rc < 0) {
// - etc. // Handle execution error
send_message_string(cli_socket, CMD_ERR_RDSH_EXEC);
send_message_eof(cli_socket);
continue; // Continue to the next iteration
}
// TODO send_message_eof when done // Free memory for each command's buffer
for (int i = 0; i < cmd_list.num; i++) {
free(cmd_list.commands[i]._cmd_buffer);
}
} }
return WARN_RDSH_NOT_IMPL; // Clean up (this will never be reached due to the while(1) loop)
free(io_buff);
return WARN_RDSH_NOT_IMPL; // Default return value
} }
/* /*
...@@ -296,14 +383,22 @@ int exec_client_requests(int cli_socket) { ...@@ -296,14 +383,22 @@ int exec_client_requests(int cli_socket) {
* ERR_RDSH_COMMUNICATION: The send() socket call returned an error or if * ERR_RDSH_COMMUNICATION: The send() socket call returned an error or if
* we were unable to send the EOF character. * we were unable to send the EOF character.
*/ */
int send_message_eof(int cli_socket){
int send_len = (int)sizeof(RDSH_EOF_CHAR);
int sent_len;
sent_len = send(cli_socket, &RDSH_EOF_CHAR, send_len, 0);
if (sent_len != send_len){ int send_message_string(int cli_socket, char *buff) {
int bytes_sent;
// Send the message
bytes_sent = send(cli_socket, buff, strlen(buff), 0);
if (bytes_sent < 0) {
perror("send");
return ERR_RDSH_COMMUNICATION; return ERR_RDSH_COMMUNICATION;
} }
// Send the EOF character
if (send_message_eof(cli_socket) != OK) {
return ERR_RDSH_COMMUNICATION;
}
return OK; return OK;
} }
...@@ -327,8 +422,15 @@ int send_message_eof(int cli_socket){ ...@@ -327,8 +422,15 @@ int send_message_eof(int cli_socket){
* we were unable to send the message followed by the EOF character. * we were unable to send the message followed by the EOF character.
*/ */
int send_message_string(int cli_socket, char *buff){ int send_message_string(int cli_socket, char *buff){
//TODO implement writing to cli_socket with send() int bytes_sent;
return WARN_RDSH_NOT_IMPL;
bytes_sent = send(cli_socket, buff, strlen(buff), 0);
if (bytes_sent < 0) {
perror("send");
return ERR_RDSH_COMMUNICATION;
}
return OK;
} }
...@@ -370,28 +472,75 @@ int send_message_string(int cli_socket, char *buff){ ...@@ -370,28 +472,75 @@ int send_message_string(int cli_socket, char *buff){
* macro that we discussed during our fork/exec lecture to * macro that we discussed during our fork/exec lecture to
* get this value. * get this value.
*/ */
int rsh_execute_pipeline(int cli_sock, command_list_t *clist) { int rsh_execute_pipeline(int cli_sock, command_list_t *clist) {
int pipes[clist->num - 1][2]; // Array of pipes int pipes[clist->num - 1][2]; // Array of pipes
pid_t pids[clist->num]; pid_t pids[clist->num];
int pids_st[clist->num]; // Array to store process IDs int pids_st[clist->num]; // Array to store process statuses
Built_In_Cmds bi_cmd;
int exit_code; int exit_code;
// Create all necessary pipes // Create all necessary pipes
for (int i = 0; i < clist->num - 1; i++) { for (int i = 0; i < clist->num - 1; i++) {
if (pipe(pipes[i]) == -1) { if (pipe(pipes[i]) == -1) {
perror("pipe"); perror("pipe");
exit(EXIT_FAILURE); return ERR_RDSH_COMMUNICATION;
} }
} }
// Fork and execute each command in the pipeline
for (int i = 0; i < clist->num; i++) { for (int i = 0; i < clist->num; i++) {
// TODO this is basically the same as the piped fork/exec assignment, except for where you connect the begin and end of the pipeline (hint: cli_sock) pids[i] = fork();
if (pids[i] == -1) {
perror("fork");
return ERR_RDSH_COMMUNICATION;
}
if (pids[i] == 0) { // Child process
// Redirect input for the first command
if (i == 0) {
if (dup2(cli_sock, STDIN_FILENO) == -1) {
perror("dup2 stdin");
exit(EXIT_FAILURE);
}
} else {
// Redirect stdin to the read end of the previous pipe
if (dup2(pipes[i - 1][0], STDIN_FILENO) == -1) {
perror("dup2 stdin");
exit(EXIT_FAILURE);
}
}
// TODO HINT you can dup2(cli_sock with STDIN_FILENO, STDOUT_FILENO, etc. // Redirect output for the last command
if (i == clist->num - 1) {
// Redirect stdout and stderr to the client socket
if (dup2(cli_sock, STDOUT_FILENO) == -1) {
perror("dup2 stdout");
exit(EXIT_FAILURE);
}
if (dup2(cli_sock, STDERR_FILENO) == -1) {
perror("dup2 stderr");
exit(EXIT_FAILURE);
}
} else {
// Redirect stdout to the write end of the current pipe
if (dup2(pipes[i][1], STDOUT_FILENO) == -1) {
perror("dup2 stdout");
exit(EXIT_FAILURE);
}
}
// Close all pipe ends in the child process
for (int j = 0; j < clist->num - 1; j++) {
close(pipes[j][0]);
close(pipes[j][1]);
} }
// Execute the command
execvp(clist->commands[i].argv[0], clist->commands[i].argv);
perror("execvp");
exit(EXIT_FAILURE);
}
}
// Parent process: close all pipe ends // Parent process: close all pipe ends
for (int i = 0; i < clist->num - 1; i++) { for (int i = 0; i < clist->num - 1; i++) {
...@@ -404,15 +553,15 @@ int rsh_execute_pipeline(int cli_sock, command_list_t *clist) { ...@@ -404,15 +553,15 @@ int rsh_execute_pipeline(int cli_sock, command_list_t *clist) {
waitpid(pids[i], &pids_st[i], 0); waitpid(pids[i], &pids_st[i], 0);
} }
//by default get exit code of last process // Determine the exit code
//use this as the return value
exit_code = WEXITSTATUS(pids_st[clist->num - 1]); exit_code = WEXITSTATUS(pids_st[clist->num - 1]);
for (int i = 0; i < clist->num; i++) { for (int i = 0; i < clist->num; i++) {
//if any commands in the pipeline are EXIT_SC if (WEXITSTATUS(pids_st[i]) == EXIT_SC) {
//return that to enable the caller to react
if (WEXITSTATUS(pids_st[i]) == EXIT_SC)
exit_code = EXIT_SC; exit_code = EXIT_SC;
break;
}
} }
return exit_code; return exit_code;
} }
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment