Question: Program in C, just edit the file pipe.c Complete the function run program so that the forked child transmits the error number errno to the
Program in C, just edit the file pipe.c
Complete the function run program so that the forked child transmits the error number errno to the parent (the program starter), if the call to exec fails. Your function should fulfill the following requirements: Returns -1 on any error; the exit status of the child process otherwise. Allows the caller to read the correct error number from errno, as explained in the following items: Uses the pipe2 system call to create a new pipe and sets the necessary flags to avoid leakage of the pipes file descriptors into the execed program. (Note: unlike the basic pipe system call, pipe2 has an additonal argument with flags that can be used among other things to indicate that the file descripter should automatically be closed upon an exec system call.) Uses the write/read system calls to write to and read from the pipe, transmitting on error the error number errno of exec from the child to the parent. The parent shall then set its errno to the received value. Closes unnecessary pipe endings in the parent and child respectively as soon as possible and fully closes the pipe before returning to the caller. Do not leak file descriptors! Hints: Assume that the write and read system calls do not fail and ignore errors from the close system call. The template only marks the most important locations that need to be extended, you need to add further code to fulfill all requirements.
MacOS hint: The pipe2 system call is not available on MacOS. Instead, you could use the pipe system call combined with fcntl system calls for each of the file descriptors to set the close on exec flag (this works on MacOS and Linux). We accept both version, but make sure the code you submit compiles and runs on Linux. int run_program(char *argv[]);
The file pipe.c is:
// This changes the way some includes behave. // This should stay before any include. #define _GNU_SOURCE
#include "pipe.h" #include
int run_program(char *argv[]) {
if (argv == NULL) { return -1; }
// ------------------------- // TODO: Open a pipe // -------------------------
int child_pid = fork(); if (child_pid == -1) { return -1; } else if (child_pid == 0) {
// Replace program execvp(argv[0], argv);
// ------------------------- // TODO: Write the error on the pipe // -------------------------
exit(0); } else { int status, hadError = 0;
int waitError = waitpid(child_pid, &status, 0); if (waitError == -1) { // Error while waiting for child. hadError = 1; } else if (!WIFEXITED(status)) { // Our child exited with another problem (e.g., a segmentation fault) // We use the error code ECANCELED to signal this. hadError = 1; errno = ECANCELED; } else { // ------------------------- // TODO: If there was an execvp error in the child, set errno // to the value execvp set it to. // ------------------------- }
return hadError ? -1 : WEXITSTATUS(status); } }
The main file is just to test this file and it is
#include "testlib.h" #include "pipe.h" #include
int main() { test_start("pipe.c");
printf("Running ls "); errno = 0; char *argv1[] = { "/bin/ls", "/home", NULL }; int result1 = run_program(argv1); test_equals_int(result1, 0); test_equals_int(errno, 0);
printf("Running unknown-program-name "); errno = 0; char * argv2[] = { "unknown-program-name", NULL }; int result2 = run_program(argv2); test_equals_int(result2, -1); test_equals_int(errno, ENOENT); printf("Error: %s (errno=%d) ", strerror(errno), errno);
return test_end(); }
pipe.h file is:
#ifndef PIPE_H #define PIPE_H
int run_program(char *argv[]);
#endif
The Makefile is:
CC= gcc LD= ld CFLAGS= -g -W -Wall -Werror -std=c99 TARGET= pipe SRC= pipe.c testlib.c main.c OBJ= $(SRC:.c=.o)
pipe: $(OBJ) $(CC) -o $@ $(OBJ)
.PHONY: $(TARGET)-solution.o $(TARGET)-sol: $(subst $(TARGET),$(TARGET)-solution,$(OBJ)) $(CC) -o $@ $^
$(TARGET)-solution.o: @test -f $(TARGET)-solution.c \ || { echo "Please download solution." >&2; false; } $(CC) $(CFLAGS) -c $(TARGET)-solution.c
%.o: %.c $(CC) $(CFLAGS) -c $<
clean: rm -rf $(TARGET) $(TARGET)-sol *.o
The testlib.c fiile is:
#include
#define test_failed_expected(type, should, is, file, line) { \ printf("%s line %d: Expected " type ", but got " type " ", file, line, should, is); \ had_error = 1; }
int had_error = 0;
void _test_failed_message(char *message, char *file, int line) { printf("%s line %d: %s ", file, line, message); }
void test_start(char *filename) { printf("Starting to test %s... ", filename); }
void _test_equals_int64(int64_t is, int64_t should, char* file, int line) { if (is != should) { test_failed_expected("%"PRIu64, should, is, file, line); } }
void _test_equals_int(int is, int should, char* file, int line) { if (is != should) { test_failed_expected("%d", should, is, file, line); } }
void _test_failed() { had_error = 1; }
void _test_equals_string(char* is, char* should, char* file, int line) { if (strcmp(is, should) != 0) { test_failed_expected("\"%s\"", should, is, file, line); } }
int test_end() { if (had_error) { printf("You have errors in your solution, please fix them. "); } else { printf("All test cases passed. This does not mean your solution is correct. "); } return had_error; }
And testlib.h file is:
#ifndef TESTLIB_H #define TESTLIB_H
#include
#define test_equals_int(is, should) _test_equals_int(is, should, __FILE__, __LINE__); void _test_equals_int(int is, int should, char* file, int line);
#define test_equals_int64(is, should) _test_equals_int64(is, should, __FILE__, __LINE__); void _test_equals_int64(int64_t is, int64_t should, char* file, int line);
#define test_equals_string(is, should) _test_equals_string(is, should, __FILE__, __LINE__); void _test_equals_string(char* is, char* should, char* file, int line);
#define test_failed_message(message) _test_failed_message(message, __FILE__, __LINE__); void _test_failed_message(char *message, char *file, int line);
int test_end();
#endif
Step by Step Solution
There are 3 Steps involved in it
Get step-by-step solutions from verified subject matter experts
