proj: Add exam-2020W-2A
This commit is contained in:
37
proj/exam-2020W-2A/src/Makefile
Normal file
37
proj/exam-2020W-2A/src/Makefile
Normal file
@@ -0,0 +1,37 @@
|
||||
##
|
||||
# Makefile for server and client libs.
|
||||
#
|
||||
|
||||
|
||||
# config
|
||||
|
||||
DEFS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -D_POSIX_C_SOURCE=200809 -D_XOPEN_SOURCE
|
||||
override CFLAGS += -Wall -g -std=c99 -pedantic $(DEFS)
|
||||
override LDFLAGS +=
|
||||
override LIBS += -pthread -lrt
|
||||
|
||||
# objects for the server to build
|
||||
OBJS = server_lib.o
|
||||
|
||||
|
||||
# rules
|
||||
|
||||
all: $(OBJS) client osue-tool
|
||||
make -f Makefile.student all
|
||||
|
||||
client: client.o
|
||||
gcc $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
osue-tool: osue-tool.c
|
||||
gcc -o $@ $^
|
||||
|
||||
%.o: %.c
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
strip -S $@
|
||||
|
||||
clean:
|
||||
make -f Makefile.student clean
|
||||
rm -f client client.o $(OBJS) osue-tool
|
||||
|
||||
distclean: clean
|
||||
rm -rf *.txt __pycache__
|
||||
38
proj/exam-2020W-2A/src/Makefile.student
Normal file
38
proj/exam-2020W-2A/src/Makefile.student
Normal file
@@ -0,0 +1,38 @@
|
||||
##
|
||||
# Makefile for server.
|
||||
#
|
||||
|
||||
|
||||
# config
|
||||
|
||||
DEFS = -D_DEFAULT_SOURCE -D_BSD_SOURCE -D_SVID_SOURCE -D_POSIX_C_SOURCE=200809 -D_XOPEN_SOURCE
|
||||
override CFLAGS += -Wall -g -std=c99 -pedantic $(DEFS)
|
||||
override LDFLAGS +=
|
||||
override LIBS += -pthread -lrt
|
||||
|
||||
# objects to build
|
||||
OBJS = server.o
|
||||
|
||||
# objects to link (already built)
|
||||
LDOBJS = server_lib.o
|
||||
|
||||
|
||||
# rules
|
||||
|
||||
.PHONY : all clean
|
||||
|
||||
all: server
|
||||
|
||||
server: $(OBJS) $(LDOBJS)
|
||||
gcc $(LDFLAGS) -o $@ $^ $(LIBS)
|
||||
|
||||
%.o: %.c
|
||||
gcc $(CFLAGS) -c -o $@ $<
|
||||
|
||||
clean:
|
||||
rm -f server $(OBJS)
|
||||
|
||||
|
||||
# dependencies
|
||||
|
||||
server.o: server.c
|
||||
114
proj/exam-2020W-2A/src/client.c
Normal file
114
proj/exam-2020W-2A/src/client.c
Normal file
@@ -0,0 +1,114 @@
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <unistd.h>
|
||||
|
||||
#define BUFSIZE 1024
|
||||
|
||||
/** Name of the executable (for printing messages). */
|
||||
char *program_name = "<not yet set>";
|
||||
|
||||
/** Structure for the arguments. */
|
||||
struct arguments {
|
||||
const char *port_str;
|
||||
const char *argument;
|
||||
};
|
||||
|
||||
/** Print a usage message and exit. */
|
||||
void usage(const char *msg) {
|
||||
fprintf(stderr, "Usage: %s [-p PORT] ARGUMENT\n%s\n", program_name, msg);
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
void print_message(const char *msg) {
|
||||
fprintf(stderr, "[%s] %s\n", program_name, msg);
|
||||
}
|
||||
|
||||
/** Print an error message and exit with EXIT_FAILURE. */
|
||||
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);
|
||||
}
|
||||
|
||||
/** 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->argument = argv[optind];
|
||||
}
|
||||
|
||||
FILE *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("localhost", port_str, &hints, &ai);
|
||||
if (res != 0)
|
||||
error_exit("getaddrinfo()");
|
||||
|
||||
int connfd = socket(ai->ai_family, ai->ai_socktype, ai->ai_protocol);
|
||||
if (connfd < 0)
|
||||
error_exit("socket()");
|
||||
|
||||
if (connect(connfd, ai->ai_addr, ai->ai_addrlen) < 0)
|
||||
error_exit("connect()");
|
||||
|
||||
freeaddrinfo(ai);
|
||||
|
||||
return fdopen(connfd, "w+");
|
||||
}
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
struct arguments args;
|
||||
program_name = argv[0];
|
||||
|
||||
parse_arguments(argc, argv, &args);
|
||||
|
||||
FILE *fp = setup_connection(args.port_str);
|
||||
|
||||
fprintf(fp, "%s", args.argument);
|
||||
fflush(fp);
|
||||
shutdown(fileno(fp), SHUT_WR);
|
||||
|
||||
char *lineptr = NULL;
|
||||
size_t n = 0;
|
||||
print_message("reading response:");
|
||||
while (getline(&lineptr, &n, fp) != -1) {
|
||||
fprintf(stdout, "[%s] >>> %s", program_name, lineptr);
|
||||
fflush(stdout);
|
||||
}
|
||||
fprintf(stderr, "\n");
|
||||
print_message("exiting regularly");
|
||||
|
||||
return 0;
|
||||
}
|
||||
25
proj/exam-2020W-2A/src/osue-tool.c
Normal file
25
proj/exam-2020W-2A/src/osue-tool.c
Normal file
@@ -0,0 +1,25 @@
|
||||
/*
|
||||
* Take a single string as an argument and format it into 32-character blocks.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
int main(int argc, char **argv) {
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "invalid number of arguments\n");
|
||||
exit(1);
|
||||
}
|
||||
int i;
|
||||
for (i = 0; i < strlen(argv[1]); i++) {
|
||||
int c = argv[1][i];
|
||||
printf("%c", c);
|
||||
if (i % 32 == 31)
|
||||
printf("\n");
|
||||
}
|
||||
if (i % 32 != 0)
|
||||
printf("\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
171
proj/exam-2020W-2A/src/server.c
Normal file
171
proj/exam-2020W-2A/src/server.c
Normal file
@@ -0,0 +1,171 @@
|
||||
#include "server.h"
|
||||
|
||||
#include <errno.h>
|
||||
#include <fcntl.h>
|
||||
#include <limits.h>
|
||||
#include <netdb.h>
|
||||
#include <signal.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
char *program_name = "<not yet set>";
|
||||
|
||||
/*
|
||||
* Signal handler and quit flag for synchronization loop.
|
||||
* DO NOT CHANGE THIS!
|
||||
*/
|
||||
volatile sig_atomic_t quit = 0;
|
||||
|
||||
static void signal_handler(int sig) {
|
||||
quit = 1;
|
||||
}
|
||||
|
||||
/*
|
||||
* Prints a usage message and exits.
|
||||
* @param msg Additional output message.
|
||||
*/
|
||||
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.
|
||||
*/
|
||||
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);
|
||||
}
|
||||
|
||||
/************************************************************************
|
||||
* Task 1 - Setup the connection for the server
|
||||
*
|
||||
* Create a passive socket of domain AF_INET and type SOCK_STREAM.
|
||||
* Listen for connections on the port given by the argument `port_str'.
|
||||
* Return the file descriptor of the communication socket.
|
||||
*
|
||||
* WARNING: The communication socket is NOT the connection socket,
|
||||
* DO NOT USE accept(2) IN THIS FUNCTION!
|
||||
*
|
||||
* @param port_str The port string.
|
||||
* @return File descriptor of the communication socket.
|
||||
*
|
||||
* Hints: getaddrinfo(3), socket(2), listen(2)
|
||||
************************************************************************/
|
||||
int setup_connection(const char *port_str) {
|
||||
// put your code here
|
||||
|
||||
// Replace with a meaningful return value
|
||||
return -1;
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Task 2 - Handle client connections
|
||||
*
|
||||
* Wait for a connection on the communication socket and accept it.
|
||||
*
|
||||
* Read the argument transmitted by the client from the connection and save it
|
||||
* to a buffer that can hold a C-string with up to MAX_ARGUMENT_LEN characters.
|
||||
*
|
||||
* Call execute_command() (or the respective DEMO function) with the command
|
||||
* and the argument.
|
||||
*
|
||||
* Read the result from the file pointer returned by execute_command() and send
|
||||
* it back to the client. Send the string "COMMAND_FAILED" if execute_command()
|
||||
* fails.
|
||||
*
|
||||
* @param sockfd File descriptor of the communication socket.
|
||||
* @param command Command to execute.
|
||||
*
|
||||
* Hints: accept(2), fdopen(3), fgets(3), fprintf(3), fclose(3)
|
||||
******************************************************************************/
|
||||
void server_request_handler(int sockfd, const char *command) {
|
||||
// put your code here
|
||||
}
|
||||
|
||||
/*******************************************************************************
|
||||
* Task 3 - Execute command
|
||||
*
|
||||
* Create a pipe for redirection of standard output of the child process.
|
||||
*
|
||||
* Fork a child process, close stdin of the new process and redirect stdout to
|
||||
* the pipe.
|
||||
*
|
||||
* Execute the command in the variable 'command' with the single argument in
|
||||
* the variable 'argument'.
|
||||
*
|
||||
* Wait for the child process to finish. If the child process exits with
|
||||
* exit-status 0, create a FILE object for the read-end of the pipe and return
|
||||
* it. Return NULL if the child process fails.
|
||||
*
|
||||
* Do not read from the pipe in this function.
|
||||
*
|
||||
* @param command The command that will be executed.
|
||||
* @param argument The argument for the command.
|
||||
*
|
||||
* @return Address of the FILE-object for the read end of the child's standard
|
||||
* output pipe.
|
||||
*
|
||||
* Hints: pipe(2), dup2(2), fork(2), exec(3), fdopen(3), wait(3)
|
||||
******************************************************************************/
|
||||
FILE *execute_command(const char *command, const char *argument) {
|
||||
// put your code here
|
||||
|
||||
// Replace with a meaningful return value
|
||||
return NULL;
|
||||
}
|
||||
|
||||
/*
|
||||
* Main program
|
||||
* @param argc Number of elements in argv
|
||||
* @param argv Array of command line arguments
|
||||
* @return EXIT_SUCCESS if everything is okay
|
||||
*/
|
||||
int main(int argc, char *argv[]) {
|
||||
// DO NOT CHANGE THE FOLLOWING DECLARATIONS
|
||||
int sockfd = -1;
|
||||
struct arguments args; // struct for storing the parsed arguments
|
||||
program_name = argv[0];
|
||||
|
||||
// Register signal handlers
|
||||
struct sigaction s;
|
||||
s.sa_handler = signal_handler;
|
||||
s.sa_flags = 0; // no SA_RESTART!
|
||||
if (sigfillset(&s.sa_mask) < 0) {
|
||||
error_exit("sigfillset");
|
||||
}
|
||||
if (sigaction(SIGINT, &s, NULL) < 0) {
|
||||
error_exit("sigaction SIGINT");
|
||||
}
|
||||
if (sigaction(SIGTERM, &s, NULL) < 0) {
|
||||
error_exit("sigaction SIGTERM");
|
||||
}
|
||||
// register function to free resources at normal process termination
|
||||
if (atexit(free_resources) == -1) {
|
||||
error_exit("atexit failed");
|
||||
}
|
||||
parse_arguments(argc, argv, &args);
|
||||
|
||||
sockfd = setup_connection(args.port_str);
|
||||
//sockfd = DEMO_setup_connection(args.port_str);
|
||||
|
||||
while (!quit) {
|
||||
server_request_handler(sockfd, args.command);
|
||||
//DEMO_server_request_handler(sockfd, args.command, 0); // internally calls execute_command()
|
||||
//DEMO_server_request_handler(sockfd, args.command, USE_DEMO); // internally calls DEMO_execute_command()
|
||||
}
|
||||
|
||||
(void)fprintf(stderr, "server exiting regularly\n");
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
33
proj/exam-2020W-2A/src/server.h
Normal file
33
proj/exam-2020W-2A/src/server.h
Normal file
@@ -0,0 +1,33 @@
|
||||
/**
|
||||
* @brief Declarations used by the server application.
|
||||
**/
|
||||
|
||||
#ifndef _SERVE_H_
|
||||
#define _SERVE_H_
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
#define USE_DEMO 0x01
|
||||
|
||||
#define MAX_ARGUMENT_LEN 1024
|
||||
|
||||
/** Structure for the arguments. */
|
||||
struct arguments {
|
||||
const char *port_str;
|
||||
const char *command;
|
||||
};
|
||||
|
||||
void parse_arguments(int argc, char *argv[], struct arguments *args);
|
||||
void free_resources(void);
|
||||
|
||||
// forward declaration of student functions.
|
||||
int setup_connection(const char *port_str);
|
||||
void server_request_handler(int sockfd, const char *command);
|
||||
FILE *execute_command(const char *command, const char *argument);
|
||||
|
||||
// demo solutions
|
||||
int DEMO_setup_connection(const char *port_str);
|
||||
void DEMO_server_request_handler(int sockfd, const char *command, int flags);
|
||||
FILE *DEMO_execute_command(const char *command, const char *argument);
|
||||
|
||||
#endif
|
||||
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