Problems with shell implementation in c - c

Been working on a simple shell implementation for a for days. But I have 2 issues:
1.When you run the program and type a command like "/bin/ls -l /usr/include" and then press enter it will execute the command but if you then enter a few new lines the program will still go into execvp() and output "Permission denied", because it goes into the error handling of execvp and that's not supposed to happen. (I tried a few different things to stop it from going into the if() in main but they didn't work.)
2.If you type enter and then enter a command the command won't be executed. (I tried freeing the buff and allocating memory again but that caused more issues.)
So basically it goes in the "if(buff[offset] == '\n')" even if the line is empty when it shouldn't and it doesn't execute a command unless its the first input after starting the program.
Code:
int fd = 0;
const size_t read_size = 1;
size_t size = read_size;
char *buff = malloc(size+1);
size_t offset = 0;
size_t res = 0;
write(STDOUT_FILENO, "$ ", strlen("$ "));
while((res = read(fd, buff + offset, read_size)) > 0)
{
if(buff[offset] == '\n')
{
buff[offset] = '\0';
char **result = parse_cmdline(buff); // turn string into array for execvp()
int exec;
int status;
pid_t pid = fork();
if(pid == -1) {/*error handling*/}
else if(pid == 0)
{
if((exec = execvp(result[0], result)) == -1) {/*error handling*/}
}
else
{
waitpid(pid, &status, 0);
}
offset = 0;
free(result[0]);
free(result);
result = NULL;
write(STDOUT_FILENO, "$ ", strlen("$ "));
}
offset += res;
if (offset + read_size > size)
{
size *= 2;
buff = realloc(buff, size+1);
}
buff[offset] = '\0';
}
free(buff);
return 0;
UPDATE: Works with commands inputted after the first line, but with an error in every case:
"a.out: malloc.c:2379: sysmalloc: Assertion `(old_top == initial_top (av) && old_size == 0) || ((unsigned long) (old_size) >= MINSIZE && prev_inuse (old_top) && ((unsigned long) old_end & (pagesize - 1)) == 0)' failed.
Aborted (core dumped)"
Updated code:
if(buff[offset] == '\n')
{
buff[offset] = '\0';
char **result = parse_cmdline(buff);
if(result[0] != NULL)
{
int exec;
int status;
pid_t pid = fork(); // create the child process
if(pid == -1) // check for fork() errors (no memory, etc.)
{
fork_error();
}
else if(pid == 0) // Handle child process
{
if((exec = execvp(result[0], result)) == -1) // run the command itself and check if an error will occur (invalid command, etc.)
{
file_error(result[0]);
}
}
else // Handle parent process
{
waitpid(pid, &status, 0);
}
offset = 0;
free(buff);
size = read_size;
free(result[0]);
free(result);
result = NULL;
}
write(STDOUT_FILENO, "$ ", strlen("$ "));
}
else
{
offset += res;
if (offset + read_size > size)
{
size *= 2;
buff = realloc(buff, size+1);
}
buff[offset] = '\0';
}

Your parse_cmdline() function should test for empty lines, lines with just whote space and comment lines starting with # and return NULL is these cases.
The calling code should then test result for such lines to be ignored.
The calling code should also compare result[0] with the names of internal commands, such as cd or chdir and handle them locally instead of calling fork and exec.

Related

Why does strtok occasionally cause a bus error?

