C system calls fails - c

I'm trying to write a code which manipulates standard input and output and redirect them to files, and then use execvp (also tried other exec's) to run a program that simply uses printf and scanf , but the execvp fails..
Relevant code:
int pid2 = fork();
if (pid2 == 0) {
int fdInput = open("myinputfile", O_RDONLY);
close(0);
dup(fdInput);
int fdOutput = open("results.txt", O_WRONLY | O_CREAT | O_TRUNC);
close(1);
dup(fdOutput);
char* tmp[]={"...somepath/prog"};
execvp("...somepath/prog", tmp);
}
My prog:
int main(){
int x;
scanf("%d",&x);
printf("Hello World! %d",x);
return 0;
}
myinputfile contains only -> 4
I tried two main things:
copying the code from prog and hardcoding it into my code instead of the call to execvp, which works fine and I can see "Hello world! 4" in results.txt
running "mypath" in the terminal manually which also seems to work(with the standard I/O).
I can't understand why it's not working, I tried everything I could think of..

Your array of arguments passed to execvp() is not NULL-terminated.
Per the POSIX exec() documentation:
...
The argument argv is an array of character pointers to null-terminated strings. The application shall ensure that the last member of this array is a null pointer. These strings shall constitute the argument list available to the new process image. The value in argv[0] should point to a filename string that is associated with the process being started by one of the exec functions.
...
Your code should be
int pid2 = fork();
if (pid2 == 0) {
int fdInput = open("myinputfile", O_RDONLY);
close(0);
dup(fdInput);
int fdOutput = open("results.txt", O_WRONLY | O_CREAT | O_TRUNC);
close(1);
dup(fdOutput);
// note the NULL terminator
char* tmp[]={"...somepath/prog", NULL };
execvp("...somepath/prog", tmp);
}

Related

Using execvp with dup2 raises EFAULT error

I'm building a small linux shell and am trying to implement the > operator to redirect the output of the commands to a file.
The issue I have is that when I try to run something like ls > test.txt, I get a Bad Address (EFAULT) error.
However, if I try it without the redirection, everything works as expected.
I've trimmed the code to a minimum to test only for ls, but i still get the same error, here's the code.
int saved_stdout;
__pid_t id = fork();
if (id == 0) {
saved_stdout = dup(1);
int fd = open("test.txt", O_RDWR | O_CREAT, S_IRUSR | S_IWUSR);
dup2(fd, STDOUT_FILENO);
dup2(fd, STDERR_FILENO);
close(fd);
char* args[] = {"\0"};
execvp("ls", args);
fprintf(stderr, "Value of errno: %d\n", errno);
perror("Error printed by perror");
} else {
int status;
waitpid(id, &status, 0);
if (saved_stdout) {
dup2(saved_stdout, 1);
close(saved_stdout);
}
}
Does anyone as an idea on what I'm doing wrong here?
Thanks a lot
The execvp function expects the argument array to be terminated by a null pointer, not an empty string.
You should also remember that the argument array includes argv[0].
So the array should be like
char* args[] = { "ls", NULL };
Does anyone as an idea on what I'm doing wrong here?
The main problem is likely that your arguments to execvp() are incorrect:
char* args[] = {"\0"};
execvp("ls", args);
There are two things definitely wrong with that:
The argument array needs to be terminated with a null pointer. "\0" is not a null pointer; rather it is an array containing two null characters, and it decays to a valid, therefore non-null, pointer.
Even if "\0" were a null pointer, you would be short one argument. The first element of the argument vector, at index 0, should be a pointer to a string representing the program name.
In other words:
char* args[] = { "ls", NULL };
execvp("ls", args);
Additionally, the redirection you are performing is not consistent with the POSIX shell's treatment of the > redirection operator. In that form, that operator redirects only the standard output, not the standard error. Furthermore, it should open the designated file write-only, not read/write, because writing to it is all the program needs to do. Opening it read/write could cause it to fail to redirect to an existing file on which the user has write permissions but not read permissions.
Furthermore, The file mode you specify for the event that a new file is created also produces behavior inconsistent with the POSIX shell's. You should specify read/write permissions for user, group, and other, and let that be modified according to the umask in effect:
int fd = open("test.txt", O_WRONLY | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH);

