proj: Add exam-2020W-2A
This commit is contained in:
260
proj/exam-2020W-2A/src/server_lib.c
Normal file
260
proj/exam-2020W-2A/src/server_lib.c
Normal file
@@ -0,0 +1,260 @@
|
||||
#include <arpa/inet.h>
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#include "server.h"
|
||||
|
||||
extern char *program_name;
|
||||
|
||||
extern volatile sig_atomic_t quit;
|
||||
|
||||
/*
|
||||
* Prints a usage message and exits.
|
||||
* @param msg Additional output message.
|
||||
*/
|
||||
static void usage(const char *msg) {
|
||||
fprintf(stderr, "Usage: %s [-p PORT] COMMAND\n%s\n", program_name, msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
/*
|
||||
* Print an error message and exit with EXIT_FAILURE.
|
||||
* @param msg Additional output message.
|
||||
*/
|
||||
static void error_exit(const char *msg) {
|
||||
if (errno == 0)
|
||||
fprintf(stderr, "[%s] [ERROR] %s\n", program_name, msg);
|
||||
else
|
||||
fprintf(stderr, "[%s] [ERROR] %s - %s\n", program_name, msg,
|
||||
strerror(errno));
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
static void print_error(const char *msg) {
|
||||
fprintf(stderr, "[%s] [ERROR] %s - %s\n", program_name, msg,
|
||||
strerror(errno));
|
||||
}
|
||||
|
||||
static void print_message(const char *msg) {
|
||||
fprintf(stderr, "[%s] %s\n", program_name, msg);
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Function freeing the resources.
|
||||
*/
|
||||
void free_resources(void) {
|
||||
}
|
||||
|
||||
/*
|
||||
* Parse Arguments
|
||||
*/
|
||||
void parse_arguments(int argc, char *argv[], struct arguments *args) {
|
||||
memset(args, 0, sizeof(*args));
|
||||
|
||||
int opt;
|
||||
while ((opt = getopt(argc, argv, "p:")) != -1) {
|
||||
switch (opt) {
|
||||
case 'p':
|
||||
if (args->port_str)
|
||||
usage("-p option allowed only once");
|
||||
args->port_str = optarg;
|
||||
break;
|
||||
default:
|
||||
usage("invalid option");
|
||||
}
|
||||
}
|
||||
if (args->port_str == NULL)
|
||||
args->port_str = "2020";
|
||||
|
||||
if (argc - optind != 1)
|
||||
usage("invalid number of arguments");
|
||||
|
||||
args->command = argv[optind];
|
||||
}
|
||||
|
||||
/*
|
||||
* Custom getaddrinfo implementation for localhost only.
|
||||
*/
|
||||
int getaddrinfo(const char *node, const char *service,
|
||||
const struct addrinfo *hints, struct addrinfo **res) {
|
||||
struct in_addr localhost;
|
||||
localhost.s_addr = htonl(0x7f000001); // 127.0.0.1
|
||||
|
||||
// check hostname (allow only `localhost' or any string which `inet_aton()'
|
||||
// converts to 127.0.0.1)
|
||||
struct in_addr node_addr;
|
||||
if (node != NULL &&
|
||||
(inet_aton(node, &node_addr) == 0 ||
|
||||
node_addr.s_addr != localhost.s_addr) &&
|
||||
strcmp(node, "localhost") != 0)
|
||||
return EAI_NONAME;
|
||||
|
||||
// check port (allow only numerical ports)
|
||||
char *endptr;
|
||||
int port = strtol(service, &endptr, 10);
|
||||
|
||||
if (*endptr != '\0')
|
||||
return EAI_NONAME;
|
||||
|
||||
// check hints (NULL is acceptable)
|
||||
if (hints != NULL) {
|
||||
// check family
|
||||
if (hints->ai_family != AF_UNSPEC && hints->ai_family != AF_INET)
|
||||
return EAI_FAMILY;
|
||||
|
||||
// check socket type
|
||||
if (hints->ai_socktype != 0 && hints->ai_socktype != SOCK_STREAM)
|
||||
return EAI_SOCKTYPE;
|
||||
|
||||
// check protocol
|
||||
if (hints->ai_protocol != 0 && hints->ai_protocol != IPPROTO_TCP)
|
||||
return EAI_SOCKTYPE;
|
||||
}
|
||||
|
||||
// allocate memory for result
|
||||
*res = malloc(sizeof(struct addrinfo));
|
||||
struct sockaddr_in *sin = malloc(sizeof(struct sockaddr_in));
|
||||
|
||||
if (*res == NULL || sin == NULL)
|
||||
return EAI_MEMORY;
|
||||
|
||||
memset(sin, 0, sizeof(struct sockaddr_in));
|
||||
sin->sin_family = AF_INET;
|
||||
sin->sin_addr = localhost;
|
||||
sin->sin_port = htons(port);
|
||||
|
||||
(*res)->ai_flags = (AI_V4MAPPED | AI_ADDRCONFIG);
|
||||
(*res)->ai_family = AF_INET;
|
||||
(*res)->ai_socktype = SOCK_STREAM;
|
||||
(*res)->ai_protocol = IPPROTO_TCP;
|
||||
(*res)->ai_addrlen = sizeof(struct sockaddr_in);
|
||||
(*res)->ai_addr = (struct sockaddr *)sin;
|
||||
(*res)->ai_canonname = NULL;
|
||||
(*res)->ai_next = NULL;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/******************************************************************************
|
||||
* DEMO FUNCTIONS
|
||||
*****************************************************************************/
|
||||
int DEMO_setup_connection(const char *port_str) {
|
||||
struct addrinfo hints, *ai;
|
||||
memset(&hints, 0, sizeof hints);
|
||||
hints.ai_family = AF_INET;
|
||||
hints.ai_socktype = SOCK_STREAM;
|
||||
hints.ai_flags = AI_PASSIVE;
|
||||
|
||||
int res = getaddrinfo(NULL, port_str, &hints, &ai);
|
||||
if (res != 0)
|
||||
error_exit("getaddrinfo() failed");
|
||||
|
||||
int sockfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||
if (sockfd < 0)
|
||||
error_exit("socket() failed");
|
||||
|
||||
int reuse = 1;
|
||||
if (setsockopt(sockfd, SOL_SOCKET, SO_REUSEADDR, &reuse, sizeof(reuse)) < 0)
|
||||
error_exit("setsockopt() failed");
|
||||
|
||||
if (bind(sockfd, ai->ai_addr, ai->ai_addrlen) < 0)
|
||||
error_exit("bind() failed");
|
||||
|
||||
freeaddrinfo(ai);
|
||||
|
||||
if (listen(sockfd, 1) < 0)
|
||||
error_exit("listen() failed");
|
||||
return sockfd;
|
||||
}
|
||||
|
||||
void DEMO_server_request_handler(int sockfd, const char *command, int flags) {
|
||||
int connfd = accept(sockfd, NULL, NULL);
|
||||
if (connfd < 0)
|
||||
error_exit("accept() failed");
|
||||
|
||||
print_message("Accepted connection");
|
||||
|
||||
FILE *fps = fdopen(connfd, "w+");
|
||||
|
||||
char argument[MAX_ARGUMENT_LEN + 1] = {0};
|
||||
fgets(argument, sizeof(argument), fps);
|
||||
|
||||
FILE *fpc = NULL;
|
||||
if (flags & USE_DEMO) {
|
||||
print_message("Calling DEMO_execute_command()");
|
||||
fpc = DEMO_execute_command(command, argument);
|
||||
} else {
|
||||
print_message("Calling execute_command()");
|
||||
fpc = execute_command(command, argument);
|
||||
}
|
||||
|
||||
if (fpc) {
|
||||
int c;
|
||||
while ((c = fgetc(fpc)) != EOF)
|
||||
fputc(c, fps);
|
||||
fclose(fpc);
|
||||
} else {
|
||||
fputs("COMMAND_FAILED", fps);
|
||||
}
|
||||
fflush(fps);
|
||||
fclose(fps);
|
||||
}
|
||||
|
||||
FILE *DEMO_execute_command(const char *command, const char *argument) {
|
||||
// file stream to read the output of the process
|
||||
FILE *proc_output = NULL;
|
||||
|
||||
// file descriptors for the pipe for parent-child process communication
|
||||
int c2p[2];
|
||||
|
||||
// create the pipes
|
||||
if (pipe(c2p) == -1)
|
||||
error_exit("cannot create pipe");
|
||||
|
||||
pid_t pid = fork();
|
||||
switch (pid) {
|
||||
case 0:
|
||||
// duplicate write end to stdout
|
||||
if (dup2(c2p[1], STDOUT_FILENO) == -1) {
|
||||
error_exit("dup2() failed");
|
||||
}
|
||||
close(c2p[0]);
|
||||
close(STDIN_FILENO);
|
||||
|
||||
// exec the command
|
||||
if (execlp(command, command, argument, NULL) == -1) {
|
||||
error_exit("exec() failed");
|
||||
}
|
||||
case -1:
|
||||
error_exit("fork() failed");
|
||||
|
||||
default:
|
||||
// parent process
|
||||
proc_output = fdopen(c2p[0], "r");
|
||||
if (proc_output == NULL) {
|
||||
error_exit("fdopen() failed");
|
||||
}
|
||||
close(c2p[1]);
|
||||
|
||||
int stat = 0;
|
||||
wait(&stat);
|
||||
|
||||
if (WIFEXITED(stat) && WEXITSTATUS(stat) == 0) {
|
||||
print_message("child process exited normally");
|
||||
} else {
|
||||
print_error("child process exited abnormally");
|
||||
proc_output = NULL;
|
||||
}
|
||||
}
|
||||
return proc_output;
|
||||
}
|
||||
Reference in New Issue
Block a user