EDIT: I have made the info here more specific and executed some recommendations from the comments.
I have a shell written in C that works like a charm when used. However, I have some tests written for a function called pipe_exec that causes a bus error. I thought it was originally from strtok in my split function (and it may still be).
The pipe_exec func basically deals with commands with pipes like ls -a | wc -l or something. It always works fine when I'm using the actual shell but with the tests, there's always a bus error if there are any flags involved with the piped commands.
The issue could jut be with my test.
But I have no clue what the issue is. It's tracing back to the strtok in my split function, but it only has a bus issue with the tests and never in any actual equivalent situations.
Any help here is appreciated. Sorry for so much code to look at.
shell_exec_tests.c
static char *args1[20] = {"ls ", " wc"}; // works
static char *args2[20] = {"ls -a", "wc -l"}; // causes bus error
static int a = 0;
static int b = 0;
void test_setup(void)
{
a = pipe_exec(args1);
b = pipe_exec(args2);
}
void test_teardown(void)
{
// nothing
}
MU_TEST(test_check)
{
mu_check(a == EXIT_SUCCESS);
mu_check(b == EXIT_SUCCESS);
}
MU_TEST_SUITE(test_suite)
{
MU_SUITE_CONFIGURE(&test_setup, &test_teardown);
MU_RUN_TEST(test_check);
}
int main()
{
MU_RUN_SUITE(test_suite);
MU_REPORT();
return MU_EXIT_CODE;
}
pipe_exec.c
// make_proc: determine if a process goes to stdout or takes in data from stdin
void make_proc(int in, int out, char **cmd)
{
pid_t rc;
int status;
rc = fork();
if (rc < 0) {
perror("fork");
exit(1);
}
if (rc == 0) {
if (in != STDIN_FILENO) {
dup2(in, STDIN_FILENO);
close(in);
}
if (out != STDOUT_FILENO) {
dup2(out, STDOUT_FILENO);
close(out);
}
execvp(*cmd, cmd);
errmsg(*cmd);
exit(1);
}
waitpid(rc, &status, WUNTRACED);
return;
}
// pipe_exec: loop through each command, connecting each through a pipe
int pipe_exec(char **args)
{
int in, status, return_val;
int pipe_no; // keep track of no. of cmds seperated by pipes
int pfd[2];
pid_t rc;
char **cmd;
return_val = EXIT_SUCCESS;
in = 0;
pipe_no = 0;
while (*args) {
cmd = split(*args, " \t\r\n");
if (!args[1]) {
break;
}
if (pipe(pfd) < 0) {
perror("pipe");
}
make_proc(in, pfd[1], cmd);
close(pfd[1]);
in = pfd[0];
args++;
pipe_no++;
}
// move pointer back
args -= pipe_no;
rc = fork();
if (rc < 0) {
perror("fork");
exit(1);
}
if (rc == 0) {
if (in != 0) dup2(in, STDIN_FILENO);
execvp(*cmd, cmd);
errmsg(*cmd);
return_val = EXIT_FAILURE;
exit(1);
}
waitpid(rc, &status, WUNTRACED);
// pretty sure i need a pipe to get the EXIT_FAILURE from
// the child if the child fails, but for now im just working
// on finding that bus error issue
return return_val;
}
And lastly, my split function:
// trim: trim leading and trailing whitespace on a string
static char *trim(char *str)
{
char *end;
// Trim leading space
while(isspace((unsigned char)*str)) str++;
if(*str == 0) // All spaces?
return str;
// Trim trailing space
end = str + strlen(str) - 1;
while(end > str && isspace((unsigned char)*end)) end--;
// Write new null terminator character
end[1] = '\0';
return str;
}
// split: take a string and break it up into an array of strings based on delim
char **split(char *s, const char *delim)
{
char **split_s;
char *token;
size_t len;
int i;
len = strlen(s);
split_s = calloc(len*2, sizeof(char*));
if (split_s == NULL) {
fprintf(stderr, "split: could not allocate memory\n");
exit(EXIT_FAILURE);
}
i = 0;
token = strtok(s, delim);
while (token != NULL) {
split_s[i] = trim(token);
token = strtok(NULL, delim);
i++;
}
split_s[i] = NULL;
return split_s;
}

getting output from excl used to run a shell script

