why wouldn't ls work with execvp? - c

I have an assignment requesting me to write a mini-shell - something that will get a command to execute, execute it, and wait for some more commands.
when I pass to this mini-shell the command ls . it prints the contest of the current directory. When I pass to it ls it prints nothing. Why?
here is my code:
#include <stdio.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <string.h>
#include <unistd.h>
#include <assert.h>
#include <stdlib.h>
#include <stdbool.h>
#define MAX_CMD_SIZE 40
char** parse(char*);//will parse the arguments for the execv/excevp commands.
int main(int argc, char** argv)
{
bool debug = false;
assert(argc <= 2);
if (argc == 2)
{
//check for string -debug
debug = true;
}
if (debug)
printf("INFO: Father started PID[%d]\n", getpid());
char *command = malloc(MAX_CMD_SIZE);
while(true)
{
printf("minishell> ");
fgets(command, MAX_CMD_SIZE, stdin);
if (strcmp(command, "exit\n") == 0)
return 0;
pid_t pid = fork();
assert(pid >= 0);
if (pid == 0) //child
{
if (debug)
printf("INFO: Child started PID[%d]\n", getpid());
char** buf = parse(command);
if (debug)
{
int i;
for (i = 0; buf[i]; i++)
printf("INFO: buf[%d] = %s\n",i,buf[i]);
}
execvp(buf[0],buf);
return 0;
}
else //father
{
int status;
wait(&status);
if (debug)
printf("INFO: Child with PID[%d]terminated, continue waiting commands\n", pid);
}
}
}
char** parse(char *string)
{
char** ret = malloc(sizeof(char*));
ret[0] = strtok(string, " ");
int i = 0;
for (; ret[i]; ret[i] = strtok(NULL, " \n"))
{
ret = realloc(ret, sizeof(char*) * ++i);
}
return ret;
}

Your parse() command includes a \n in the last argument :)
So with a single ls, you're actually executing ls\n, which is not in the PATH (of course)
The problem is that on the first strtok() call, you only pass " " as a delimiter. Use " \n" (like in the subsequent calls) and the problem goes away.
You could also fix it by chomping the \n:
int l = strlen (string);
if (l > 0 && string [l - 1] == '\n') string [l - 1] = '\0';
and only using " " as a delimiter.

Related

Basic shell that takes multiple word command

I am trying to implement a shell that takes a command ; however, I can not get it to work properly. For example, if I type "ls -a", I get this:
invalid option -- '
'
Try 'ls --help' for more information.
I have probably made some bad mistakes as I am a beginner so please forgive me. Also, I will put the code that reads in the command into a function. Its just like this for testing- thanks.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <unistd.h>
#include <stddef.h>
int main()
{
pid_t pid;
int status;
char* token;
char* argv[20];
char input[100];
printf("AP> ");
while (1)
{
fgets(input, 100, stdin);
token = strtok(input, " ");
int i = 0;
//walk through other tokens
while (token != NULL) {
argv[i] = malloc(strlen(token) + 1);
strncpy(argv[i], token, strlen(token));
//argv[i] = token;
i++;
token = strtok(NULL, " ");
}
argv[i] = NULL; //argv ends with NULL
pid = fork();
if (pid < 0)
{
perror("fork error");
return EXIT_FAILURE;
}
else if (pid == 0)
{
// child process
execvp(argv[0], argv);
perror("execl error");
return EXIT_FAILURE;
}
else {
// parent process
if (waitpid(pid, &status, 0)<0)
{
perror("waitpid error");
return EXIT_FAILURE;
}
}
printf("AP> ");
}
return 0;
}
If you insert the following loop after the inner while loop, then you'll see that you aren't removing the newline character at the end of input before the while loop starts.
for (i=0; argv[i]; i++)
printf("argv[%d] = '%s'\n", i, argv[i]);
Alternatively, you can use:
" \n"
as your separators instead of just
" "

Address out of bounds in C, problems with making an ls command

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

Global variable not staying set, maybe caused by fork()

