Added missing homework 8 and 9
This commit is contained in:
parent
59dd4ecc7d
commit
25adb24a16
15 changed files with 1625 additions and 0 deletions
4
homework09/.gitignore
vendored
Normal file
4
homework09/.gitignore
vendored
Normal file
|
|
@ -0,0 +1,4 @@
|
|||
curlit
|
||||
*.o
|
||||
*.sh
|
||||
*.unit
|
||||
63
homework09/Makefile
Normal file
63
homework09/Makefile
Normal file
|
|
@ -0,0 +1,63 @@
|
|||
CC= gcc
|
||||
CFLAGS= -Wall -g -std=gnu99
|
||||
LD= gcc
|
||||
LDFLAGS= -L.
|
||||
TARGETS= timeit curlit
|
||||
|
||||
all: $(TARGETS)
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# TODO: Rules for object files and executables
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
timeit.o: timeit.c
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
socket.o: socket.c socket.h
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
curlit.o: curlit.c socket.h
|
||||
$(CC) $(CFLAGS) -c -o $@ $<
|
||||
|
||||
timeit: timeit.o
|
||||
$(LD) $(LDFLAGS) -o $@ $^
|
||||
|
||||
curlit: curlit.o socket.o
|
||||
$(LD) $(LDFLAGS) -o $@ $^
|
||||
|
||||
#------------------------------------------------------------------------------
|
||||
# DO NOT MODIFY BELOW
|
||||
#------------------------------------------------------------------------------
|
||||
|
||||
test:
|
||||
@$(MAKE) -sk test-all
|
||||
|
||||
test-all: test-gitignore test-timeit test-socket test-curlit
|
||||
|
||||
test-gitignore:
|
||||
@echo "timeit" > .gitignore
|
||||
@echo "curlit" > .gitignore
|
||||
@echo "*.o" >> .gitignore
|
||||
@echo "*.sh" >> .gitignore
|
||||
@echo "*.unit" >> .gitignore
|
||||
|
||||
test-timeit: timeit
|
||||
@curl -sLO https://www3.nd.edu/~pbui/teaching/cse.20289.sp25/static/txt/homework09/timeit.test.sh
|
||||
@chmod +x timeit.test.sh
|
||||
@./timeit.test.sh
|
||||
|
||||
test-socket: socket.unit
|
||||
@curl -sLO https://www3.nd.edu/~pbui/teaching/cse.20289.sp25/static/txt/homework09/socket.unit.sh
|
||||
@chmod +x socket.unit.sh
|
||||
@./socket.unit.sh
|
||||
|
||||
socket.unit: socket.unit.c socket.c
|
||||
$(CC) $(CFLAGS) -o $@ $^
|
||||
|
||||
test-curlit: curlit
|
||||
@curl -sLO https://www3.nd.edu/~pbui/teaching/cse.20289.sp25/static/txt/homework09/curlit.test.sh
|
||||
@chmod +x curlit.test.sh
|
||||
@./curlit.test.sh
|
||||
|
||||
clean:
|
||||
@rm -f $(TARGETS) *.o *.sh *.unit
|
||||
187
homework09/curlit.c
Normal file
187
homework09/curlit.c
Normal file
|
|
@ -0,0 +1,187 @@
|
|||
/* curlit.c: Simple HTTP client*/
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
#include <limits.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <netdb.h>
|
||||
|
||||
/* Constants */
|
||||
|
||||
#define HOST_DELIMITER "://"
|
||||
#define PATH_DELIMITER '/'
|
||||
#define PORT_DELIMITER ':'
|
||||
#define BILLION (1000000000.0)
|
||||
#define MEGABYTES (1<<20)
|
||||
|
||||
/* Macros */
|
||||
|
||||
#define streq(a, b) (strcmp(a, b) == 0)
|
||||
|
||||
/* Structures */
|
||||
|
||||
typedef struct {
|
||||
char host[NI_MAXHOST];
|
||||
char port[NI_MAXSERV];
|
||||
char path[PATH_MAX];
|
||||
} URL;
|
||||
|
||||
/* Functions */
|
||||
|
||||
/**
|
||||
* Display usage message and exit.
|
||||
* @param status Exit status.
|
||||
**/
|
||||
void usage(int status) {
|
||||
fprintf(stderr, "Usage: curlit [-h] URL\n");
|
||||
exit(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse URL string into URL structure.
|
||||
* @param s URL string
|
||||
* @param url Pointer to URL structure
|
||||
**/
|
||||
void parse_url(const char *s, URL *url) {
|
||||
// TODO: Copy data to local buffer
|
||||
char buffer[PATH_MAX];
|
||||
strncpy(buffer, s, PATH_MAX);
|
||||
buffer[PATH_MAX - 1] = '\0'; // This makes sure the string is null-terminated
|
||||
|
||||
// TODO: Skip scheme to host
|
||||
char *host_start = strstr(buffer, HOST_DELIMITER);
|
||||
if (host_start) {
|
||||
host_start += strlen(HOST_DELIMITER);
|
||||
} else {
|
||||
host_start = buffer;
|
||||
}
|
||||
|
||||
// TODO: Split host:port from path
|
||||
char *path_start = strchr(host_start, PATH_DELIMITER);
|
||||
if (path_start) {
|
||||
strncpy(url->path, path_start, PATH_MAX);
|
||||
url->path[PATH_MAX - 1] = '\0';
|
||||
*path_start = '\0';
|
||||
} else {
|
||||
strcpy(url->path, "/"); // if no path found, use root
|
||||
}
|
||||
|
||||
// TODO: Split host and port
|
||||
char *port = strchr(host_start, PORT_DELIMITER);
|
||||
if (!port) {
|
||||
strcpy(url->port, "80"); // the default port
|
||||
} else {
|
||||
*port = '\0'; // had to modify from the suggested code due to an error with types
|
||||
port++;
|
||||
strncpy(url->port, port, NI_MAXSERV);
|
||||
url->port[NI_MAXSERV - 1] = '\0';
|
||||
}
|
||||
|
||||
// TODO: Copy components to URL
|
||||
strncpy(url->host, host_start, NI_MAXHOST);
|
||||
url->host[NI_MAXHOST - 1] = '\0';
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetch contents of URL and print to standard out.
|
||||
*
|
||||
* Print elapsed time and bandwidth to standard error.
|
||||
* @param s URL string
|
||||
* @param url Pointer to URL structure
|
||||
* @return true if client is able to read all of the content (or if the
|
||||
* content length is unset), otherwise false
|
||||
**/
|
||||
bool fetch_url(URL *url) {
|
||||
// TODO: Grab start time
|
||||
struct timespec start_time, end_time;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||
|
||||
// TODO: Connect to remote host and port
|
||||
FILE *client_socket = socket_dial(url->host, url->port);
|
||||
if (!client_socket) {
|
||||
fprintf(stderr, "Failed to connect to %s:%s\n", url->host, url->port);
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO: Send request to server
|
||||
fprintf(client_socket, "GET %s HTTP/1.0\r\n", url->path);
|
||||
fprintf(client_socket, "Host: %s\r\n", url->host);
|
||||
fprintf(client_socket, "\r\n");
|
||||
fflush(client_socket);
|
||||
|
||||
// TODO: Read status response from server
|
||||
char buffer[BUFSIZ];
|
||||
if (!fgets(buffer, BUFSIZ, client_socket)) {
|
||||
fprintf(stderr, "Failed to read server status response\n");
|
||||
fclose(client_socket);
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_status_ok = (strstr(buffer, "200 OK") != NULL); // set flag that checks for 200 OK status
|
||||
|
||||
// TODO: Read response headers from server
|
||||
size_t content_length = 0;
|
||||
while (fgets(buffer, BUFSIZ, client_socket) && buffer[0] != '\r' && buffer[0] != '\n') {
|
||||
sscanf(buffer, "Content-Length: %lu", &content_length);
|
||||
}
|
||||
|
||||
// TODO: Read response body from server
|
||||
size_t bytes_read = 0;
|
||||
size_t total_bytes = 0;
|
||||
|
||||
while ((bytes_read = fread(buffer, 1, BUFSIZ, client_socket)) > 0) {
|
||||
size_t bytes_written = fwrite(buffer, 1, bytes_read, stdout);
|
||||
if (bytes_written != bytes_read) {
|
||||
fprintf(stderr, "Failed to write all data to stdout\n");
|
||||
fclose(client_socket);
|
||||
return false;
|
||||
}
|
||||
total_bytes += bytes_read;
|
||||
}
|
||||
|
||||
// TODO: Grab end time
|
||||
clock_gettime(CLOCK_MONOTONIC, &end_time);
|
||||
double elapsed = (end_time.tv_sec - start_time.tv_sec) +
|
||||
(end_time.tv_nsec - start_time.tv_nsec) / BILLION;
|
||||
|
||||
// TODO: Output metrics
|
||||
fprintf(stderr, "Time Elapsed: %.2f s\n", elapsed);
|
||||
fprintf(stderr, "Bandwidth: %.2f MB/s\n", (total_bytes / elapsed) / MEGABYTES);
|
||||
|
||||
fclose(client_socket);
|
||||
|
||||
// Return true if status is ok and either the expected content length was 0
|
||||
// or we received at least as much content as was expected
|
||||
return is_status_ok && (content_length == 0 || total_bytes >= content_length);
|
||||
}
|
||||
|
||||
/* Main Execution */
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// TODO: Parse command line options
|
||||
if (argc != 2) {
|
||||
usage(EXIT_FAILURE);
|
||||
} else if (streq(argv[1], "-h")) {
|
||||
usage(EXIT_SUCCESS);
|
||||
} else if (argv[1][0] == '-') {
|
||||
usage(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// TODO: Parse URL
|
||||
URL url = {0};
|
||||
parse_url(argv[1], &url);
|
||||
|
||||
// TODO: Fetch URL
|
||||
if (fetch_url(&url)) {
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
/* vim: set sts=4 sw=4 ts=8 expandtab ft=c: */
|
||||
70
homework09/socket.c
Normal file
70
homework09/socket.c
Normal file
|
|
@ -0,0 +1,70 @@
|
|||
/* socket.c: TCP Socket Functions */
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
// I commented out the unneeded libraries below
|
||||
// #include <errno.h>
|
||||
// #include <stdlib.h>
|
||||
// #include <string.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <netdb.h>
|
||||
#include <netinet/in.h>
|
||||
#include <sys/socket.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/**
|
||||
* Create socket connection to specified host and port.
|
||||
* @param host Host string to connect to.
|
||||
* @param port Port string to connect to.
|
||||
* @return Socket file stream of connection if successful, otherwise NULL.
|
||||
**/
|
||||
FILE *socket_dial(const char *host, const char *port) {
|
||||
// TODO: Lookup server address information
|
||||
struct addrinfo *results;
|
||||
struct addrinfo hints = {
|
||||
.ai_family = AF_UNSPEC,
|
||||
.ai_socktype = SOCK_STREAM,
|
||||
};
|
||||
|
||||
int status = getaddrinfo(host, port, &hints, &results);
|
||||
if (status != 0) {
|
||||
fprintf(stderr, "getaddrinfo: %s\n", gai_strerror(status));
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// TODO: For each server entry, allocate socket and try to connect
|
||||
int client_fd = -1;
|
||||
for (struct addrinfo *p = results; p && client_fd < 0; p = p->ai_next) {
|
||||
// TODO: Allocate socket
|
||||
client_fd = socket(p->ai_family, p->ai_socktype, p->ai_protocol);
|
||||
if (client_fd < 0) {
|
||||
continue;
|
||||
}
|
||||
|
||||
// TODO: Connect to host
|
||||
if (connect(client_fd, p->ai_addr, p->ai_addrlen) < 0) {
|
||||
close(client_fd);
|
||||
client_fd = -1;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
// TODO: Release allocate address information
|
||||
freeaddrinfo(results);
|
||||
|
||||
if (client_fd < 0) {
|
||||
return NULL;
|
||||
}
|
||||
|
||||
// TODO: Open file stream from socket file descriptor
|
||||
FILE *stream = fdopen(client_fd, "r+");
|
||||
if (!stream) {
|
||||
close(client_fd);
|
||||
return NULL;
|
||||
}
|
||||
|
||||
return stream;
|
||||
}
|
||||
|
||||
/* vim: set expandtab sts=4 sw=4 ts=8 ft=c: */
|
||||
11
homework09/socket.h
Normal file
11
homework09/socket.h
Normal file
|
|
@ -0,0 +1,11 @@
|
|||
/* socket.h */
|
||||
|
||||
#pragma once
|
||||
|
||||
#include <stdio.h>
|
||||
|
||||
/* Functions */
|
||||
|
||||
FILE * socket_dial(const char *host, const char *port);
|
||||
|
||||
/* vim: set sts=4 sw=4 ts=8 expandtab ft=c: */
|
||||
91
homework09/socket.unit.c
Normal file
91
homework09/socket.unit.c
Normal file
|
|
@ -0,0 +1,91 @@
|
|||
/* socket.unit.c: Socket unit test */
|
||||
|
||||
#include "socket.h"
|
||||
|
||||
#include <assert.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Structure */
|
||||
|
||||
typedef struct {
|
||||
char * host;
|
||||
char * port;
|
||||
} URL;
|
||||
|
||||
/* Constants */
|
||||
|
||||
URL GOOD_URLS[] = {
|
||||
{.host = "google.com" , .port = "80"},
|
||||
{.host = "weasel.h4x0r.space", .port = "9898"},
|
||||
{.host = NULL},
|
||||
};
|
||||
|
||||
URL BAD_URLS[] = {
|
||||
{.host = "localhost" , .port = "1000"},
|
||||
{.host = "fakehost" , .port = "1000"},
|
||||
{.host = NULL},
|
||||
};
|
||||
|
||||
/* Tests */
|
||||
|
||||
int test_00_socket_dial_success() {
|
||||
for (URL *url = GOOD_URLS; url->host; url++) {
|
||||
FILE *socket_stream = socket_dial(url->host, url->port);
|
||||
assert(socket_stream);
|
||||
fclose(socket_stream);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int test_01_socket_dial_failure() {
|
||||
for (URL *url = BAD_URLS; url->host; url++) {
|
||||
fprintf(stderr, "%s:%s\n", url->host, url->port);
|
||||
FILE *socket_stream = socket_dial(url->host, url->port);
|
||||
assert(!socket_stream);
|
||||
}
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
int test_02_socket_dial_mode() {
|
||||
URL *url = &GOOD_URLS[0];
|
||||
|
||||
FILE *socket_stream = socket_dial(url->host, url->port);
|
||||
assert(socket_stream);
|
||||
|
||||
fprintf(socket_stream, "GET / HTTP/1.0\r\n\r\n");
|
||||
|
||||
char buffer[BUFSIZ];
|
||||
assert(fgets(buffer, BUFSIZ, socket_stream));
|
||||
|
||||
fclose(socket_stream);
|
||||
return EXIT_SUCCESS;
|
||||
}
|
||||
|
||||
/* Main Execution */
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
if (argc != 2) {
|
||||
fprintf(stderr, "Usage: %s NUMBER\n\n", argv[0]);
|
||||
fprintf(stderr, "Where NUMBER is right of the following:\n");
|
||||
fprintf(stderr, " 0 Test socket_dial_success\n");
|
||||
fprintf(stderr, " 1 Test socket_dial_failure\n");
|
||||
fprintf(stderr, " 2 Test socket_dial_mode\n");
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
int number = atoi(argv[1]);
|
||||
int status = EXIT_FAILURE;
|
||||
|
||||
switch (number) {
|
||||
case 0: status = test_00_socket_dial_success(); break;
|
||||
case 1: status = test_01_socket_dial_failure(); break;
|
||||
case 2: status = test_02_socket_dial_mode(); break;
|
||||
default: fprintf(stderr, "Unknown NUMBER: %d\n", number); break;
|
||||
}
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* vim: set sts=4 sw=4 ts=8 expandtab ft=c: */
|
||||
183
homework09/timeit.c
Normal file
183
homework09/timeit.c
Normal file
|
|
@ -0,0 +1,183 @@
|
|||
/* timeit.c: Run command with a time limit */
|
||||
|
||||
#include <errno.h>
|
||||
#include <stdbool.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
#include <time.h>
|
||||
|
||||
#include <fcntl.h>
|
||||
#include <sys/time.h>
|
||||
#include <sys/wait.h>
|
||||
#include <unistd.h>
|
||||
|
||||
/* Macros */
|
||||
|
||||
#define streq(a, b) (strcmp(a, b) == 0)
|
||||
#define strchomp(s) (s)[strlen(s) - 1] = 0
|
||||
#define debug(M, ...) \
|
||||
if (Verbose) { \
|
||||
fprintf(stderr, "%s:%d:%s: " M, __FILE__, __LINE__, __func__, ##__VA_ARGS__); \
|
||||
}
|
||||
|
||||
#define BILLION 1000000000.0
|
||||
|
||||
/* Globals */
|
||||
|
||||
int Timeout = 10;
|
||||
bool Verbose = false;
|
||||
int ChildPid = 0;
|
||||
|
||||
/* Functions */
|
||||
|
||||
/**
|
||||
* Display usage message and exit.
|
||||
* @param status Exit status.
|
||||
**/
|
||||
void usage(int status) {
|
||||
fprintf(stderr, "Usage: timeit [options] command...\n");
|
||||
fprintf(stderr, "Options:\n");
|
||||
fprintf(stderr, " -t SECONDS Timeout duration before killing command (default is %d)\n", Timeout);
|
||||
fprintf(stderr, " -v Display verbose debugging output\n");
|
||||
exit(status);
|
||||
}
|
||||
|
||||
/**
|
||||
* Parse command line options.
|
||||
* @param argc Number of command line arguments.
|
||||
* @param argv Array of command line argument strings.
|
||||
* @return Array of strings representing command to execute (must be freed).
|
||||
**/
|
||||
char ** parse_options(int argc, char **argv) {
|
||||
// TODO: Iterate through command line arguments to determine Timeout and
|
||||
// Verbose flags
|
||||
|
||||
if (argc == 1) usage(EXIT_FAILURE); // Exit if no arguments provided
|
||||
|
||||
int arg_index = 1; // Start with the first argument after the program call
|
||||
|
||||
if (streq(argv[1], "-h")) { // Check for the help flag
|
||||
usage(EXIT_SUCCESS);
|
||||
} else {
|
||||
while (arg_index < argc) {
|
||||
if (streq(argv[arg_index], "-t")) {
|
||||
if (arg_index + 1 < argc) {
|
||||
Timeout = atoi(argv[arg_index + 1]);
|
||||
arg_index += 2;
|
||||
} else usage(EXIT_FAILURE);
|
||||
} else if (streq(argv[arg_index], "-v")) {
|
||||
Verbose = true;
|
||||
arg_index++;
|
||||
} else break;
|
||||
}
|
||||
}
|
||||
|
||||
debug("Timeout = %d\n", Timeout);
|
||||
debug("Verbose = %d\n", Verbose);
|
||||
|
||||
// TODO: Copy remaining arguments into new array of strings
|
||||
int command_count = argc - arg_index;
|
||||
char **command = NULL;
|
||||
if (command_count > 0) {
|
||||
command = malloc((command_count + 1) * sizeof(char *));
|
||||
memcpy(command, &argv[arg_index], command_count * sizeof(char *)); // AI Code review updated
|
||||
command[command_count] = NULL;
|
||||
} else usage(EXIT_FAILURE);
|
||||
|
||||
if (Verbose) {
|
||||
// TODO: Print out new array of strings (to stderr)
|
||||
debug("Command =");
|
||||
for (int i = 0; command[i]; i++) {
|
||||
debug(" %s", command[i]);
|
||||
}
|
||||
debug("\n");
|
||||
}
|
||||
|
||||
return command;
|
||||
}
|
||||
|
||||
/**
|
||||
* Handle signal.
|
||||
* @param signum Signal number.
|
||||
**/
|
||||
void handle_signal(int signum) {
|
||||
// TODO: Kill child process gracefully, then forcefully
|
||||
debug("Killing child %d...\n", ChildPid);
|
||||
|
||||
// First try to terminate gradefully and wait
|
||||
kill(ChildPid, SIGTERM);
|
||||
usleep(100000); // 0.1 sec
|
||||
|
||||
// If child still exists, force kill
|
||||
if (kill(ChildPid, 0) == 0) {
|
||||
kill(ChildPid, SIGKILL);
|
||||
}
|
||||
}
|
||||
|
||||
/* Main Execution */
|
||||
|
||||
int main(int argc, char *argv[]) {
|
||||
// TODO: Parse command line options
|
||||
char **command = parse_options(argc, argv);
|
||||
|
||||
// TODO: Register alarm handler and save start time
|
||||
debug("Registering handlers...\n");
|
||||
signal(SIGALRM, handle_signal);
|
||||
|
||||
debug("Grabbing start time...\n");
|
||||
struct timespec start_time, end_time;
|
||||
clock_gettime(CLOCK_MONOTONIC, &start_time);
|
||||
|
||||
// TODO: Fork child process:
|
||||
pid_t pid = fork();
|
||||
if (pid < 0) {
|
||||
perror("fork");
|
||||
free(command);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// 1. Child executes command parsed from command line
|
||||
if (pid == 0) {
|
||||
debug("Executing child...\n");
|
||||
execvp(command[0], command);
|
||||
perror("execvp");
|
||||
exit(EXIT_FAILURE);
|
||||
}
|
||||
|
||||
// 2. Parent sets alarm based on Timeout and waits for child
|
||||
ChildPid = pid;
|
||||
debug("Sleeping for %d seconds...\n", Timeout);
|
||||
alarm(Timeout);
|
||||
|
||||
int status;
|
||||
debug("Waiting for child %d...\n", ChildPid);
|
||||
if (waitpid(ChildPid, &status, 0) < 0) {
|
||||
perror("waitpid");
|
||||
free(command);
|
||||
return EXIT_FAILURE;
|
||||
}
|
||||
|
||||
// TODO: Print out child's exit status or termination signal
|
||||
if (WIFEXITED(status)) {
|
||||
debug("Child exit status: %d\n", WEXITSTATUS(status));
|
||||
} else if (WIFSIGNALED(status)) {
|
||||
debug("Child killed by signal: %d\n", WTERMSIG(status));
|
||||
}
|
||||
|
||||
// TODO: Print elapsed time
|
||||
debug("Grabbing end time...\n");
|
||||
clock_gettime(CLOCK_MONOTONIC, &end_time);
|
||||
double elapsed = (end_time.tv_sec - start_time.tv_sec) +
|
||||
(end_time.tv_nsec - start_time.tv_nsec) / BILLION;
|
||||
printf("Time Elapsed: %0.1lf\n", elapsed);
|
||||
|
||||
// TODO: Cleanup
|
||||
free(command);
|
||||
|
||||
status = WIFEXITED(status) ? WEXITSTATUS(status) : WTERMSIG(status);
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
/* vim: set sts=4 sw=4 ts=8 expandtab ft=c: */
|
||||
Loading…
Reference in a new issue