Hi i would like to get suggestions as to getting output from a script which i am supposed to execute from an excl call:
void getCurrentFSLSMode(char* const in_fsls_directory, enum Mode* out_mode) {
int link[2];
pid_t pid;
char buffer[MAX_PATH_STR_LENGTH];
char command_buffer[MAX_COMMAND_STR_LENGTH];
char script_name[] = "/scpt.sh\0";
if (pipe(link)==-1)
printf("pipe");
if ((pid = fork()) == -1)
printf("fork");
if(pid == 0) {
dup2 (link[1], STDOUT_FILENO);
close(link[0]);
close(link[1]);
command_buffer[0] = '\0';
strcat(command_buffer, in_fsls_directory);
strcat(command_buffer, script_name);
printf("%s\n", command_buffer);
execl("/bin/sh", "sh", command_buffer, (char *)0);
} else {
close(link[1]);
int nbytes = read(link[0], buffer, sizeof(buffer));
buffer[nbytes - 1] = '\0';
// LAZY COMPARISON
// not algorithm name, it's just what im doing.
//
printf("DBG : %s %d\n", buffer, nbytes);
if (buffer[0] == 'G') {
*out_mode = K;
} else if (buffer[0] == 'K') {
*out_mode = G;
} else {
*out_mode = CLEARED;
}
wait(NULL);
}
}
if i use the same code on running a binary, i am able to read out its output,
however, when i try to execute a script such as the one above, i am only able to read the command issued.
example:
excl("binary command to run");
read(output)
output == "correct output"
but if
excl("shell script to run");
read(output)
output == "shell script to run"
why is this?

Receiving multiple TCP segments

I have an assignment in which a TCP client sends data to the TCP server in the form of:
IP_address\0port\0message\n
Now, the server (IP address 10.0.2.15) receives the packet fine when I send some data through a terminal like this:
printf "127.0.0.1\0004444\000Some message\n" | nc -N 10.0.2.15 3333
However, the second part of the assignment is to read a packet that comes in multiple segments:
(printf "127.0.0.1"; sleep 0.3; printf "\0004444\000"; sleep 0.3; \
printf "It works"; sleep 0.5; printf "\n") | nc -N 10.0.2.15 3333
How should I implement the read function on the server so that, if possible, all the segments are stored into a buffer?
The number of bytes recv() returns can be as few as 1 byte up to as many bytes as requested. TCP is a byte stream, it has no concept of messages, that has to be handled in the application code instead.
The receiver must know how many bytes to expect, and then keep reading in a loop until it has read that many bytes, however many reads it takes.
However, in this situation, the receiver does not know the exact length of the message, because the sender is not sending the message length before sending the message itself, so the only option available is for the receiver to read from the socket byte-by-byte until it encounters the terminating \n.
For example:
int readLine(int socket, char **line)
{
int r, len = 0, cap = 256;
char b;
*line = NULL;
char *outline = (char*) malloc(cap);
if (!outline) return -2;
do
{
r = recv(socket, &b, 1, 0);
if (r <= 0)
{
free(outline);
return r;
}
if (b == '\n')
break;
if (len == cap)
{
cap += 256;
char *newline = (char*) realloc(outline, cap);
if (!newline)
{
free(outline);
return -2;
}
outline = newline;
}
outline[len] = b;
++len;
}
while (true);
if ((len > 0) && (line[len-1] == '\r'))
--len;
if (len == cap)
{
char *newline = (char*) realloc(outline, cap + 1);
if (!newline)
{
free(outline);
return -2;
}
outline = newline;
}
outline[len] = '\0';
*line = outline;
return 1;
}
char *line;
int r;
do
{
r = readLine(cliSock, &line);
if (r <= 0)
{
if (r == 0)
printf("client disconnected\n");
else if (r == -2)
printf("memory error\n");
else
printf("read error\n");
break;
}
// process line as needed...
free(line);
}
while (true);
Alternatively, you can use an intermediate buffer to help you cache data between reads and get data out of the socket more efficiently:
char *buffer;
int buflen, bufcap;
int readLine(int socket, char **line)
{
char *ptr;
int r, idx = 0;
*line = NULL;
do
{
ptr = memchr(buffer + idx, '\n', buflen - idx);
if (ptr)
{
int total = ((ptr + 1) - buffer);
int len = (total - 1);
if ((len > 0) && (buffer[len-1] == '\r'))
--len;
*line = (char*) malloc(len + 1);
if (*line == NULL)
return -2;
memcpy(*line, buffer, len);
(*line)[len] = '\0';
if (total < buflen)
memmove(buffer, buffer + total, buflen - total);
buflen -= total;
break;
}
if (buflen == bufcap)
{
int newcap = bufcap + 256;
char *newbuffer = (char*) realloc(buffer, newcap);
if (!newbuffer)
return -2;
buffer = newbuffer;
bufcap = newcap;
}
r = recv(socket, buffer + buflen, bufcap - buflen, 0);
if (r <= 0)
return r;
buflen += r;
}
while (true);
return 1;
}
buflen = 0;
bufcap = 256;
buffer = (char*) malloc(bufcap);
if (buffer)
{
char *line;
int r;
do
{
r = readLine(cliSock, &line);
if (r <= 0)
{
if (r == 0)
printf("client disconnected\n");
else if (r == -2)
printf("memory error\n");
else
printf("read error\n");
break;
}
// process line as needed...
free(line);
}
while (true);
free(buffer);
}

