getting output from excl used to run a shell script - c

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?

Related

Problems with shell implementation in 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.

Variable suddenly changing inside fork() child process C - very weird behaviour

I'm hoping someone here could help me cause I'm witnessing some weird behavior from my C code.
I have a Linux shell-like program in C. The code is 250 lines long, so I can't be posting it here.
In short, I have this function for non-built in commands:
int not_builtin(char** args, int background, char* line) {
int status;
pid_t pid;
pid = fork();
if (pid < 0) {
printf("fork failed\n");
}
else if (pid == 0) {
if (execvp(args[0], args) == -1) {
printf("exec failed\n");
}
return 0;
}
else {
if (!background) {
if (waitpid(pid, &status, 0) < 0) {
printf("An error occurred\n");
}
}
add_job(line, pid, background);
}
return 1;
}
When I run my shell on my PC (Macbook), everything works as expected. BUT, when I run this on my university server (Linux), I get a bug. I cannot properly debug this since on my computer everything works fine. The bug is it outputs: "exec failed" for any command (line 9).
By printing through the code, I was able to detect the following behavior:
The char** args are good throughout the code, I printed them until just before the "if (pid == 0)".
Once you enter the if statement mentioned above, printing the args returns some garbage.
Specifically, for an input: $ ls -l
I got printed ls -l before the if, and ?\?? after the if.
Like this for any other command string.
What could be the reason?
The whole day the program worked fine. It started to happen once I changed my "parse-line" function, to copy the line before editing. It now looks like this:
char** parse_line(char *line, int *background) {
char line_copy[BUFF_SIZE];
strcpy(line_copy, line);
char **tokens = malloc(BUFF_SIZE*sizeof(char*));
if (!tokens) {
printf("An error occurred\n");
exit(EXIT_FAILURE);
}
int position = 0;
char *token;
token = strtok(line_copy, DELIM);
while (token) {
tokens[position] = token;
position++;
token = strtok(NULL, DELIM);
}
// change the background option
if (position > 0 && strcmp(tokens[position-1], "&")==0) {
*background = 1;
tokens[position-1]=NULL;
line[strlen(line)-2] = '\0';
} else {
*background = 0;
tokens[position] = NULL;
}
return tokens;
}
Before the change I would perform the strtok directly on line, but now I figured I need that value intact. But how is this connect in any way?
Any help would be MUCH appreciated

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;
}

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.

segmentation fault unix shell

I have an assignment for school where I have to create a shell which can do the following:
read incoming command, parse each part of command
fork child process and execute each command without (< > >> |)
successfully execute each command with <, >, >>
successfully execute each command with |
I am seriously lost... I am new to shell and I have no clue on what to do from here.
My code gives me an error stating segmentation fault (core dumped). Any and all help will be greatly appreciated.
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#define MAX_ARG 10
int main()
{
char line [256];
char prompt[] = "sh2 % ";
char command[256], *args[MAX_ARG];
int pid;
char status;
int i;
/* spit out the prompt */
printf("%s", prompt );
while( fgets(line, sizeof line, stdin) != NULL)
{
/* fgets leaves '\n' in input buffer. ditch it */
line [strlen(line)-1] = '\0';
while (line != NULL)
{
//parse command and arg
args[0] = strtok(line, " "); //grab command
for (i=1; i<MAX_ARG; i++) //grab arguments, to assume max = 10?
{
//if a single command with arguments then set command & argument
//for (i>0)
{
// check to see if the command is 'exit'
if(!strcmp("exit", args[i]))
{
exit(0);
}
{
int p[2];
pipe(p)
if (fork() == 0) //child
{
close (0);
dup(p[0]);
exec("cmd2");
}
else
{
close(1);
close(p[0]);
close(p[1]);
dup(p[1]);
exec("cmd1");
}
close(0);
open("stdout.txt", "r");
if (fork()== 0)
{
exec("cmd3");
}
}
else if (!strcmp(">", args[i]))
open("stderr.txt". "w")
if (fork() == 0)
{
exec("cmd1");
}
}
else if (!strcmp(">>", args[i]))
{
close(1);
open("stdout_stderr.txt", "w");
if (fork() == 0)
{
close(2);
dup(1);
exec("cmd2");
}
}
else
{
pid = fork();
if (pid == 0)
{
status = execvp(command,args);
exit(0);
}
else
{
waitpid(-1);
}
}
}
}
}
}
return 0;
}
You have quite a few things wrong in your program, so it's hard to point to one line and say change this. I think you are trying to do too much at once and not building on a solid base.
In programming you want to start small and build on your progress, your professor did you a favor by lining up the steps:
read incoming command, parse each part of command
fork child process
and execute each command without (< > >> |)
successfully execute
each command with <, >, >>
successfully execute each command with |
Try to get #1 working befor moving on to #2 and as mentioned before using functions is going to help a lot. I would suggest looking at this post http://bytes.com/topic/c/answers/215994-writing-shell-c which will give you a simple shell you could model from. Here is a baseline parser to get you started on #1 (based on the post mentioned)
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
char line[4096] = {0};
char safeline[4096] = {0};
int done = 0;
int i = 0;
char *s = line;
char **t = NULL;
char *prompt = ">";
char *args = NULL;
char *nl = NULL;
int main(void)
{
while(!done)
{
printf("%s", prompt);
fflush(stdout);
if(NULL == fgets(line, sizeof line, stdin))
{
t = NULL;
done = 1;
}
else
{
nl = strchr(line, '\n');
if(nl != NULL)
{
*nl = '\0';
strcpy(safeline, line);
}
else
{
int ch;
printf("Line too long! Ignored.\n");
while((ch = getchar()) != '\n' && ch != EOF)
{
continue;
}
if(ch == EOF)
{
done = 1;
}
}
args = strchr(line, ' ');
if(args != NULL)
{
*args++ = '\0';
}
if(!done)
{
printf("command - %s : args - %s\n",s, args);
}
}
}
}

Resources