C: execlp() and > - c

I want to run execlp() from C file and write the result to some output file.
I use the line:
buff = "./cgi-bin/smth";
execlp(buff, buff, "> /cgi-bin/tmp", NULL);
where smth is a compiled c script.
But smth prints to stdout, and no file appears.
What happens, and how to put script result to an output file?

You have to handle it yourself with dup2 if using execlp. You can look at how I handle file out with execvp in comparison. I pass a flag for out redirection and then I handle it:
if (structpipeline->option[0] == 1) { /* output redirection */
int length = structpipeline[i].size;
char *filename = structpipeline->data[length - 1];
for (int k = length - 2; k < length; k++)
structpipeline->data[k] = '\0';
fd[1] = open(filename, O_WRONLY | O_CREAT, 0666);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
} /* TODO: input redirection */
execvp(structpipeline[i].data[0], structpipeline[i].data);
See also this question
Redirecting exec output to a buffer or file

Related

Redirecting I/O in a custom shell program written in C

I have been working on a custom shell script and have come to a small error when redirecting output with the code given below. In its current state the code works perfectly but when passing to execvp args throws errors such as : (ls ">" no such file or directory). I know this is because it is passing the whole args[] to the parent shell which isn't working. Adding in the args[j] = NULL takes away the "<"/ ">" thus fixing the error, but also causes the redirections to not work anymore. How can I get it to not throw an error but also work properly? I have read multiple versions of this question but cant seem to find an answer. Thanks in advance for any help.
switch (fork()){
case -1:
fprintf(stderr, "error forking");
case 0://CHILD
for(int j = 0; j < size; j++){
if(!strcmp(args[j], "<")){//looking for input character
++ext;
if((in = open(args[j+1], O_RDONLY)) < 0){//open file for reading
fprintf(stderr, "error opening file\n");
}
dup2(in, STDIN_FILENO);//duplicate stdin to input file
close(in);//close after use
//args[j] = NULL;
}//end input chech
if(!strcmp(args[j],">")){//looking for output character
++ext;
out = creat(args[j+1], 0644);//create new output file
dup2(out, STDOUT_FILENO);//redirect stdout to file
close(out);//close after usere
// args[j] = NULL;
}//end output check
if(!strcmp(args[j], ">>")){//looking for append
++ext;
int append = open(args[j+1],O_CREAT | O_RDWR | O_APPEND, 0644);
dup2(append, STDOUT_FILENO);
close(append);
// args[j] = NULL;
}
}//end loop
execvp(args[0],args);//execute in parent
fprintf(stderr, "error in child execi \n");//error
exit(0);
default://PARENT
wait(&status); //wait for child to finish
}//end switch
When you are parsing redirections (e.g. <, >, >>) and doing your open/dup2, you have to strip them from the argument list you pass to execvp.
So, given your args, you need a second (e.g. args_clean) argument list that you only copy over the program name and its arguments.
And, you need an extra increment of j to skip over the redirection file in args (i.e. just doing j + 1 isn't equivalent).
Here's the cleaned up child code [please pardon the gratuitous style cleanup]:
char *args_clean[size];
int cleanidx = 0;
for (int j = 0; j < size; j++) {
if (!strcmp(args[j], "<")) { // looking for input character
++j;
if ((in = open(args[j], O_RDONLY)) < 0) { // open file for reading
fprintf(stderr, "error opening file\n");
}
dup2(in, STDIN_FILENO); // duplicate stdin to input file
close(in); // close after use
continue;
} // end input chech
if (!strcmp(args[j], ">")) { // looking for output character
++j;
out = creat(args[j], 0644); // create new output file
dup2(out, STDOUT_FILENO); // redirect stdout to file
close(out); // close after usere
continue;
} // end output check
if (!strcmp(args[j], ">>")) { // looking for append
++j;
int append = open(args[j], O_CREAT | O_RDWR | O_APPEND, 0644);
dup2(append, STDOUT_FILENO);
close(append);
continue;
}
args_clean[cleanidx++] = args[j];
} // end loop
args_clean[cleanidx] = NULL;
execvp(args_clean[0], args_clean); // execute in parent
fprintf(stderr, "error in child execi \n"); // error
exit(0);
Also, see my answer here for something similar with pipes: fd leak, custom Shell
And, for a full blown shell, see my answer: Implementing input/output redirection in a Linux shell using C and look at the embedded pastebin link

Redirecting input and output in a shell

Hi I've been programming a shell in c and I got stuck while trying to redirect. While redirecting the stdout in my program works the stdin doesn't.
void redirect(node_t* node){
// mode 0: >$%d mode 1: < mode 2: > mode 3: >>
int input = 0;
if(node->redirect.mode == 2){
input = 1; // >
} else{
input = 0; // <
}
int pid = 0;
int *status = 0;
char * filename = node->redirect.target; // filename
int fd;
fd = open(filename, O_WRONLY | O_CREAT | O_TRUNC);
if((pid = fork()) == 0){
dup2(fd, input); // STDIN OR STDOUT
close(fd);
node_t* node2 = node->redirect.child;
execvp(node2->command.program, node2->command.argv); // execute program
printf("failed to execvp\n");
exit(1);
} else {
wait(status);
}
}
I'm new to the fork() but my question is what am I doing wrong here that redirecting stdout works but stdin it writes nothing to the given file.
As mentioned in the comments, you need to use different open options depending on whether you're opening the file for input or output redirection. You can put this into your if.
int flags;
if(node->redirect.mode == 2){
input = 1; // >
flags = O_WRONLY | O_CREAT | O_TRUNC;
} else{
input = 0; // <
flags = O_RDONLY;
}
int pid = 0;
int *status = 0;
char * filename = node->redirect.target; // filename
int fd;
fd = open(filename, flags, 0666);
Also, you need to specify the permission modes for the case where the output file is created. It's OK to specify this argument all the time, it will be ignored when O_CREAT isn't in the flags.

Redirecting execvp path

I'm trying to write a simple code which execute a program from subfolders from a input file and print thr result into a output file.
My problem is that when i execute the program it keeps failing on me. since the execvp command is trying to look for an exe named "a.out" on the wrong location. in (desktop rather than searching the correct path address).
here's the code. please help me out :)
pid_t runner;
char enter[] = "/home/demo/Desktop/OS/Ex1/Ex12/code/input.txt"; // input file
char path[] = "/home/demo/Desktop/OS/Ex1/Ex12/Ex1/ronen/"; correct path
char *r [] = {"./a.out", NULL};
int savedFD = dup(0);
int sever2Fd=dup(1);
int fdin = open(enter,O_RDONLY);
int fdout = open ("output.txt", O_CREAT | O_RDWR, 0466);
dup2(fdin, 0);
dup2(fdout, 1);
if ((runner = fork()) < 0) {perror("could not make fork");}
else if (runner == 0) {
if (execvp(r[0],r) < 0 ) {printf("Failed!\n");}
} else if (runner != 0) {
waitpid(runner,0,0);
dup2(savedFD, 0);
dup2(sever2Fd, 1);
printf("done\n");
}
close(fdin);close(fdout);
The answer was simple.
"chdir(wanted path)"
int dirchange = chdir(argv[1]);

Trying to read from file

I'm trying to create a program that runs commands from user input.
At the moment it works for multiple word commands but I'm trying to implement redirections.
I started with taking input from a file and it's not working but I'm not getting any error (I'm testing using the "wc -l < text.txt" command, the text.txt file is the the same dir as the program.)
Here is the code:
- input is the str with the user's input
- before coming to this method I already checked that it has a redirection on it
redirect(int proc, char * input){
char * comm;
if(proc == 1){ //in
comm = strsep(&input, "<");
}
else{ //out
comm = strsep(&input, ">");
}
int proc2 = check(input);
if(proc2 == 0){ //only one redirection
if(proc == 1){ //in
input = trim(input);
int fd = open(input, O_RDWR);
close(0);
dup2(fd, 0);
close(fd);
comm = trim(comm);
char ** words = parse(comm);
char str[105];
strcpy(str, "/bin/");
strcat(str, words[0]);
shrink(str);
if(!execvp(str, words)){ /*exec failed */
exit(1);
}
}
else{ //out
}
}
else{ //more than one redirection/pipe
}
}
edit
I need to use the execvp command to run the user input.
The user command "<" needs to change the stdin to be the file after it.
I changed the stdin to be the text.txt but I don't know how to pass it as an arg so the execvp can run it.
what types of commands you are trying to execute ?
if your commands are dos commands, you can read the user inputs in a string variable
, and when you want to execute the file . create a bat file then execute it
by the following code
Process.Start("your file path ");
If its just about execution of the user specified commands then to execute the shell commands using you can use the system() call. This function takes the command to be executed as argument and executes it on the command shell. You would not need to make any separate file.
you can take the command user wants to execute as string and then pass it to system() as argument to execute it.
ie
system("wc -l < text.txt");
System() works fine for both Linux and Windows.
References : Execute a Linux command in the c program
http://www.gnu.org/software/libc/manual/html_node/Running-a-Command.html
After a lot of testing and research I found that the file that I was using did not have the permissions necessary for the execvp to read it from it.
I figure it out when I wrote the code for writing into a file and then try to read that newly created file and it worked (after adding the flags).
Here is the code:
redirect(int proc, char * input){
char * comm;
if(proc == 1){ //in
comm = strsep(&input, "<");
}
else{ //out
comm = strsep(&input, ">");
}
int proc2 = check(input);
if(proc2 == 0){ //only one redirection
if(proc == 1){ //in
input = trim(input);
int fd = open(input, O_RDWR);
close(0);
dup2(fd, 0);
close(fd);
comm = trim(comm);
char ** words = parse(comm);
if(!execvp(words[0], words)){ /*exec failed */
exit(1);
}
}
else{ //out
input = trim(input);
int fd = open(input, O_CREAT | O_RDWR, S_IRUSR | S_IWUSR);
dup2(fd, 1);
close(fd);
comm = trim(comm);
char ** words = parse(comm);
if(!execvp(words[0], words)){ /*exec failed */
exit(1);
}
}
}
else{ //more than one redirection/pipe
}
}
So I run the command "ls > text.txt" and it creates the text.txt file with the "ls" results in it and then run the "wc -l < text.txt" command and it returns the lines in the file.

Implementation of Redirection in User-Created Shell is not working properly

I am trying to create a shell similar to bash, with redirection and pipes.
In my main(), I call a parser and then call the function below. The problem I am running into is the fact that when I run my shell, it outputs to the terminal correctly, but it does not output to the file correctly when using the >, 1>, or 2>.
For instance, if I call:
pwd > foo5.txt
I end up receiving:
>
foo5.txt
In the text file I write too as opposed to the stdout (for ">"/"1>") or stderr (for "2>") which I am trying to achieve.
This is my code to fork and create the child process:
pid_t create_process(char *part, int const pipes[][2], int pipenum)
{
pid_t pid; // Initialize variables/pointers/arrays.
char *args[64];
int argc=0, n;
char *arg=strtok(part, " \t");
//char const **filename = args;
while(arg != NULL)
{
args[argc++]=arg;
arg=strtok(NULL, " \t");
}
args[argc++]=NULL;
pid = fork(); // Create Fork.
if(pid == 0)
{
int m;
if(pipes[pipenum][STDIN_FILENO] >= 0)
dup2(pipes[pipenum][STDIN_FILENO], STDIN_FILENO); // FD 0.
if(pipes[pipenum][STDOUT_FILENO] >= 0)
dup2(pipes[pipenum][STDOUT_FILENO], STDOUT_FILENO); // FD 1.
// Close all pipes.
for(m=0; m<64; m++)
{
if(pipes[m][STDIN_FILENO] >= 0)
close(pipes[m][STDIN_FILENO]);
if(pipes[m][STDOUT_FILENO] >= 0)
close(pipes[m][STDOUT_FILENO]);
}
char *filename;
char *newargs[64];
newargs[63] = NULL;
int i = 0;
int j = 0;
for(i = 0; i<64; i++)
{
if (args[i] == ">")
{
i++;
if (args[i] != NULL)
{
filename = args[i];
int redir = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
dup2(redir, 1);
close(redir);
}
}
else if (args[i] == "2>")
{
i++;
if (args[i] != NULL)
{
filename = args[i];
int redir = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
dup2(redir, 2);
close(redir);
}
}
else if (args[i] == "2>")
{
i++;
if (args[i] != NULL)
{
filename = args[i];
int redir = open(filename, O_WRONLY | O_TRUNC | O_CREAT, S_IRUSR | S_IRGRP | S_IWGRP | S_IWUSR);
dup2(redir, 2);
close(redir);
}
}
else if (args[i] == 0)
{
break;
}
else
{
newargs[j] = args[i];
j++;
cout<<"The arg is: " << newargs[j] <<endl;
}
}
execvp(newargs[0], newargs);
fprintf(stderr, "Command not found.\n");
exit(255);
}
else if(pid < 0)
{ // Error checking.
fprintf(stderr, "Fork Failed\n");
}
return(pid);
}
UPDATE: Now my code will not recognize the commands, and the arguments being printed (for error checking) appear as such:
ls > foo5.txt
The arg is: fprintf
The arg is:
The arg is: ▒
Command not found.
I see a few problems here:
First, you have the for loop where you scan the command arguments for redirection syntax (if (strcmp(args[i],">")==0) and so on) - but if the condition is true (meaning you found a redirection character) you're always opening args[2], not args[i+1].
Second (and this is why the redirection syntax gets passed on to the command you're running as command arguments) - once you detect redirection syntax, you don't remove the redirection operator or the target filename from the list of arguments that you pass to execvp().
For instance, if args[] = {"echo", "a", ">", "logfile", 0}, your code detects a request to send the output to a new file called "logfile" and redirects the FDs correctly, but it still passes those three arguments ["a", ">", "logfile"] to the command.
Third - in your loop, you're calling execvp() at the end of each conditional statement - meaning that you don't get to the end of argument processing before you launch the new process. You need to process all the command arguments for shell syntax and then exec the command.
To fix the various problems with arg handling, probably the most effective solution is to build a new argument list as you're processing the raw ones provided by the user. For instance (using a==b as a string equality test for brevity)
if (args[i] == ">")
{
i++; //skip the arg
if (args[i]) { // check we haven't hit the end of the arg list
filename = args[i];
// then open the file, dup it to stdout or whatever, etc...
}
else if (args[i] == "<") // repeat for other redirection syntax...
else { // Finally, handle the case where we didn't identify any shell syntax:
newargs[j] = args[i]; // Copy the arg to the new list, since it isn't "special"
j++; // Size of newargs[] has been increased
}
Note that this still doesn't handle things like no whitespace around the ">" character: "echo foo>file" will just print "foo>file"... Syntax processing gets a little more complicated in that case, as you've got to account for quoting rules and escape characters to process the arguments correctly.

Resources