Signal Handler running multiples times

Currently writing a basic shell that handles signal interrupt with variety of other functions.
int numberOfCommands = 0;
int signalHandlerFired = 0;
char history[HISTORY_DEPTH][COMMAND_LENGTH];
void handle_SIGINT(){
writeHistory();
signalHandlerFired = 1;
}
int main(int argc, char* argv[])
{
char input_buffer[COMMAND_LENGTH];
char *tokens[NUM_TOKENS];
char cwd[COMMAND_LENGTH];
struct sigaction handler;
handler.sa_handler = handle_SIGINT;
sigaction(SIGINT, &handler, NULL);
while (true) {
signalHandlerFired = 0;
getcwd(cwd, sizeof(cwd));
write(STDOUT_FILENO, cwd, strlen(cwd));
write(STDOUT_FILENO, "> ", strlen("> "));
_Bool in_background = false;
read_command(input_buffer, tokens, &in_background);
if (signalHandlerFired == 1) {
continue;
}
char exclamationCommand[2];
exclamationCommand[0] = tokens[0][0];
exclamationCommand[1] = '\0';
char* hcNumber = &tokens[0][1];
if (strcmp(&exclamationCommand[0], "!") == 0) {
if (atoi(hcNumber) == 0) {
} else {
retrieveHistory(hcNumber, numberOfCommands, input_buffer, tokens, in_background);
}
continue;
}
insertHistory(&numberOfCommands, input_buffer, tokens);
//exit
if (strcmp(tokens[0], "exit") == 0) {
exit(0);
}
//pwd
if (strcmp(tokens[0], "pwd") == 0) {
getcwd(cwd, sizeof(cwd));
write(STDOUT_FILENO, cwd, strlen(cwd));
write(STDOUT_FILENO, "\n", strlen("\n"));
continue;
}
//cd
if (strcmp(tokens[0], "cd") == 0) {
int dcSuccess = chdir(tokens[1]);
if (dcSuccess == -1) {
write(STDOUT_FILENO, "Invalid Directory", strlen("Invalid Directory"));
write(STDOUT_FILENO, "\n", strlen("\n"));
}
continue;
}
//history
if (strcmp(tokens[0], "history") == 0) {
writeHistory(tokens[0]);
}
executeCommand(numberOfCommands, input_buffer, tokens, in_background);
}
return 0;
}
In the while loop in main() which gets inputs from the command line and does processing depending on the type of command entered, the signal handler will run twice or more depending on...is what I don't know.
The purpose of SIGINT [Ctrl+C] interrupt is to display the command history. This works fine if i run it once, but after..
/home/ahn/> ls
Token: ls
a.out Makefile.txt~ shell shell.c~ shellsample.c~
Makefile mystring_sol.c shell.c shell.o
/home/ahn/> ^C
History:
1 ls
---------------Second Run
/home/ahn/> ks
Token: ks
Unknown Command
/home/ahn/> ^C
History:
Number Of Commands: 2
1 ls
2 ks
/home/ahn/>
History:
Number Of Commands: 2
1 ls
2 ks
/home/ahn/>
I think I don't have a clear understanding when interrupts happen, at which point in the program is returning too. Moreover, I have no idea why it is running twice. I will provide any function definitions if needed.
Also, the char ks comes out corrupted. Full core here if needed.

Multi Piping bash-style in C

