Segmentation fault when trying to run custom shell - c

School project to make our own shell and facing segmentation fault issues.
Can someone help?
Edit: kind of running but "quit" doesn't trigger the exit and execvp can't run properly (says : "no such file or directory" & "or ls:invalid option -- ' "
void interactive_mode();
void batch_mode(char *path);
void parse(char *str,char *delimiter, char **args);
int execute(char **args);
int main(int argc, char *argv[]) {
if(strcmp(argv[1], "-b") == 0) {
batch_mode(argv[2]);
}
else if(strcmp(argv[1], "-i") == 0) {
interactive_mode();
}
}
I suspect the segmentation fault problem derives from the interactive and batch modes code
void interactive_mode() {
int quit_flag;
char str[512];
char *commands[128];
char *args[128];
quit_flag = 0;
while(1) {
printf("whatever> ");
if (fgets(str,512,stdin) == NULL) {
exit(0); //error reading
}
int i = 0;
parse(str,";",commands); //split the string into commands eg "ls -a ; ls -l" -> "ls -a ","ls -l"
while (commands[i]!=NULL) {
parse(commands[i]," ",args); //split commands into arguments eg "ls -l" -> "ls","-l"
i++;
quit_flag = execute(args);
}
if (quit_flag == 1)
exit(1);
}
}
Trying to read from file:
void batch_mode(char *path) {
FILE *fp;
char str[512];
char *commands[128];
char *args[128];
int res;
fp = fopen(path,"r");
if (fp == NULL) {
perror("Error opening file");
exit(4); //file not open
}
while(1) {
if (fgets(str, 512, fp) == NULL)
break;
int i = 0;
parse(str, ";", commands);
while (commands[i] != NULL) {
parse(commands[i], " ", args);
i++;
res = execute(args);
}
}
fclose(fp);
printf("whatever>Press Any Key to Continue\n");
getchar();
}
Parsing strings:
void parse(char *str, char *delimiter, char **args) {
char *pch;
int i = 0;
pch = strtok(str,delimiter);
while (pch != NULL) {
args[i] = pch;
i++;
pch = strtok(NULL, delimiter);
}
args[i] = NULL;
}
Executing with fork:
int execute(char **args) {
char path[50];
pid_t pid;
int status;
if(strcmp(args[0],"quit")==0) return 1; //exited by quit
strcpy(path,"/bin/");
strcat(path,args[0]);
if ((pid = fork()) < 0) {
perror("fork failed");
exit(2);
}
else if (pid == 0) {
if(execvp(path, args) < 0) {
perror("execvp failed");
exit(3);
}
}
else {
while (wait(&status) != pid) /* wait for completion */
;
}
}
Please help?

Have you tried to print all your variables argv, str, commands ... To make sure all is going on as expected there ? It could also help you locate in your script where you segfault. It will take you time, sorry I have no quick answer. For starterprintf("%d", __LINE__) would print the line number you are at, don't forget to add an explicit statement.
Otherwise you could use gdb. There is quite a learning curve, but it could be useful for another project.

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

Unable to find the source of infinite loop

#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<sys/utsname.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<pwd.h>
#include<sys/wait.h>
#include<signal.h>
#define MAX_NO_OF_CMD_ELEMENTS (10)
struct utsname u; /* to get hostname*/
struct passwd *pw; /*to get username*/
int pid;
char* username, *hostname;
char cwd[100];
char *basedir;
int bg;
void init_prompt()
{
getcwd(cwd,100);
basedir = cwd;
uname(&u);
pw = getpwuid(getuid());
username = pw->pw_name;
hostname = u.nodename;
}
void prompt_me()
{
sleep(1);
getcwd(cwd,100);
bg =0;
printf("%s#%s:%s$ ",username,hostname,cwd);
}
void pinfo(char **argv)
{
char path[1024];
char c;
char buf[1024];
char filename[1000];
printf("pid --- %s\n",argv[1]);
sprintf(filename,"/proc/%s/status",argv[1]);
FILE *f = fopen(filename, "r");
FILE *fp;
char state;
fgets(buf,1024,f);
fgets(buf,1024,f);
sscanf(buf, "State: %c\n", &state);
printf("process state = %c\n", state);
fclose(f);
char target_path[1024];
sprintf(filename, "/proc/%d/exe",pid);
int len = readlink (filename, target_path, sizeof (target_path));
char buffer[1024];
if(len ==-1)
{
perror("readlink");
}
else
{
target_path[len] = '\0';
printf("executable path: %s\n", target_path);
}
}
void pwd_me()
{
getcwd(cwd,100);
printf("%s",cwd);
}
void cd_me(char **argv)
{
chdir(argv[1]);
if(getcwd(cwd,100)!=0)
{
perror(" ");
}
if(strcmp("~\0",argv[1])==0||strcmp("\0",argv[1])==0)
chdir(basedir);
}
void echo_me(char **argv,int num)
{
int i;
for(i=0;i<num;i++)
{
printf("%s", argv[i]);
if(i!=0 || i!=num-1)
{
printf(" ");
}
}
}
void execute(char **argv,int num)
{
int i;
pid_t pid;
int status;
if ((pid = fork()) < 0)
{ /* fork a child process*/
printf("*** ERROR: forking child process failed\n");
exit(1);
}
else if (pid == 0)
{ /* for the child process: */
if(strcmp(argv[0],"cd")==0)
cd_me(argv);
else if(strcmp(argv[0],"pwd")==0)
pwd_me();
else if(strcmp(argv[0],"echo")==0)
echo_me(argv,num);
else if(strcmp(argv[0],"pinfo")==0)
pinfo(argv);
else if(strcmp(argv[0],"exit")==0)
return;
int c;
if (c==execvp(argv[0], argv) < 0)
{ /* execute the command */
printf("%d\n", c);
printf("*** ERROR: exec failed\n");
perror(" ");
exit(1);
}
}
else if(bg!=1){
while (wait(&status) != pid);
}
}
void input()
{
int i,j,n,len,c;
char *buffer = 0;
size_t bufsize = 0;
ssize_t characters;
char *cmd[MAX_NO_OF_CMD_ELEMENTS+1];
characters = getline(&buffer, &bufsize, stdin);
len = strlen(buffer);
buffer[len-1]='\0';
if (characters > 0)
{
char *end_str1;
char *token1 = strtok_r(buffer, ";", &end_str1);
int count = 0, wordcnt;
while (token1 != NULL)
{
char *token2;
memset(cmd,0,sizeof(cmd));
char * cmd[MAX_NO_OF_CMD_ELEMENTS + 1]; /* 1+ for the NULL-terminator */
size_t wordcnt = 0;
char *end_str2;
count++;
token2 = strtok_r(token1, " ", &end_str2);
while ((NULL != token2)
&& (MAX_NO_OF_CMD_ELEMENTS > wordcnt)) /* Prevent writing
out of `cmd`'s bounds. */
{
cmd[wordcnt] = token2;
wordcnt++;
token2 = strtok_r(NULL, " ", &end_str2);
}
if(token2==NULL)
{
cmd[wordcnt] = NULL;
execute(cmd, wordcnt);
}
}
}
free(buffer);
}
int main()
{
init_prompt();
while(1)
{
prompt_me();
input();
}
}
I am unable to find the source of the infinite loop that occurs while running this program. Please help?
I think it is most likely to be in the input function, but I'm not sure so to be safe I included the whole program.
Add the following line to the end of the while (token1 != NULL) loop in the input function:
token1 = strtok_r(NULL, ";", &end_str1);
The problem is that you are not updating token1, therefore, token1 will never be NULL, and thus while (token != NULL) will loop forever.
See The linux man page on strtok_r(3)

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

C - wait, fail when getting the returned value

I have the following problem: In my code, here in line 83, I have this: check = wait(NULL);
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>
#include <sys/stat.h>
#include <sys/types.h>
//---------------------------------
//Function: parse_cmdline(const char* cmdline)
//This function takes the input from stdin
//and returns it as array of strings.
//If stdin is /bin/ls -l /usr/include
//the function will return ["/bin/ls","-l","/usr/include"]
//---------------------------------
char** parse_cmdline(const char* cmdline) {
int count, word_count = 0;
char** line_parsed, line_return;
char *pch, *cmdline_copy = (char*)malloc(sizeof(char)*(strlen(cmdline)+1));
strcpy(cmdline_copy, cmdline);
pch = strtok(cmdline_copy," \n\t\r");
while (pch != NULL) {
++word_count;
pch = strtok(NULL, " \n\t\r");
}
line_parsed = (char**)malloc((word_count+1)*sizeof(char*));
count = 0;
strcpy(cmdline_copy, cmdline);
pch = strtok(cmdline_copy," \n\t\r");
while (pch != NULL) {
line_parsed[count] = (char*)malloc((strlen(pch) + 1)*sizeof(char));
strcpy(line_parsed[count], pch);
++count;
pch = strtok(NULL," \n\t\r");
}
line_parsed[count] = NULL;
free(cmdline_copy);
return line_parsed;
}
int main() {
int count = 0, check;
size_t size;
char* line;
char** cmdline;
while(1) {
check = 0;
printf("$Monkey Eats:< ");
getline(&line, &size, stdin);
cmdline = parse_cmdline(line);
pid_t pid = fork();
if (pid == -1) {
perror("fork");
return -1;
} else if(pid == 0) {
struct stat _stat;
stat(cmdline[0],&_stat);
if(_stat.st_mode & S_IXUSR){
execvp(cmdline[0], cmdline);
}else fprintf(stderr,"%s: Permission denied!\n",cmdline[0]);
perror("");
exit(1);
}else {
check = wait(NULL);
}
count = 0;
while(cmdline[count] != NULL) {
free(cmdline[count]);
++count;
}
free(cmdline);
}
return 0;
}
It makes me a problem. When I run it and when I type a command I have the following message:
$Monkey Eats:< ls
ls: Permission denied!
No such file or directory
If I have only wait(NULL); the program runs normally without a problem. Can somebody tell me what is the problem? Thank you :)
The problem is trying to run ls. execvp() doesn't know where ls is. Try running /bin/ls as your command.
The problem is: stat(cmdline[0],&_stat); - the return code is not checked. What if file not found ? The program continues, and finds that _stat.st_mode & S_IXUSR is 0 (randomly).
However you may test the program as is with "/bin/ls" as input..

Get Output of system("insmod mmodule.ko")

I want to run shell command in C program and get stdout output.
I did it in this function:
int run_shell_cmd_nout(const char* cmd)
{
FILE *fp;
char out[4096] = {0};
char str[256] = {0};
char full_cmd[1024] = {0};
int result = 0;
// Compose full shell command
if (!sprintf(full_cmd, "/system/bin/%s", cmd))
{
printf("Failed to compose full shell command\n");
return -1;
}
// Open the command for reading.
fp = popen(full_cmd, "r");
if (fp == NULL)
{
printf("Failed to run command\n");
return -1;
}
// Read the output a line at a time - output it.
while(!feof(fp))
{
if(fgets(str, 256, fp) != NULL)
{
result = -1;
strcat(out, str);
}
}
pclose(fp);
if (result != 0)
{
printf("%s\n", out);
return -1;
}
return 0;
}
But it doesn't work with insmod.
Is there any way to intercept all outputs when invoke insmod?

Resources