I need some help with a simple shell for class, and I'm worried I don't quite understand how the execvp() function works.
The shell does not do much, does not support piping, redirection, scripting or anything fancy like that. It only reads a command, reads in the options (with the command as option[0]), and forks.
It worked a few times, then started giving me errors about not being able to find commands. Other similar questions posted here had to do with piping or redirection.
Please forgive the noobcode, it's not pretty, but I hope it's legible:
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/types.h>
#define OPT_AMT 10
const size_t SIZE = 256;
int i = 0;
int o = 0;
int main(void) {
// initializing data
int exit = 0;
char cwd[SIZE];
char cmd[SIZE];
char input[SIZE * OPT_AMT];
char *opt[OPT_AMT];
getcwd(cwd, SIZE);
// main loop
while (exit == 0) {
// reset everything
o = 1;
i = 0;
cmd[0] = "\0";
while (i < OPT_AMT) {
opt[i++] = "\0";
}
// get input
printf("%s $ ", cwd);
scanf("%s", cmd);
gets(input);
opt[0] = cmd;
char *t = strtok(input, " ");
while (t != NULL) {
opt[o++] = t;
t = strtok(NULL, " ");
}
// if exit, exit
if (strcmp(cmd, "exit") == 0) {
exit = 1;
}
// else fork and execute
else {
pid_t pID = fork();
if (pID == 0) { // child process
execvp(cmd, opt);
} else if (pID < 0) { // failed to fork
printf("\nFailed to fork\n");
} else { // parent process
wait(0);
}
}
}
// cleanup
printf("\nFinished! Exiting...\n");
return 0;
}
Anything blatantly wrong? I most recently added the exit condition and the resetting the options array.
Also, this is my first question, so remind me of any rules I may have broken.
For starters, this
cmd[0] = "\0";
ought to be
cmd[0] = '\0';
Listen to your compiler's warnings.
To enable them use the options -Wall -Wextra -pedantic (for gcc).
Also you might better want to initalise opt's elements to point to "nothing", that is NULL but to the literal "\0":
while (i < OPT_AMT) {
opt[i++] = NULL;
}
as execvp() requires opt to be a NULL-terminated array of C-"strings" (thanks Paul for mentioning/wording the relevant background).
Also^2: Do not use gets(), as it's evil and not even part of the C Standard anymore. Instead of
gets(input);
use
fgets(input, sizeof input, stdin);
gets() easyly let's the user overflow the (input) buffer passed. (Mentioning this came to my mind without Paul, btw ... ;-))
Related
Trying to create a new bash shell in C and bring it to the user, this is my code:
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
char* secretpass = "password";
char password[50];
printf("%s", "Password: ");
fgets(password, 50, stdin);
password[strcspn(password, "\n")] = 0;
if (!strcmp(password, secretpass)){
pid_t pid = fork();
if (pid == 0){
execl("/bin/bash", "bash", NULL);
}
}
return 0;
}
After running the code (ELF), i get a new bash shell in ps but it's not my shell because echo $$ brings the first shell, what can I do to get the new shell to screen? kernel module will help?
EDIT:
edited my code for more help, /dev/chardev is a char device that come up with the boot process, the driver is also 0666 (.rw.rw.rw.) writable for everyone, the system(cmd) says at there is no permission at console, even if I do the command myself after execve.
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <stdlib.h>
#include <unistd.h>
#include <pwd.h>
#define MAX 50
#define USERNAME 2
int main(int argc, char const *argv[])
{
// Declare variables.
const char* username = argv[USERNAME];
char* password = (char*)calloc(MAX, sizeof(char));
char* cmd = (char*)calloc(5 * MAX, sizeof(char));
char* secretpass = "password";
printf("%s", "Password: ");
fgets(password, MAX, stdin);
password[strcspn(password, "\n")] = 0;
if (!strcmp(password, secretpass)){
int err;
struct passwd* pw_user = getpwnam(username);
//printf("-%s-%s-%d-%d-%s-%s-%s-\n", pw_user->pw_name, pw_user->pw_passwd,
//pw_user->pw_uid, pw_user->pw_gid, pw_user->pw_gecos,
//pw_user->pw_dir, pw_user->pw_shell);
if ( (err = fchown(0, pw_user->pw_uid, pw_user->pw_gid) ) != 0)
printf("%s %d\n", "fchown error", err);
if ( (err = setpgid(0, 0) ) != 0)
printf("%s %d\n", "setpgid error", err);
if ( (err = tcsetpgrp(0, getpid()) ) != 0)
printf("%s %d\n", "tcsetpgrp error", err);
if ( (err = chdir(pw_user->pw_dir) ) != 0)
printf("%s %d\n", "chdir error", err);
if ( (err = setgid(pw_user->pw_gid) ) != 0)
printf("%s %d\n", "setgid error", err);
if ( (err = setuid(pw_user->pw_uid) ) != 0)
printf("%s %d\n", "setuid error", err);
sprintf(cmd, "%s \"%d %d %d\" %s", "echo", pw_user->pw_uid, pw_user->pw_gid, getpid(), "> /dev/chardev");
system(cmd);
const char *args[] = {"bash", "--rcfile", "/etc/bashrc", NULL};
char LOGNAME[MAX];
char HOME[MAX];
char USER[MAX];
sprintf(LOGNAME, "%s%s", "LOGNAME=", pw_user->pw_name);
sprintf(HOME, "%s%s", "HOME=",pw_user->pw_dir);
sprintf(USER, "%s%s", "USER=", pw_user->pw_name);
const char *env[] = {"SHELL=/bin/bash", LOGNAME, HOME, USER, "IFS= ","PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin", "TTY=tty1", NULL}; /* need to generate these; TTY is passed to you */
execve("/bin/bash", args, env);
}
else
execl("/bin/login", "login", NULL);
return 0;
}
always setpgid error and if username isn't root there are also setuid and chdir errors.
From the comments: you're trying to write a login program.
Ok. That's a bit more, and you're going about this all the wrong way. We don't want to fork at all. Let init worry about waiting. Anyway, we get to write a long sequence here:
int targetuid = ... ; /* You need a strategy for getting this */
int targetgid = ... ; /* You need a strategy for getting this */
const char *homdir = ... ; /* You need a strategy for getting this */
if (!strcmp(password, secretpass)){
/* Start up the user's shell */
fchown(0, targetuid, targetgid);
setpgid(0, 0);
tcsetpgrp(0, getpid());
chdir(homedir);
setgid(targetgid);
setuid(targetuid);
const char *args[] = {"-bash", NULL};
const char *env[] = {"SHELL=/bin/bash", "LOGNAME=...", "HOME=...", "USER=...", IFS="...", PATH=/bin:/usr/bin", "TERM=...", NULL }; /* need to generate these; TERM is passed to you */
execve("/bin/bash", args, env);
}
This is very much involved and I actually don't recommend this unless you really have to. I learned a ton when I tried this but it took forever to get it working right.
Particular subpoints: 1) The tty device needs to be owned by the user after a successful login. Thus the fchown(0, ...) call to give ownership to the user. 2) The chdir() first is traditional; you could reverse the order if you wanted to but I don't see why. 3) Starting the shell with a leading - in argv0 tells the shell that it's a login shell. Check in ps -f and you can see this.
I picked up your new code; it actually looks pretty good. The only mistake I can spot is my own; the variable is TERM not TTY (now corrected in my sample above) and the best place to get its value is getenv(). On running your code I only had to make only one correction; that is putting the -bash back. The only error it spits out is the one about chardev; what is chardev?
I guess your failures aren't in this code at all but rather in your kernel.
Info from chat: OP has a custom kernel with a custom /dev/chardev; I can't explain the failures as the code works for me. There may or may not be other changes to the kernel.
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
Before I start, I just want to say that this is for a school assignment of mine. I'm really close to finishing, except well, since I'm here, obvious to say, I'm stuck on a problem :(.
First of I'll explain what my assignment wants:
The assignment wants me to create a command-line program in C that allows the user to type in N number of /bin/ commands (E.g. > /bin/ps /bin/ls "/bin/which gcc"). Once the user enters the command and hits the enter key, the parent process (the parent process is the program) will create N child processes (i.e. no. of /bin/ commands entered = no. of child processes parent process will create). Each child will run one of the N commands. All the children will be running concurrently, with the parent waiting for each child to terminate.
Once a child terminates, the parent will print whether the command executed successfully or not (E.g. "Command /bin/ps has completed successfully" or "Command /bin/ps has not completed successfully") and once all children have been terminated, the parent will print "All done, bye!"
The issue:
So I've managed to get my child processes to run concurrently, the only issue is that I'm not sure how to pipe the value of the command (like /bin/ps or /bin/which gcc) from the child process to the parent process to print out the success or not message. I've tried putting the write pipe above my execv which allows me to pipe what I want but the execv won't output anything and I can't put my pipe code below my execv because in that case, then while my execv output will show, my pipe won't. I did think that it might be due to close(1) but commenting that out didn't change the result.
So what I am trying to achieve is something like this:
> /bin/ls "/bin/which gcc" /bin/domainname /bin/fake_command
Output:
/usr/bin/gcc
localdomain
Command /bin/which gcc has completed successfully
Command /bin/domainname has completed successfully
a.txt b.c
Command /bin/ls has completed successfully
Command /bin/fake_command has not completed successfully
All done, bye!
>
But right now, I'm getting:
> /bin/ls "/bin/which gcc" /bin/domainname /bin/fake_command
Output:
Command /bin/which gcc has completed successfully
Command /bin/domainname has completed successfully
Command /bin/ls has completed successfully
Command /bin/fake_command has not completed successfully
>
As you can see, my execv output for the /bin/ commands aren't shown in the output.
I've tried searching SO for people who faced this similar issue as me but none of their solutions managed to work for me which is why I'm asking here. If there's anything you're not clear about, please let me know and I will try my best to explain.
The code:
q1.c
#include "q1.h"
int main(int argc, char *argv[])
{
int
child_status,
pipe_array[2];
pid_t child;
char *success_or_fail;
char *msg_buffer = malloc(CHAR_MAX);
if (msg_buffer == NULL)
{
return -1;
}
struct timespec tw = {.tv_sec = 0, .tv_nsec = 10000000L};
Tuple *process_tuple;
size_t tuple_size = sizeof(*process_tuple) + sizeof(pid_t) + sizeof(char *);
process_tuple = calloc(argc, tuple_size);
if (process_tuple == NULL)
{
return -1;
}
// if (pipe(pipe_array) == -1)
// {
// perror("pipe: ");
// return -1;
// }
for (int j = 1; j < argc; j++)
{
child = fork();
if (child == 0)
{
int
executed,
num_of_words,
num_of_chars;
char
string[strlen(argv[j]) + 1],
*backup = argv[j];
snprintf(string, sizeof(string), "%s", argv[j]);
num_of_chars = get_num_of_chars(string);
num_of_words = get_num_of_words(string);
char *command[num_of_chars + 1];
preparing_the_command(num_of_words, string, command);
// close(pipe_array[0]);
// close(1);
// dup2(pipe_array[1], STDOUT_FILENO);
// write(pipe_array[1], backup, sizeof(backup));
process_tuple[j - 1].pid = getpid();
process_tuple[j - 1].command = backup;
printf(" %i-PID -> %i\n %i-Command -> %s\n\n", process_tuple[j - 1].pid, process_tuple[j - 1].pid, process_tuple[j - 1].pid, process_tuple[j - 1].command);
executed = execv(command[0], command);
nanosleep(&tw, 0);
if (executed == -1)
{
exit(EXIT_FAILURE);
}
else
{
exit(EXIT_SUCCESS);
}
}
else if (child == -1)
{
perror("fork() failed: ");
exit(EXIT_FAILURE);
}
}
printf(" PID -> %i\n Command -> %s\n\n", process_tuple[0].pid, process_tuple[0].command);
// while ((child = waitpid(-1, &child_status, 0)) != -1)
// {
// for (int o = 0; o < argc; o++)
// {
// printf(" PID -> %i\n Command -> %s\n\n", process_tuple[o].pid, process_tuple[o].command);
// }
// close(0);
// close(pipe_array[1]);
//
// dup2(pipe_array[0], STDIN_FILENO);
// char *recipient;
//
// read(pipe_array[0], recipient, sizeof(recipient));
// if (!(WIFEXITED(child_status) && (WEXITSTATUS(child_status) == 0)))
// {
// success_or_fail = "not completed successfully";
// }
// else
// {
// success_or_fail = "completed successfully";
// }
// snprintf(msg_buffer, CHAR_MAX, "Command %s has %s\n", recipient, success_or_fail);
// fputs(msg_buffer, stdout);
// }
fputs("All done, bye!\n", stdout);
free(msg_buffer);
return 0;
}
int get_num_of_chars(const char string[])
{
int
i = 0,
num_of_chars = 0;
while (string[i++] != '\0')
{
if (string[i] != ' ' && string[i] != '\t')
{
num_of_chars++;
}
}
return num_of_chars;
}
int get_num_of_words(const char string[])
{
int
i = 0,
num_of_words = 0;
bool is_not_separator = false;
while (string[i++] != '\0')
{
if (string[i] == ' ' || string[i] == '\t')
{
is_not_separator = false;
}
else if (!is_not_separator)
{
is_not_separator = true;
num_of_words++;
}
}
return num_of_words;
}
void preparing_the_command(int num_of_words, char string[], char *command[])
{
char *token;
for (int j = 0; j < num_of_words && (token = strtok_r(string, " ", &string)); j++)
{
command[j] = token;
}
command[num_of_words] = (void *) NULL;
}
q1.h
#ifndef ASSIGNMENT2Q1_Q1_H
#define ASSIGNMENT2Q1_Q1_H
/***************
** LIBRARIES **
***************/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
/*************
** STRUCTS **
*************/
typedef struct
{
pid_t pid;
char *command;
} Tuple;
/*************************
** FUNCTION PROTOTYPES **
*************************/
int get_num_of_chars(const char string[]);
int get_num_of_words(const char string[]);
void preparing_the_command(int num_of_words, char string[], char *command[]);
#endif //ASSIGNMENT2Q1_Q1_H
I've been writing a shell program in C. The program is working as expected in Linux (Ubuntu 16.04) but I'm getting unexpected output in MacOS (10.14.2 Mojave).
/* A shell program.
*/
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
void input(char* argv[]);
void print_arr(char *argv[]); // For debugging
int
main(void)
{
while (1)
{
pid_t pid;
char *argv[100];
// Display shell prompt
write(1, "(ash) $ ", 8);
// Take user input
input(argv);
// print_arr(argv); // DEBUG STATEMENT
if (argv[0] != NULL)
{
// Exit if exit command is entered
if (strcmp(argv[0], "exit") == 0)
{
exit(0);
}
// Create child process
if ((pid = fork()) > 0)
{
wait(NULL);
}
else if (pid == 0)
{
// print_arr(argv); // DEBUG STATEMENT
execvp(argv[0], argv);
printf("%s: Command not found\n", argv[0]);
exit(0);
}
else
{
printf("Fork Error!\n");
}
}
}
}
/* Takes input from user and splits it in
tokens into argv. The last element in
argv will always be NULL. */
void
input(char* argv[])
{
const int BUF_SIZE = 1024;
char buf[BUF_SIZE];
int i;
buf[0] = '\0';
fgets((void*) buf, BUF_SIZE, stdin);
i = 0;
argv[i] = strtok(buf, " \n\0");
while (argv[i] != NULL)
{
argv[++i] = strtok(NULL, " \n\0");
}
}
/* Print argv for debugging */
void
print_arr(char *argv[])
{
int i = 0;
while (argv[i] != NULL)
{
printf("%d: %s\n", i, argv[i]);
++i;
}
}
In Linux:
(ash) $ ls
// files and folders are listed
In MacOS (with debug statements):
(ash) $ ls
0: p?M??
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
: Command not found
(ash) $ ls
0: ls
0: ??M??
I don't understand that why are the contents of char* argv[] getting modified across fork()?
I've also tried it in the default clang compiler and brew's gcc-4.9, the results are same.
When a program behaves different for no good reason, that's a VERY good sign of undefined behavior. And it is also the reason here.
The array buf is local to the function input and ceases to exist when the function exits.
One way of solving this is to declare buf in main and pass it to input. You will also need the size of the buffer for fgets.
void
input(char * argv[], char * buf, size_t size)
{
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
Another solution (although I suspect many will frown upon it) is to declare buf as static, but then you would need to change BUF_SIZE to a #define or a hard coded value, since you cannot have a static VLA.
#define BUF_SIZE 1024
void
input(char * argv[])
{
static char buf[BUF_SIZE];
buf[0] = '\0';
fgets(buf, sizeof buf, stdin);
argv[0] = strtok(buf, " \n\0");
for(int i=0; argv[i] != NULL; i++) argv[i+1] = strtok(NULL, " \n\0");
}
I removed the cast to void* since it's completely unnecessary. I also changed the while loop to a for loop to make the loop variable local to the loop.
Yeah I know that sounds crazy but that is the only way I can describe it at this point. I’m writing a program for a class that mimics a terminal, in that it takes commands as inputs and executes them. (I’ll put some code below) As you will see, the program holds a history of commands historyArgs so that the user can execute recent commands.
Command history is listed when the user performs Ctrl-C. Recent commands are accessed with the command 'r' (for most recent) and r 'x' (where x is matches the first letter of a command in recent history). When I started implementing the 'r' command, I started getting this segfault. I then reverted all my changes and added one line at a time. I found that adding even primitive variable declaration causes a segfault (int temp = 10;) But this is where it gets stranger. I believe the line that causes a segfault (int temp = 10;) is never accessed. I put printf statements and flush the output at the beginning of the if block to see if the block has been entered, but they don't execute.
setup was provided for us. It takes the user input and puts it in char *args[] i.e. input = ls -a -C, args = {"ls", "-a", "-C", NULL, ... NULL}. I marked the line in main that somehow leads to a segfault.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <string.h>
#define BUFFER_SIZE 50
static char buffer[BUFFER_SIZE];
#define MAX_LINE 80 /* 80 chars per line, per command, should be enough. */
char *historyArgs[10][MAX_LINE/2 + 1];
int historyCount = 0;
int indexOfLatestCommand = 0;
/* the signal handler function */
void handle_SIGINT() {
//write(STDOUT_FILENO,buffer,strlen(buffer));
if(historyCount > 0){
printf("\n%i command(s), printing most recent:\n", historyCount);
int i;
for(i = 0; i < historyCount && i < 10; i++){
printf("%i.] %s ", i+1, historyArgs[i][0]);
//print args
int j = 1;
while(historyArgs[i][j] != NULL){
printf("%s ", historyArgs[i][j]);
j++;
}
printf("\n");
}
}
else{
printf("\nNo recent commands.\n");
}
fflush(stdout);
}
/**
* setup() reads in the next command line, separating it into distinct tokens
* using whitespace as delimiters. setup() sets the args parameter as a
* null-terminated string.
*/
void setup(char inputBuffer[], char *args[],int *background)
{
int length, /* # of characters in the command line */
i, /* loop index for accessing inputBuffer array */
start, /* index where beginning of next command parameter is */
ct; /* index of where to place the next parameter into args[] */
ct = 0;
/* read what the user enters on the command line */
length = read(STDIN_FILENO, inputBuffer, MAX_LINE);
start = -1;
if (length == 0)
//exit(0); /* ^d was entered, end of user command stream */
if (length < 0){
perror("error reading the command");
exit(-1); /* terminate with error code of -1 */
}
/* examine every character in the inputBuffer */
for (i = 0; i < length; i++) {
switch (inputBuffer[i]){
case ' ':
case '\t' : /* argument separators */
if(start != -1){
args[ct] = &inputBuffer[start]; /* set up pointer */
ct++;
}
inputBuffer[i] = '\0'; /* add a null char; make a C string */
start = -1;
break;
case '\n': /* should be the final char examined */
if (start != -1){
args[ct] = &inputBuffer[start];
ct++;
}
inputBuffer[i] = '\0';
args[ct] = NULL; /* no more arguments to this command */
break;
case '&':
*background = 1;
inputBuffer[i] = '\0';
break;
default : /* some other character */
if (start == -1)
start = i;
}
}
args[ct] = NULL; /* just in case the input line was > 80 */
}
int main(void)
{
int i;
char inputBuffer[MAX_LINE]; /* buffer to hold the command entered */
int background; /* equals 1 if a command is followed by '&' */
char* args[MAX_LINE/2+1];/* command line (of 80) has max of 40 arguments */
int status;
struct sigaction handler;
handler.sa_handler = handle_SIGINT;
sigaction(SIGINT, &handler, NULL);
strcpy(buffer,"Caught <ctrl><c>\n");
while (1){ /* Program terminates normally inside setup */
background = 0;
printf("COMMAND->");
fflush(0);
setup(inputBuffer, args, &background); /* get next command */
//If command wasn't empty
if(args[0] != NULL){
if(strcmp(args[0], "r") != 0){
//Copy into history if not a recent call
for(i = 0; i < MAX_LINE/2+1 && args[i] != NULL; i++){
historyArgs[historyCount%10][i] = malloc(strlen(args[i]));
strcpy(historyArgs[historyCount%10][i], args[i]);
}
indexOfLatestCommand = historyCount%10;
historyCount++;
}
//Create child process
int pid = fork();
//In child process
if(pid == 0){
if(strcmp(args[0], "r") == 0){
//If only "r" was entered, execute most recent command
if(args[1] == NULL){
printf("Entering recent?\n");
fflush(stdout);
int temp = 10; //SEGFAULTS HERE IF THIS IS INCLUDED
execvp(historyArgs[indexOfLatestCommand][0], &historyArgs[indexOfLatestCommand][0]);
}
else{
//Find in args[1][0] history, run if found
for(i = indexOfLatestCommand; i >= 0; i--){
if(historyArgs[i][0][0] == args[1][0]){
execvp(historyArgs[i][0], &historyArgs[i][0]);
break;
}
}
if(i == -1){
for(i = historyCount > HISTORY_SIZE ? HISTORY_SIZE : historyCount; i > indexOfLatestCommand; i--){
if(historyArgs[i][0][0] == args[1][0])
execvp(historyArgs[i][0], &historyArgs[i][0]);
break;
}
}
}
}
else execvp(args[0], &args[0]);
}
//In parent process
else if (pid > 0){
/*If child isn't a background process,
wait for child to terminate*/
if(background == 0)
while(wait(&status) != pid);
}
}
}
}
Another thing worth mentioning is that declaring a variable in that spot doesn't cause a segfault. Only assigning a value to a new variable does. Reassigning globals in that section also doesn't cause a segfault.
EDIT: What triggers a crash. Commands execute correctly. When you run it, you can type in any command, and that should work. It isn't until I perform Ctrl-C and print out the history that the program segfaults.
Example input:
ls
ls -a
grep
Ctrl-C
HEADS UP: if you decide to run this, know that to end the task, you will probably need to use the kill command because I haven't implement "q" to quit.
Symptoms like you see (unrelated code changes appear to affect the nature of a crash) usually mean that your program caused undefined behaviour earlier. The nature of the behaviour changes because your program is relying on garbage values it has read or written at some stage.
To debug it, try and remove all sources of undefined behaviour in your program. The most obvious one is the content of your void handle_SIGINT() function. The only things you can portably do in a signaler are:
Set a variable of type volatile sig_atomic_t, or other lock-free type, and return
Do other stuff and call _Exit, abort or similar.
Especially, you cannot call any library functions as they may not be re-entrant.
For a full specification see section 7.14.1 of the current C Standard. If you are also following some other standard, e.g. POSIX, it may specify some other things which are permitted in a signal handler.
If you do not intend to exit then you must set a flag , and then test that flag from your main "thread" later to see if a signal arose.