I'm trying to write a very very simple unix shell in C, and I have the basics of what I need working, except support for a history command. I have a global 2D char array that holds the history of all entered commands. Commands are added before the fork() system call, and I was originally printing out the value of the history global array after strings were added, and they were printing out correctly, so I'm not sure why it doesn't print out when the command "history" is used at the shell.
Thank to anyone who takes a look.
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <unistd.h>
#include "myhistory.h"
int BUFFER_SIZE = 1024;
char history[100][80];
int command_index = 0;
int main(int argc, char *argv[]){
int status = 0;
int num_args;
pid_t pid;
while(1){
char *buffer_input, *full_input;
char command[BUFFER_SIZE];
char *args[BUFFER_SIZE];
printf("myshell> ");
buffer_input = fgets(command, 1024, stdin);
full_input = malloc(strlen(buffer_input)+1);
strcpy(full_input, buffer_input);
if (command_index >= 100) {
command_index = 0;
}
strncpy(history[command_index], full_input, strlen(full_input) + 1);
command_index += 1;
parse_input(command, args, BUFFER_SIZE, &num_args);
//check exit and special command conditions
if (num_args==0)
continue;
if (!strcmp(command, "quit" )){
exit(0);
}
if(!strcmp(command, "history")){
int i;
fprintf(stderr,"%d\n",(int)pid);
for(i = 0; i < command_index; i++){
fprintf(stdout, "%d: %s\n",i+1,history[command_index]);
}
continue;
}
errno = 0;
pid = fork();
if(errno != 0){
perror("Error in fork()");
}
if (pid) {
pid = wait(&status);
} else {
if( execvp(args[0], args)) {
perror("executing command failed");
exit(1);
}
}
}
return 0;
}
void parse_input(char *input, char** args,
int args_size, int *nargs){
char *buffer[BUFFER_SIZE];
buffer[0] = input;
int i = 0;
while((buffer[i] = strtok(buffer[i], " \n\t")) != NULL){
i++;
}
for(i = 0; buffer[i] != NULL; i++){
args[i] = buffer[i];
}
*nargs = i;
args[i] = NULL;
}
Change:
fprintf(stdout, "%d: %s\n",i+1,history[command_index]);
to:
fprintf(stdout, "%d: %s\n",i+1,history[i]);

I am making linux command Program by C-language. and I want to know what is wrong the code?

I wanna ask about how to make exec process programing by C.
Now, I typed like these code, and I use strtok and strdup.
my code wrong assign value from input, so could you see my code and could you teach what is wrong in the code.
#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
#include <string.h>
int main(){
int pid;
int status, i = 0;
char input[256], path[30];
const char* a[10];
char* token;
char* split = " ";
while(input != "exit"){
printf("Please type your command:\n");
fgets(input, 256, stdin); /* emxape: ls -alf argv[0] = "ls" argv[1] = -alf*/
printf("Input: %s", input);
i = 0;
token = strdup(strtok(input,split));
while(token != NULL){
a[i] = token;
token = strdup(strtok(NULL,split));
printf("a%d is %s\n", i, a[i]);
i++;
}
int j = 0;
while( j < sizeof(a))
{
printf("%s", a[j]);
j++;
}
//free(copy);
if(strcmp(a[0],"cd")== 0 )/*for compare pointer*/
{
if (chdir((a[1])) == 0) {
printf("Sucess change Directory.\n");
return 0;
}
else {
printf("Fault change Directroy\n");
perror("");
}
if (a[1] == NULL)
{
if(chdir(getenv("HOME"))<<0)
perror("cd");
return 0;
}
else
{
if(chdir(a[1]) <0)
perror("cd");
}
}
sprintf(path,"%s",a[0]);
pid = fork();
/*Child process*/
if(pid == 0){
//execl(path,a[0],a[1],NULL);
execl(a[0],a[1],a[2],NULL);
printf("Wrong child process: %s",path);
exit(0);
}
/*Parents Process*/
else {
wait(&status);
}
}//while
printf("Thank you.");
}/*main*/
There are at least 2 problems
Line 26: token = strdup(strtok(NULL,split)); should be token = strdup(strtok(input,split));
After the first modification, the loop in line 24 can run. but still does not run correctly. It seems the strtok(), and strdup() does not run correctly.

