I am trying to make a some what shell in C but I am having problems with making the ls command. mkdir, and cd work fine but with ls it gives me
"Address out of bounds segmentation error"
Hope somebody can help me. Here's my code.
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <readline/readline.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
printf("\033[1;33mWelcome To Crisp Bacon Shell\n");
while (1) {
printf("\033[0m%s $", hostname);
input = readline("");
command = get_input(input);
child_pid = fork();
if (child_pid < 0) {
perror("Fork failed");
exit(1);
}else if (child_pid == 0) {
/* Never returns if the call is successful */
execvp(command[0], command);
printf("This won't be printed if execvp is successul\n");
} else {
waitpid(child_pid, &stat_loc, WUNTRACED);
}
free(input);
free(command);
}
return 0;
}
char **get_input(char *input) {
char **command = malloc(8 * sizeof(char *));
char *separator = " ";
char *parsed;
int index = 0;
parsed = strtok(input, separator);
while (parsed != NULL) {
command[index] = parsed;
index++;
parsed = strtok(NULL, separator);
}
command[index] = NULL;
return command;
}
The only thing I understand it has something to do with memory and references or pointers but I tried changing everything from & refrencing to pointers and it just gave me more errors what do I do?
There were many undeclared variables in your code snippets. You also need to fetch the hostname, it isn't a global variable. It's also a best practice to declare your functions before using them.
This works fine:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <readline/readline.h>
#include <unistd.h>
#include <sys/wait.h>
char **get_input(char *input) {
char **command = malloc(8 * sizeof(char *));
char *separator = " ";
int index = 0;
char *parsed = strtok(input, separator);
while (parsed != NULL && index < 8) { // you need to make sure the index does not overflow the array
command[index] = parsed;
index++;
parsed = strtok(NULL, separator);
}
command[index] = NULL;
return command;
}
int main() {
printf("\033[1;33mWelcome To Crisp Bacon Shell\n");
while (1) {
// hostname does not exist, you need to fetch it
char hostname[1024];
gethostname(hostname, 1023); // POSIX only
printf("\033[0m%s $", hostname);
char *input = readline(NULL);
char **command = get_input(input);
pid_t child_pid = fork();
if (child_pid < 0) {
perror("Fork failed");
exit(1);
} else if (child_pid == 0) {
/* Never returns if the call is successful */
execvp(command[0], command);
printf("This won't be printed if execvp is successul\n");
} else {
waitpid(child_pid, NULL, WUNTRACED); // since you don't use the middle argument, no need to point to valid data
}
free(input);
free(command);
}
return 0;
}
Related
So I'm trying to create a custom shell for my school project. My method was to create child process, and have that process execute the command using the execvp() function that my professor briefly mentioned in class that we are meant to use. Here's my code, as always, any help is appreciated.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#define MAX_LINE 80
int main(int argc, char *argv[])
{
char *input = (char*)malloc(MAX_LINE*sizeof(char));
int should_run = 1;
while(should_run){
printf("osh>");
fflush(stdout);
pid_t pid;
pid = fork();
if(pid < 0){
printf("error with creating chiled process");
return 0;
}
if(pid == 0){
fgets(input, MAX_LINE, stdin);
char *token = strtok(input," ");
if(execvp(token[0], token) < 0){
printf("Error in execution.");
return(0);
}
//should_run = 0;
}
waitpid(pid, 1, 0);
}
return 0;
}
The prototype of execvp is
int execvp(const char *file, char *const argv[]);
It expects a pointer to char as the first argument, and a NULL-terminated
pointer to an array of char*. You are passing completely wrong arguments.
You are passing a single char as first argument and a char* as the second.
Use execlp instead:
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
So
char *token = strtok(input," \n");
if(token == NULL)
{
fprintf(stderr, "only delimiters in line\n");
exit(1);
}
if(execlp(token, token, NULL) < 0){
fprintf(stderr, "Error in execution: %s\n", strerror(errno));
exit(1);
}
Also the convention in UNIX is to print error messages to stderr and a process with an error should
have an exit status other than 0.
As Pablo's states, you are passing the wrong arguments to execvp().
You can consider coding by yourself a function (char **strsplit(char *str, char delim)) which takes a string and split it into smaller pieces, returning an array of strings.
Also don't ignore compiler's warnings, they tell you a lot of things, and I suggest you to compile with gcc -Wall -Wextra -Werror to get almost any possible error in your program.
I tell you this because waitpid() takes as second argument a pointer to integer, to get an update of the status of the forked program. With this status you how the program exited (normally, segf, bus error...), you can use it to print an error if something went wrong.
You can consider using execv() instead (I know I'm going off topic, but you can learn useful things doing this), and find by yourself the correct executable(s).
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <dirent.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <sys/stat.h>
#define MAX_LINE 255
char **strsplit(char *str, char delim);
char *strjoin(char const *s1, char const *s2);
int isexec(char *path)
{
struct stat buf;
lstat(path, &buf);
if (S_ISREG(buf.st_mode) && (S_IXUSR & buf.st_mode))
return (1);
return (0);
}
static char *find_exec_readdir(char *paths, char *cmd)
{
DIR *dir;
struct dirent *dirent;
char *exec;
exec = NULL;
if ((dir = opendir(paths)) != NULL)
{
while ((dirent = readdir(dir)) != NULL)
{
if (!strcmp(dirent->d_name, cmd))
{
exec = strdup(dirent->d_name);
break ;
}
}
if (closedir(dir))
dprintf(2, "Failed closing dir.\n");
}
return (exec);
}
char *find_exec(char *cmd, char **paths)
{
char *exec;
char *path;
char *tmp;
int i;
i = -1;
exec = NULL;
path = NULL;
if ((cmd[0] == '.' || cmd[0] == '/'))
{
if (isexec(cmd))
return (strdup(cmd));
return (NULL);
}
while (paths[++i])
if ((exec = find_exec_readdir(paths[i], cmd)) != NULL)
{
tmp = strjoin(paths[i], "/");
path = strjoin(tmp, exec);
free(tmp);
free(exec);
break ;
}
return (path);
}
int handle_return_status(int status)
{
int sig;
int i;
if (!WIFEXITED(status) && WIFSIGNALED(status))
{
sig = WTERMSIG(status);
i = -1;
while (++i <= 13)
{
if (print_signal_error(sig))
{
return (-1);
}
}
dprintf(2, "Process terminated with unknown signal: %d\n", sig, NULL);
return (-1);
}
return (0);
}
int main(int argc, char *argv[])
{
char *input = NULL;
char **command = NULL;
int should_run = 1;
int status = 0;
(void)argc;
(void)argv;
if ((input = (char*)malloc(MAX_LINE*sizeof(char))) == NULL)
return (dprintf(2, "Failed to malloc, abort.\n"));
while(should_run){
printf("osh> ");
fflush(stdout);
pid_t pid;
pid = fork();
if(pid < 0)
return (dprintf(2, "error with creating chiled process\n"));
if(pid == 0){
fgets(input, MAX_LINE, stdin);
command = strsplit(input, ' ');
command[0] = find_exec(command[0], strsplit(getenv("PATH"), ':'));
if(execv(command[0], &command[1]) < 0)
return (dprintf(2, "Error in execution.\n"));
//should_run = 0;
}
waitpid(pid, &status, 0);
handle_ret_status(status);
}
return 0;
}
I am trying to use fork with execvp to run two shell commands concurrently. I have two problems which are when I input mkdir folder1&mkdir folder2, it creates a folder named folder1 and another folder named folder2? (the question mark is included in the folder name). The other problem is that the code exits after performing the two commands.
Here is the code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MAXLINE 80 /* The maximum length command */
int main(void) {
char *args [MAXLINE / 2 + 1]; /* command line arguments */
char *line = (char *) malloc((MAXLINE + 1) * sizeof (char));
char *firstCommand = (char *) malloc((MAXLINE + 1) * sizeof (char));
char *secondCommand = (char *) malloc((MAXLINE + 1) * sizeof (char));
int shouldrun = 1; /* flag to determine when to exit program */
pid_t pid;
while (shouldrun) {
printf("osh>");
fflush(stdout);
fgets(line, MAXLINE, stdin);
if (strncmp(line, "exit", 4) == 0) {
shouldrun = 0;
} else {
firstCommand = strsep(&line, "&");
secondCommand = strsep(&line, "&");
pid = fork();
if (pid == 0) {
// child
if (secondCommand != NULL) {
char *token;
int n = 0;
do {
token = strsep(&secondCommand, " ");
args[n] = token;
n++;
} while (token != NULL);
execvp(args[0], args);
}
} else {
// parent
char *token;
int n = 0;
do {
token = strsep(&firstCommand, " ");
args[n] = token;
n++;
} while (token != NULL);
execvp(args[0], args);
}
}
}
return 0;
}
UPDATE 1:
I tried to follow Kevin's answer. I am trying to execute multiple processes concurrently, e.g. ps&ls&who&date. I tried a recursive method which gave me the same behavior. Here is my code:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#define MAXLINE 80 /* The maximum length command */
void execute(char *command) {
char *args [MAXLINE / 2 + 1]; /* command line arguments */
char *parentCommand = strsep(&command, "&");
pid_t pid = fork();;
if (pid == 0) {
// child
if (command != NULL) {
execute(command);
}
} else {
// parent
char *token;
int n = 0;
do {
token = strsep(&parentCommand, " ");
args[n] = token;
n++;
} while (token != NULL);
execvp(args[0], args);
}
}
int main(void) {
char *line = (char *) malloc((MAXLINE + 1) * sizeof (char));
int shouldrun = 1; /* flag to determine when to exit program */
while (shouldrun) {
printf("osh>");
fflush(stdout);
fgets(line, MAXLINE, stdin);
if (strncmp(line, "exit", 4) == 0) {
shouldrun = 0;
} else {
execute(line);
}
}
return 0;
}
For your question about why it doesn't loop, you're calling fork once but calling execvp twice. If successful, execvp will not return. Nothing will get back to run the loop again. What you need to do is call fork once for each execvp. I suggest you move the fork and execvp calls to a separate function:
void run_command(const char* command) {
/* I suggest you also check for errors here */
pid_t pid = fork();
if (pid == 0) {
/* get args, call execvp */
}
}
/* in your loop */
run_command(firstCommand);
run_command(secondCommand);
Regarding the first question, you need to truncate the \n from the line. Regarding the second question, you can use system function from stdlib.h header file which will not terminate your program.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#define BUFFER 64
char *read_command(void);
char **parse_line(char *line);
int execute(char **arguments);
int main(void)
{
char *command = NULL;
char **arguments;
int status;
do
{
printf("protoulis_7968> ");
command = read_command();
arguments = parse_line(command);
status = execute(arguments);
free(arguments);
free(command);
}while(status);
}
char *read_command(void)
{
char *command = NULL;
ssize_t buf = 0;
getline(&command, &buf, stdin);
return command;
}
char **parse_line(char *line)
{
int buffer = BUFFER;
int pos = 0;
char **tokens = malloc(buffer * sizeof(char*));
char *token;
if (!tokens)
{
printf("Error allocating memory with malloc\n");
exit(0);
}
token = strtok(line, " \t\r\n\a");
while(token != NULL)
{
tokens[pos] = token;
pos++;
if (pos >= buffer)
{
buffer += BUFFER;
tokens = realloc(tokens, buffer * sizeof(char*));
if (!tokens)
{
printf("Error reallocating memory!\n");
exit(0);
}
}
token = strtok(NULL, " \t\r\n\a");
}
tokens[pos] = NULL;
return tokens;
}
int execute(char **arguments)
{
// printf("%*c\n", arguments);
int pid, waitPid, status;
pid = fork();
if(pid == 0) //child process
{
if (execvp(arguments[0], arguments) == -1)
perror("Error with EXECVP\n");
}
else if (pid < 0)
perror("Error PID < 0\n");
else //parent process
{
do
{
waitPid = waitpid(pid, &status, WUNTRACED);
}while(!WIFEXITED(status) && !WIFSIGNALED(status));
}
return 1;
}
Well, I have written the above code simulating a command line interpreter in C. I would like to be able to execute multiple commands by entering them in one line. I mean I want to pass as input for example the line: ls -l ; touch hello.c ; pwd. Having passed this entire line I want to separate the commands by the semicolon and let the system execute each command in any order. I believe I have to use the strtok function but have done many attempts and managed nothing. Any help would be really appreciated!
strtok will not suffice in your case. The reason is that it will take you to the next subcommand but to be able to execute this subcommand, you have to have it as a single string.
Two ways to solve this issue:
Count how many ';' there is, replace them by '\0' to have several contiguous strings in memory, then execute them one by one.
Write a function that splits your command string into a 2d array of subcommands, then execute them one by one.
Here's a code that does that if you need some inspiration:
Using linked lists: https://github.com/yoones/chelpers/blob/master/src/split.c
2d array version: https://github.com/yoones/hsn/blob/master/src/tools/split.c
I am experimenting with writing a simple shell to support all the usual functions.
So far, despite my haphazard approach to it, I have been met with success - I have been able to fork() new processes with parameters but other certain methods don't seem to run.
When I run my shell, functions like pwd, ls, help work whilst other functions such as cd, mkdir don't - why is this & what research/investigation can I do in order to begin to address this?
I've included my code so far (although I'm not sure if it will actually help).
Thanks very much
EDIT: My output, when my shell is running in cygwin, if I type "cd /cygdrive/c" the output is "Command not found"
#include <stdio.h>
#include <stdlib.h>
#include <math.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
const int MAX_ARGS = 9;
size_t nBytes = 64; //Data type representing size of objects (unsigned)
int bytesRead = -1;
char *cmd = NULL;
char prompt[] = "DaSh-> ";
int argc;
char **argv;
int pid;
int childpid;
int status;
void process();
int readcmd();
int main() {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(stderr, NULL, _IONBF, 0);
while (1) {
bytesRead = -1;
printf(prompt);
while (bytesRead == -1) {
bytesRead = readcmd();
}
process(cmd); //process the single line input into arguments
if (strcmp(argv[0], "exit") == 0) { //If user types exit
exit(0);
}
childpid = fork();
if (childpid == 0) { //This is run by the child
execvp(argv[0], argv);
printf("Command not recognised\n");
exit(1);
}
else if (childpid > 0 ) {
waitpid(-1, &status, 0);
}
}
return 0;
}
int nrows = 10;
int ncolumns = 2;
void process(char argStr[]) {
argv = malloc(MAX_ARGS * sizeof(char *)); //Array of pointers to the char first letter of each argument
char delims[] = " \n"; //Delimit about space
int i = 0;
argv[i] = strtok(argStr, delims);
while (argv[i] != NULL) {
argv[++i] = strtok( NULL, delims);
}
argc = i;
}
int readcmd() {
return getline(&cmd, &nBytes, stdin);
}
I'm trying to write a simple C shell. My problem is that I have written the program so that when the user enters a NULL value I've got the shell to exit and stop running. However after using different shells i've realised that the shell continues to loop. Is there anyway to fix this without having to rewrite my code? I'm still quite a novice to C.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define MAX_CMD_SIZE 512
int getPath(){
printf("PATH = %s\n", getenv("PATH"));
return 0;
}
int setPath(char* arg){
setenv("PATH", arg, 1);
return 0;
}
int setwd() {
char *arg;
arg = getenv("HOME");
chdir(arg);
return 0;
}
int main()
{
char buff[MAX_CMD_SIZE]; //buff used to hold commands the user will type in
char *defaultPath = getenv("PATH");
char *args[50] = {NULL};
char *arg;
int i;
pid_t pid;
setwd();
while(1){
printf(">");
if (fgets(buff, MAX_CMD_SIZE, stdin) == NULL) { //Will exit if no value is typed on pressing enter
setPath(defaultPath);
getPath();
exit(0);
}
arg = strtok(buff, " <|>\n\t");
i = 0;
if (arg == NULL) return -1;
while (arg != NULL){
printf("%s\n", arg);
args[i] = arg;
arg = strtok(NULL, " <|>\n\t");
i++;
}
if (strcmp(args[0], "exit") == 0 && !feof(stdin)){ //Will exit if input is equal to "exit" or ctrl + d
setPath(defaultPath);
getPath();
exit(0);
}
else {
pid = fork();
if (pid < 0){ //Error checking
fprintf(stderr, "Fork Failed\n");
} else if (pid == 0){ //This is the child procsess
execvp(args[0], args);
exit(-1);
} else { //Parent Process
wait(NULL); // Parent will wait for child to complete
}
}
}
return 0;
}