I know there are many threads that talk about this problem but I don't really understand the way it can be done.
I'm trying to make a shell that can execute a linux command sucha as ps | grep | less
I've donne the parsing by puting every command and its args in a simply linked list.
Here's my implementation that doesn't work. Hope that's clear enough.
if ((son = fork()) < 0)
return printerr_sys("Unable to fork", 0);
if (son == 0)
{
if (first > 1 && data->format[first - 1] &&
is_directing_elt(data->format[first - 1]) == DIRECT_TPIPE)
dup2(tube_p[0], STDIN_FILENO);
first = make_argv(data, first, &argv);
if (next)
{
dup2(tube_v[1], STDOUT_FILENO);
close(tube_v[0]);
}
if (execvp(argv[0], argv) < 0)
return printerr_cmd(argv[0], 1);
}
else
{
if (next)
{
close(tube_v[1]);
cmdline_executer(data, next, tube_v);
}
waitpid(son, &(data->lastcmd), WUNTRACED);
data->lastcmd = WEXITSTATUS(data->lastcmd);
}
return TRUE;
My questions are:
What would be the correct implementation?
Is it possible to do it with recursion?
Do I need to fork from right to left or left to right (logically it give the same result)?
Here's a part of a UNIX Shell I had to implement in C for Operating System subject in my Computer Science career.
/* Executes the command 'buffer' assuming that doesn't contain redirections */
void execute_only_pipes(char* buffer)
{
char *temp = NULL, *pipeCommands[MAX_PIPES], *cmdArgs[MAX_ARGUMENTS];
int newPipe[2], oldPipe[2], pipesCount, aCount, i, status;
pid_t pid;
pipesCount = -1; /* This variable will contain how many pipes the command contains */
/* Counting the number of pipes and splitting them into pipeCommands */
do
{
temp = strsep(&buffer, "|");
if(temp != NULL)
{
if(strlen(temp) > 0)
{
pipeCommands[++pipesCount] = temp;
}
}
} while(temp);
cmdArgs[++pipesCount] = NULL;
for(i = 0; i < pipesCount; i++) /* For each command */
{
aCount = -1;
/* Parsing command & arguments */
do
{
temp = strsep(&pipeCommands[i], " ");
if(temp != NULL)
{
if(strlen(temp) > 0)
{
/* If a parameter is ~, then replace it by /home/user */
if (!strcmp(temp, "~"))
strcpy(temp, home);
cmdArgs[++aCount] = temp;
}
}
} while(temp);
cmdArgs[++aCount] = NULL;
/* If there still are commands to be executed */
if(i < pipesCount-1)
{
pipe(newPipe); /* just create a pipe */
}
pid = fork();
if(pid == 0) /* Child */
{
/* If there is a previous command */
if(i > 0)
{
close(oldPipe[1]);
dup2(oldPipe[0], 0);
close(oldPipe[0]);
}
/* If there still are commands to be executed */
if(i < pipesCount-1)
{
close(newPipe[0]);
dup2(newPipe[1], 1);
close(newPipe[1]);
}
/* Execute it */
int res = execvp(cmdArgs[0], cmdArgs);
if (res == -1)
{
printf("Error. Command not found: %s\n", cmdArgs[0]);
}
exit(1);
}
else /* Father */
{
/* If there is a previous command */
if(i > 0)
{
close(oldPipe[0]);
close(oldPipe[1]);
}
/* do we have a next command? */
if(i < pipesCount-1)
{
oldPipe[0] = newPipe[0];
oldPipe[1] = newPipe[1];
}
/* wait for last command process? */
if(i == pipesCount-1)
{
waitpid(pid, &status, 0);
}
}
}
}
It might be a little buggy (I'm not checking if fork() < 0, etc) but the main idea is correct.
> Is it possible to do it with recursion?
Most of the time I try to avoid recursion, if I can write a similar understandable code without using it.
Processes run independently, so you need to set up the pipe for at least the first pair of commands before you fork, but you're doing that in the child (son == 0). You could code a recursive solution that, as long as there are at least two commands left, creates a pipe, then forks, then runs the first command.

Resources