more(1) writing to stdout without actually applying the pager

I'm trying to create a C program that runs printenv | grep [parameters] | sort | more, and so far everything is working, and the program is printing the sorted environment variables to stdout as it's supposed to. The only problem is that there is no pager, even though the last process I'm starting - the only one with a file descriptor to stdout - is more. Here's the relevant code, I've tried editing out stuff that I know works.
#define PIPE_READ 0
#define PIPE_WRITE 1
int main(int argc, char *argv[], char *envp[])
{
int pipe_fd[3][2];
pipe(pipe_fd[0]);
pipe(pipe_fd[1]);
pipe(pipe_fd[2]);
pid[0]=fork();
if(pid[0]==0) /*grep-process*/
{
dup2(pipe_fd[0][PIPE_READ], STDIN_FILENO);
dup2(pipe_fd[1][PIPE_WRITE], STDOUT_FILENO);
/*close all 3 pipes, both ends, then execute grep*/
argv[0]= "grep";
execvp("grep",argv);
}
pid[1]=fork();
if(pid[1]==0) /*sort-process*/
{
dup2(pipe_fd[1][PIPE_READ], STDIN_FILENO);
dup2(pipe_fd[2][PIPE_WRITE], STDOUT_FILENO);
/*close all 3 pipes, both ends, then execute sort*/
execlp("sort","sort", (char*) 0);
}
pid[2]=fork();
if(pid[2]==0) /*more-process*/
{
dup2(pipe_fd[2][PIPE_READ], STDIN_FILENO);
/*close all 3 pipes, both ends, then execute more*/
execlp("more","more", (char*) 0);
}
/*parent-process*/
/*close all pipes except for the write end on the first, then write*/
for(i=0; envp[i]!=NULL; i++)
{
write(pipe_fd[0][PIPE_WRITE],envp[i], strlen(envp[i]));
write(pipe_fd[0][PIPE_WRITE],"\n",1);
}
close(pipe_fd[0][PIPE_WRITE]);
Of course I do check the return values of every call, and where I close all the pipes I simply wrote a comment to simplify reading it.
A call to ./a.out PATH results in this:
INFOPATH=/usr/share/info
MANPATH=/usr/local/vol/matlab/7.4.0/man:/usr/kerberos/man:/usr/local/share/man:/
usr/share/man/en:/usr/share/man:/usr/man
MANPATH=/usr/local/vol/matlab/7.4.0/man:/usr/kerberos/man:/usr/local/share/man:/
usr/share/man/en:/usr/share/man:/usr/man
MODULEPATH=/usr/local/vol/modulefiles:/pkg/modules/modulefiles:/etc/site/modulef
iles:/usr/share/Modules/modulefiles:/etc/modulefiles
MODULEPATH=/usr/local/vol/modulefiles:/pkg/modules/modulefiles:/etc/site/modulef
iles:/usr/share/Modules/modulefiles:/etc/modulefiles
...
...

using sort with dup2

I'm experimenting with this dup2 command in linux. I've written a code as follows:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
int main()
{
int pipe1_ends[2];
int pipe2_ends[2];
char string[] = "this \n is \n not \n sorted";
char buffer[100];
pid_t pid;
pipe(pipe1_ends);
pipe(pipe2_ends);
pid = fork();
if(pid > 0) { /* parent */
close(pipe1_ends[0]);
close(pipe2_ends[1]);
write(pipe1_ends[1],string,strlen(string));
read(pipe2_ends[0], buffer, 100);
printf("%s",buffer);
return 0;
}
if(pid == 0) { /* child */
close(pipe1_ends[1]);
close(pipe2_ends[0]);
dup2(pipe1_ends[0], 0);
dup2(pipe2_ends[1],1);
char *args[2];
args[0] = "/usr/bin/sort";
args[1] = NULL;
execv("/usr/bin/sort",args);
}
return 0;
}
I expect this program to behave as follows:
It should fork a child and replace its image with sort process. And since the stdin and stdout are replaced with dup2 command, I expect sort to read input from the pipe and write the output into the other pipe which is printed by the parent. But the sort program doesn't seem to be reading any input. If no commandline argument is given, sort reads it from the stdin right? Can someone help me with this problem, please.
Many thanks!
Hm. What's happening is that you aren't finishing your write: after sending data to the child process, you have to tell it you're done writing, either by closing pipe1_ends[1] or calling shutdown(2) on it. You should also call write/read in a loop, since it's quite likely in the general case that read at least won't give you all the results in one go. Obviously the full code checks all return values, doesn't it?
One final thing: Your printf is badly broken. It can only accept null-terminated strings, and the result returned by read will not be null-terminated (it's a buffer-with-length, the other common way of knowing where the end is). You want:
int n = read(pipe2_ends[0], buffer, 99);
if (n < 0) { perror("read"); exit(1); }
buffer[n] = 0;
printf("%s",buffer);

Reading my child pipe created by exec() with C

I'm just starting to learn C programming and I have some uncertainty about fork(), exec(), pipe(), etc.
I've developed this code, but when I execute it, the variable c remains empty, so I don't know if the child isn't writing to the pipe, or the parent isn't reading from it.
Could you help me please? This is the code:
int main() {
int pid=0;
int pipefd[2];
char* c=(char *)malloc(sizeof(char));
FILE *fp;
pipe(pipefd);
pid=fork();
if (pid==0){
close(pipefd[0]);
dup2(pipefd[1],1);
close(pipefd[1]);
execl("ls -l | cut -c28","ls -l | cut -c28", (char *) 0);
}
else{
close(pipefd[1]);
read(pipefd[0], c, 1);
char* path="/home/random";
char* txt=".txt";
char* root=malloc(strlen(path) + strlen(txt) + sizeof(char));
strcpy(root,path);
strcat(root,c);
strcat(root,txt);
close(pipefd[0]);
fp=fopen(root,"w+");
(...)
}
The problem is that the final root string its only "/home/random.txt" because there is nothing in the char c, and what I want is to open the file "/home/random(number stored in char c).txt".
execl executes a single command, and is not aware of shell concepts such as pipes. If you want to execute a shell command, you will have to execute a shell, as follows:
execl("/bin/sh","/bin/sh","-c","ls -l | cut -c28", (char*) 0);
Always check the return value of the system calls (like execve(2) and derived functions like execl(3)), and use the errno(3) to figure out what went wrong.
In your case the execl line fails.
Using strcpy/strcat seems a bit excessively complex. snprintf can turn those 3 lines into one.
snprintf( root, size_of_buf, "/home/random%s", c );
Additionally, check your error codes. As noted, execl is failing and you don't know it. fork, dup2, ...,can also fail, you want to know sooner rather than later.

C: `write error: Bad file descriptor` after fork, dup2, and execv

Continuing on this problem, but I'll reiterate:
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: write error: Bad file descriptor
Here is the code for my child process (this is after fork()):
int _child(struct command *c){
int ret;
/* When given `ls > foo.out`, these next two lines output:
** c->infile is (null)
** c->outfile is foo.out
*/
printf("c->infile is %s\n",c->infile);
printf("c->outfile is %s\n",c->outfile);
if(c->infile){
int fd = open( c->infile, O_RDONLY);
int _fd_dup = dup2(fd,0);
close(0);
if(_fd_dup != 0){
fprintf(stderr, "Failed to redirect command input.\n");
return 0;
}
}
if(c->outfile){
int fd = open( c->outfile, O_WRONLY | O_CREAT | O_TRUNC, 0600);
int _fd_dup = dup2(fd,1);
close(1);
if(_fd_dup != 1){
fprintf(stderr, "Failed to redirect command output.\n");
return 0;
}
}
I do not get the "Failed to redirect command output." error. As I mentioned, this is a homework assignment so I'm not looking for anyone to fix this, but rather point me in the right direction.
The problem is in this bit of code:
int _fd_dup = dup2(fd,1);
close(1);
You should be closing fd, not 1. You have the same problem in the fd 0 case, too.

Resources