For a homework assignment I have to write a basic shell including redirection. The program uses readline to prompt for input, parses the input string, and breaks it down into the executable name, the arguments, and the input/output file(s), if applicable. After parsing the string, it forks and the child execv()'s to the executable that was passed in. I'm using dup2() to change the file descriptors after the fork and before the execv, but am having a problem once the program has execv'd to the new executable. If in my shell I run ls > foo.out, I get: ls: cannot access H��y�A�
$ L��H)�I��$�: No such file or directory
Construction of c->argv:
char *args[6];
int i;
for(i=0;i<=4;i++){
char *_arg=strsep(&_str_cmd," ");
printf("Found _arg: %s\n",_arg);
// If there is an argument and it is not blank
if(_arg && strcmp(_arg,"")!=0){
if(strcmp(_arg,"<")==0){
_cmd.infile=strsep(&_str_cmd," ");
i--;
continue;
}
else if(strcmp(_arg,">")==0){
_cmd.outfile=strsep(&_str_cmd," ");
i--;
continue;
}
}
else{break;}
}
args[i]=(char*)0;
_cmd.binary=args[0];
memcpy(_cmd.argv,args,sizeof _cmd.argv);
How are you constructing c->argv? It must be a NULL-terminated array of char *. You are likely missing the terminator.
In your code handling <... and >..., you skip over an entry in argv, leaving it uninitialized.
Related
Im programming a shell. This is a small piece of it that handles redirects. I only included the snippet that is causing the error. This portion of the program is supposed to take a command/argument line and execute the line accordingly. The wc < junk > junk2 is an example of a command line input and for some reason when i execute the line its producing that error junk: >: open: No such file or directory. The rest of the program functions fine but its breaking here.
: wc < junk > junk2
junk: >: open: No such file or directory
1 4 31 junk2
1 4 31 total
I briefly had it working as intended but for some reason I must have lost a piece of the puzzle because it's broken once again.
Ive looked at my array values and they are as expected. For some clarification I'm using a copy of the arguments array so the original is unchanged by setting the argument copy value to NULL. Im setting the argument copy value to null so the loop doesn't repeatedly use a redirection.
If you have any advise or see the problem that I apparently can't it would be appreciated.
while (arguments[i] != NULL)
{
if ((strcmp(argumentsCopy[i], "<") == 0))
{
if ((access(argumentsCopy[i + 1], R_OK) == -1))
{
printf("Cannot open %s for input\n", argumentsCopy[i + 1]);
fflush(stdout);
redirect = 1;
}
else
{
int fd = open(argumentsCopy[i + 1], O_RDONLY);
dup2(fd, STDIN_FILENO);
close(fd);
argumentsCopy[i] = NULL;
redirect = 1;
execvp(command, &arguments[i + 1]);
}
}
if ((strcmp(argumentsCopy[i], ">") == 0))
{
int fd = open(argumentsCopy[ i + 1], O_RDWR);
dup2(fd, STDOUT_FILENO);
close(fd);
argumentsCopy[i] = NULL;
redirect = 1; // avoid repeat use of redirection symbol in arguments array
execvp(command, &arguments[i + 1]);
}
i++;
}
if (redirect == 0)
{
execvp(command, execArgs); //execArgs is entire command w/ arguments
}
exit 0; //default if error occurred.
First, note that you're not correctly using the execvp function. Once a call to execvp (or any call in the exec family) succeeds, the execution of the current program terminates and is replaced by the program you are execing. Thus, control flow should never proceed past an execvp call unless that call failed. Therefore, you cannot call execvp until you are sure both the stdin and stdout file descriptors have been properly redirected (in the case that you have both input and output redirection). From your code sample, it appears you call execvp once either one is detected, which means your shell can only redirect input or output, not both.
However, that is not the source of the error you're receiving. Consider the input wc < junk > junk2. Based on the information you provided, the command and arguments array will be populated as follows:
command = "wc"
arguments[0] = ">"
arguments[1] = "junk"
arguments[2] = "<"
arguments[3] = "junk2"
arguments[4] = NULL
When i = 0, the first if-statement will be taken, opening the file descriptor and performing execvp with the parameters command and &arguments[i+1]. These correspond to the file to be executed and the argv array passed to that function's main method respectively. In this case, the command is wc and the args are { "junk", "<", "junk2", NULL } (as this is the value of the null terminated array beginning at &arguments[i+1]). On Unix systems, it is conventional for the first argument (argv[0]) to be the name of the current program. Thus the actual command line arguments begin at argv[1] (see Program Arguments for documentation). From wc's perspective, the command line arguments it should process are { "<", "junk2", NULL } after it processes what it believes to be its program name, argv[0], or junk.
wc takes as arguments the list of files it will process. In this case, it believes that list to be < and junk2, as "<" is an operator recognized by bash and other shells, not the executing program. Thus, wc (which believes its name is junk because of argv[0]) attempts to open < as an input file and fails, printing the message:
junk: >: open: No such file or directory
I am trying to create a simple c program that takes user input, passes it to underlying shell and return the ouput of the user command. For eg: pwd will give the present working directory.
I want to do this using fork() and exec() in an infinite loop but I am facing two problems:
My loop terminates after first run
It only takes first argument. 'ls -ltr' will give me the output of 'ls' and not 'ls -ltr'
int runit(char*);
void main() {
int pid=0;
char command[50];
while(1)
{
int d=0;
printf("Please enter your command!\n");
scanf("%s", &command);
switch (pid = fork())
{
case 0: // a fork returns 0 to the child
printf("Child process \n");
d=runit(command);
if(d==-1){
printf("command not found \n");}
break;
default:
wait(5); // a fork returns a pid to the parent
printf("Parent process \n");
break;
case -1: //if something went wrong
perror("fork");
exit(1);
}
}
}
int runit(char* command) { //executing the command
char path[50]="/bin/";
int d = execl(strcat(path,command),command,NULL,NULL);
return(d);
}
Can somebody tell me what I am doing wrong or guide me how to correct this.
This input reading
scanf("%s", &command);
would stop at the first whitespace (unless input failure occurs -- you should check the return value of scanf() in any case). So, when you input ls -ltr, command will have just ls. You need to read lines. For example, use fgets() to read input (and make sure to handle the trailing newline):
fgets(command, sizeof command, stdin);
command[strcspn(command, "\n")] = 0; /* to remove \n if present */
And the next problem is that execl() takes multiple arguments. That means
this usage:
int d = execl(strcat(path,command),command,NULL,NULL);
is broken. You need to split the input command into multiple strings and pass each arguments to execl(). A better option to store the individual strings in an array and use execv() instead.
Another thing you need to be aware of is exec*() family functions do not return on success. So, the return code check isn't very useful - instead you can just use perror() to know why it failed if it failed.
i'm trying to make a shell in c and currently trying the ls command but the child always returns that execvp couldn't work. here is the function.first i get the path i'm in and the command i get from stdin using exec that is why i remove the newline after that i duplicate the command and using strtok(delimiter is " ") i put all the arguments in an array for execv and the "close it" with a null terminating string after that i print the elements of the array which give correct output ex(ls -l give ls "newline" -l) and then i print the path who also gives correctl output (/home/usr_name/Desktop). But the execv never works and it returns the fprintf
void ls(char* path,char* cmd){
int pid,elements,l,i=0;;
l=strlen(cmd);
if(cmd[l-1]=='\n'){
cmd[l-1]='\0';
}
char* ccmd=strdup(cmd);
char* t_cmd;
char* w_cmd[1024];
t_cmd=strtok(ccmd,DELIM);
while(t_cmd!=NULL){
w_cmd[i]=t_cmd;
t_cmd=strtok(NULL,DELIM);
i++;
}
w_cmd[i]='\0';
elements=i;
for(i=0;i<elements;i++){
printf("%s\n",w_cmd[i] );
}
printf("%s\n",path);
pid=fork();
if(pid==0){
execvp(path,w_cmd);
fprintf(stderr, "Child process could not do execvp\n");
}
else{
wait(NULL);
printf("Child exited\n");
}
}
You say that path is equal to "/home/usr_name/Desktop" (with your real user name of course)?
Then that is the problem. If you read an exec manual page it will tell you that the first argument is the command that you should execute.
So to execute ls -l with execvp you should do e.g.
char *w_cmd[] = { "ls", "-l", NULL };
execvp(w_cmd[0], w_cmd);
I need to access a file provided to the program from the command line in the following fashion: ./myProgram < filename
I figured you could do this with command line arguments (argv and argc), but now realize that this is not the case. I was just wondering where this file is inputted to (name or otherwise) so I can access it.
Thanks in advance!
If you're in a unix shell (zsh, bash, etc.) and do something like ./myProgram <filename, the program dosn't receive <filename in argv array. Instead, the shell parses the part with < and redirects the program's input so that it come from the file filename.
More on i/o redirection here
So, you write exactly as if you were reading from stdin (because you are reading from stdin):
#include <stdio.h>
int main()
{
char str[200];
int i;
scanf("%s", str);
scanf("%d", &i);
printf("%s %d\n", str, i);
}
I've separated a given command from the user into substrings , here's the code :
int i;
char *line = malloc(BUFFER);
char *origLine = line;
fgets(line, 128, stdin); // get a line from stdin
// get complete diagnostics on the given string
lineData info = runDiagnostics(line);
char command[20];
sscanf(line, "%20s ", command);
line = strchr(line, ' ');
printf("The Command is: %s\n", command);
int currentCount = 0; // number of elements in the line
int *argumentsCount = ¤tCount; // pointer to that
// get the elements separated
char** arguments = separateLineGetElements(line,argumentsCount);
// here we call a method that would execute the commands
if (execvp(*arguments,*argumentsCount) < 0) // execute the command
{
printf("ERROR: exec failed\n");
exit(1);
}
When I execute the command in execvp(*arguments,*argumentsCount) , it fails .
What's wrong ?
Thanks .
EDIT :
The input from the user is : ls > a.out , hence I have 3 strings , which are :
ls , > , a.out , and it fails .
Shell redirection won't work if you aren't invoking a shell. You also won't have path searching to find the ls program. Some options
use system() instead, and exit when it returns
exec a shell and have it run your command
setup redirection as a shell would, then fork and execute each required child program.
Also your command doesn't make a lot of sense, you probably want ¦ instead of > and may need to specify the directory of a.out if it is not in your path. Consider giving it a meaningful name as well.
From man page of execvp command:
int execvp(const char *file, char *const argv[]);
The second argument is a list of null-terminated C-strings as arguments to the command to be executed by execvp. But in your code, you pass an int as the second argument which is wrong.
If you have list of arguments in the variable arguments then call execvp as:
execvp(arguments[0],arguments);
When you run ls > a.out at the command-line, > and a.out are not arguments passed to the application; they're interpreted by the shell to redirect stdout.
So in short, it is not possible to do what you want to do.1
1. Well, it is, but not this way. Your application would need to interpret the arguments, create the file, and set up a stream redirect.