pipes causing infinite output

When I simply pipe ls -l | sort with this, the program just spits out the results from ls -l infinitely. Can anyone see what's wrong?
Assume that you only have to look at the main function. This will compile, though.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/wait.h>
#define COMMAND_LINE_LENGTH 256
#define HISTORY_LENGTH 10
#define TOKEN_MAX 50
#define DIRECTORY_LENGTH 5
#define DIRECTORY_PREFIX "/bin/"
struct prog_def
{
//Binary location
char *bin;
//Is this program expecting a pipe?
int expecting_pipe;
//Arguments
char *args[TOKEN_MAX + 1];
pid_t pid;
} prog_def;
int get_prog_defs(const char* buf, struct prog_def prog_defs[])
{
char *line = malloc(strlen(buf) + 1);
int prog_count = 0;
char* token;
strcpy(line, buf);
line[strlen(buf)] = 0;
while(1)
{
int arg_count = 0;
//The first time through we have to pass the line
token = strtok(line, " ");
//Each subsequent call we have to pass NULL
//http://www.cplusplus.com/reference/cstring/strtok/
line = NULL;
//Start building the binary location string
prog_defs[prog_count].bin = (char*)malloc(strlen(token) + DIRECTORY_LENGTH + 1);
//Concatenate the directory prefix and command name
strcat(prog_defs[prog_count].bin, DIRECTORY_PREFIX);
strcat(prog_defs[prog_count].bin, token);
//The first argument execvp will expect is the binary location itself
//Redundant but if I wasn't too lazy to read the doc then I'd know why
prog_defs[prog_count].args[arg_count++] = prog_defs[prog_count].bin;
while(1)
{
prog_defs[prog_count].expecting_pipe = 0;
//Check next token for end, pipe, IO redirection, or argument
token = strtok(NULL, " ");
//If we've consumed all tokens
if (token == NULL)
break;
//Pipe
if (strcmp(token, "|") == 0)
{
prog_defs[prog_count - 1].expecting_pipe = 1;
break;
}
//Regular argument
prog_defs[prog_count].args[arg_count++] = token;
}
++prog_count;
if (token == NULL) break;
}
return prog_count;
}
int main(int argc, char** argv)
{
char command[COMMAND_LINE_LENGTH] = {0};
//Generic loop counter
int x = 0;
while(1)
{
printf(">");
//Get the command
gets(command);
struct prog_def prog_defs[TOKEN_MAX];
int prog_count = get_prog_defs(command, prog_defs);
//Keep the previous out fd for the in of the subsequent process
int prev_out_fd = open("/dev/null", O_RDONLY);
for (x = 0; x < prog_count; ++x)
{
//Create a pipe for both processes to share
int pipefd[2];
if (x != prog_count -1)
{
pipe(pipefd);
}
prog_defs[x].pid = fork();
if(prog_defs[x].pid == 0)
{
dup2(prev_out_fd, STDIN_FILENO);
close(pipefd[1]);
if(x != prog_count - 1)
{
dup2(pipefd[1], STDOUT_FILENO);
close(pipefd[0]);
close(pipefd[1]);
}
execvp(prog_defs[x].bin, prog_defs[x].args);
prev_out_fd = pipefd[0];
close(pipefd[1]);
}
close(prev_out_fd);
prev_out_fd = pipefd[0];
close(pipefd[1]);
}
printf("\n");
for (x = 0; x < prog_count; ++x)
{
waitpid(prog_defs[x].pid, NULL, 0);
}
}
}
You call malloc to get some memory for a string, which will be uninitialized, so contain random garbage. You then call strcat which will attempt to append another string to the random garbage and almost certainly run off the end of the malloc'd space, leading to random confusing behavior and crashes.
You also use prog_defs[prog_count - 1] before ever incrementing prog_count, so the first time through the loop, (when prog_count == 0) this will write before the start of the array, which also leads to random confusing behavior and crashes.

Resources