Trying to implement a shell, mainly piping. I've written this test case which I expect to simply pipe ls to wc...it definitely doesn't work as expected. It prints ls to the terminal then prints memory exhausted.
I'm very lost in how to fix this and get it to work. find_path works in all of my tests.
Edit - I have to use execv for the project, its a class thing, but I've tried it with execvp just in case and it does the exact same thing. Also this is just an example, a test to see why it does not work, I call fork twice once for both commands and waitpid because I have nothing else to do.
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int find_path(char* execname, char** dst)
{
char *path = getenv("PATH");
path = strdup(path);
char *pos;
path = strtok_r(path, ":", &pos);
char *originalpath = path;
do
{
char* test = (char*)calloc(strlen(path) + strlen(execname) + 2, sizeof(char));
test = strcpy(test, path);
int testlen = strlen(test);
(*(test+testlen)) = '/';
strcpy(test + testlen + 1,execname);
struct stat buf;
int result = stat(test, &buf);
if (result == 0)
{
*dst = test;
free (originalpath);
return 1;
}
else
{
free(test);
}
} while ((path = strtok_r(NULL, ":", &pos)) != NULL);
free(originalpath);
return 0;
}
int main()
{
char *cmd1 = "ls";
char *cmd2 = "wc";
int filedes[2];
pipe(filedes);
char** argv = (char**)calloc(1, sizeof(char*));
argv[0] = (char*)malloc(sizeof(char*));
argv[0] = NULL;
pid_t pid = fork();
if (pid == 0)
{
char *path;
find_path(cmd1, &path);
dup2(filedes[1],stdout);
execv(path,argv);
}
pid = fork();
if (pid == 0)
{
dup2(filedes[0], stdin);
char *path;
find_path(cmd2, &path);
execv(path, argv);
}
else
waitpid(pid);
}
Often when it is hard to debug a program, it is best to simplify it a little to eliminate sources of error. Here is your program, simplified to remove find_path as a source of errors:
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
int main(void)
{
int filedes[2];
pipe(filedes);
/* Run LS. */
pid_t pid = fork();
if (pid == 0) {
/* Set stdout to the input side of the pipe, and run 'ls'. */
dup2(filedes[1], 1);
char *argv[] = {"ls", NULL};
execv("/bin/ls", argv);
} else {
/* Close the input side of the pipe, to prevent it staying open. */
close(filedes[1]);
}
/* Run WC. */
pid = fork();
if (pid == 0) {
dup2(filedes[0], 0);
char *argv[] = {"wc", NULL};
execv("/usr/bin/wc", argv);
}
/* Wait for WC to finish. */
waitpid(pid);
}
This should behave as you expect.
During simplification, a few errors came out:
argv[] wasn't being setup correctly, in particular, argv[0] was being set to NULL;
The program was not closing the input side of the pipe that was being given to ls. When ls finished, the pipe wasn't being closed (because the wc process still had it open), preventing wc from ever finishing.
The program was confusing the values stdout and stdin (which are of type FILE*) with the file descriptor numbers 0 and 1 (used by dup, pipe, etc.)
There is a lot you can do to improve this code (e.g. breaking this into smaller functions would be a start), but I suspect your out of memory issue is from the code in find_path(), which you could avoid entirely by using execvp which will locate the executable using the standard PATH mechanism for you. It is probably a good idea to install a signal handler using sigaction to handle SIGCHLD and invoke waitpid from the signal handler, instead of just invoking waitpid() ad-hoc like you are doing. You appear to be forking more times than you want, and you aren't checking for errors. Hope these suggestions help.
Related
I have created two processes using fork. Created a pipe. Parent will write keys at write end of pipe and child stdin(0) will be duplicated by read end of pipe. Up to know its working very well and good for alphabets.
But i want to send up and down arrow keys also, please help me.
int main()
{
int fd[2];
char enter = 10;
char *exit = "exit";
char up = 193;//what i have to use here
char down = 194;//what i have to use here
pipe(p);
if(fork())
{
write(p[1],&up,1); //not working
write(p[1],&down,1); //not working
write(p[1],exit,strlen(exit)); //working
write(p[1],&enter,1); //working
wait(NULL);
}
else
{
close(0);
dup(p[0]);
execl("/bin/sh","sh",NULL);
}
}
Please help me,
There are several points:
1.) You have to invoke a shell which supports terminal editing with arrows. On a usual Linux this may be /bin/bash instead of /bin/sh.
2.) bash is checking whether it's input is coming from a terminal device or not. Depending on this, it behaves like an interactive shell or not. It seems that you want to use it in interactive mode. However pipe is not a terminal device. To get it into interactive mode you can use bash option "-i" on its invocation.
3.) As pointed by commentaries, on a usual Linux X-terminal codes for arrow up and down are multi character strings like "\033[A" and "\033[B". It depends on the device and environment you are using, maybe your values are correct for your system.
The following code works on a usual Linux environment:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
int p[2];
char enter = 10;
char *exit = "exit";
char *up = "\033[A";
char *down = "\033[B";
pipe(p);
if(fork())
{
write(p[1],up,3);
write(p[1],down,3);
write(p[1],exit,strlen(exit));
write(p[1],&enter,1);
wait(NULL);
}
else
{
close(0);
dup(p[0]);
execl("/bin/bash","bash","-i",NULL);
}
}
Also, you shall test return values of pipe and fork. Personally I'd write it like:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
int main()
{
int p[2];
int r;
char command[] = "\033[A\033[Bexit\n";
r = pipe(p);
if (r < 0) {
perror("Can't create pipe");
return(-1);
}
r = fork();
if (r < 0) {
perror("Can't fork");
return(-1);
} else if (r > 0) {
close(p[0]);
write(p[1], command, sizeof(command)-1);
close(p[1]);
wait(NULL);
} else {
close(p[1]);
dup2(p[0], 0);
close(p[0]);
execl("/bin/bash","bash","-i",NULL);
}
}
I decided to write a simple, hard coded c program to better understand how pipes work.
The program has 3 commands:
find . -name '.' | sed 's/.*\.// | sort
And it works with 1 pipe (if i use only 2 commands) but it fails with 2 pipes(sort just does not get the information).
I think the error is in close or waitpid, but I have tried everything(i could think of) and it still does not work. What am I missing ?
information i used:
Is it possible to have pipe between two child processes created by same parent (LINUX, POSIX)
http://www.quora.com/Unix/How-can-I-write-a-code-for-PIPE-in-C-shell-script-python <--Sams example
Implementation of multiple pipes in C
EDIT:
the commands are written with no mistakes. The problem is definitely not in them (since they work if I only use 1 pipe)
My code:
#include <errno.h>
#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[]) {
pid_t test, test2, test3;
int old_pipe[2];
int new_pipe[2];
//test command
//char *argv1[] = {"ls", "-1", "-h", NULL};
char *argv1[] = {"find", ".", "-name", "*.*",NULL};
char *argv2[] = {"sed", "s/.*\\.//", NULL};
char *argv3[] = {"sort", NULL};
//creates a pipe
pipe(old_pipe);
pipe(new_pipe);
test = fork();
if(test == 0){
dup2(old_pipe[1], 1);
close(old_pipe[0]);
execvp(argv1[0], argv1);
perror("exec");
return 1;
}
test2 = fork();
if(test2 == 0){
dup2(old_pipe[0], 0);
close(old_pipe[1]);
dup2(new_pipe[1], 1);
close(new_pipe[0]);
execvp(argv2[0], argv2);
perror("exec");
return 1;
}
test3 = fork();
if(test3 == 0){
dup2(new_pipe[0], 0);
close(new_pipe[1]);
execvp(argv3[0], argv3);
perror("exec");
return 1;
}
close(old_pipe[0]);
close(old_pipe[1]);
close(new_pipe[0]);
close(new_pipe[1]);
waitpid(test);
waitpid(test2);
waitpid(test3);
return 0;
}
your 3rd exec (starting "sort") does not close old_pipe[1] and keeps it open. sed will not see the EOF and stays alive. You should call pipe(2) very late.
I suggest to look into /proc/<pid>/fd resp. use lsof to see which filedescriptors are open. E.g. after
dup2(old_pipe[1], 1);
close(old_pipe[0]);
you should close old_pipe[1] when it is not 1.
Some more explanations (as asked in comment): You program has
pipe(old_pipe);
pipe(new_pipe);
test = fork();
/* --> 'find'; writes into old_pipe[1] */
test2 = fork();
/* --> 'sed'; reads from old_pipe[0], writes into new_pipe[1] */
test3 = fork();
/* --> 'sort'; reads from new_pipe[0] */
The test2 program does not exit as long as its input (old_pipe[0]) is open. Because this pipe is alive for test3 (which waits for test2 to finish), it will deadlock.
Closing fds in child branches does not close them in the parent process.
I've been puzzling over this for a while, and now I could use some help.
I'm trying to create a loop which will fork off a child process and call "echo hello" through execve().
#include <stdio.h>
#include <stdlib.h>
#include <sys/wait.h>
int main(int argc, char *argv[],char *envp[]){
int i = 0;
while(i<10){
pid_t pid;
pid = fork();
if(pid != 0){
int status;
waitpid(-1, &status, 0);
}
if(pid == 0) {
char *arg_array[2];
arg_array[0]="echo";
arg_array[1]="hello";
char filename[] = "/bin/echo";
if (execve(filename,arg_array,envp) == (-1)) {
printf("ERROR!\n");
exit(1);
}
}
i++;
}
}
At first the code failed on its first run through, while succeeding on every subsequent run.
Now, after cleaning it up for presenting here, it won't succeed at all- all I get is ERROR! x 10. I must have broken something further, and I'm not able to tell what.
This is only my second question on this site, so if you've got any suggestions for improving my questions/ constructive criticism, please share! Thank you.
You are missing the final NULL element of the argv array. Using a perror after the execve would also give you the proper error message. Thus:
char *arg_array[3];
arg_array[0] = "echo";
arg_array[1] = "hello";
arg_array[2] = NULL;
Additionally, you are missing #include <unistd.h>
I need to implement nameless pipes using fork for my OS class but I cant get it to work. Its a simple code and have nothing special in it but I just dont get anything. Im trying to run
ls -l | wc -l but I get 0 everytime.
#include <stdio.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <fcntl.h> // for open flags
#include <time.h> // for time measurement
#include <assert.h>
#include <errno.h>
#include <string.h>
int pid,status;
int pipefd[2];
void my_exec(char* cmd, char** argv)
{
pipe(pipefd); // Fixed
pid = fork();
// pipe(pipefd); // Original
if(pid==0){
close(pipefd[0]);
dup2(pipefd[1],fileno(stdout));
close(pipefd[1]);
execvp(cmd, argv);
}
else {
close(pipefd[1]);
dup2(pipefd[0],fileno(stdin));
while(wait(&status)!=-1);
close(pipefd[0]);
return;
}
}
int main(int argc, char** argv)
{
assert(strcmp(argv[argc-1], "-"));
int i;
for (i = 1; i < argc; ++i) {
if (!strcmp(argv[i], "-")) {
argv[i] = NULL;
my_exec(argv[1], &argv[1]);
argv = &argv[i];
argc -= i;
i = 0;
}
}
char* args[argc];
args[argc-1] = NULL;
for (i = 1; i < argc; ++i) {
args[i-1] = argv[i];
}
if (execvp(args[0], args) == -1)
perror("execvp failed");
return;
}
btw the input for the command Im trying is ls -l - wc -l (instead of | type -)
OK Duck solved it: i should create the pipe before the fork, updated.
Your biggest problem is that you have the fork in front of the pipe. This will effectively have each branch of the fork call pipe() and thus you'll end up with two different pipefd sets, not the same set. Reverse the calling order of the fork() and pipe() and then you're file descriptors in each fork will be the same.
As a side note, you can do a printf() for debugging inside each of the if() statement components to make sure you're never seeing more than two descriptor numbers total.
I'm trying to create a c program that takes an executable and its arguments and runs them using execve, and then does some other stuff that shouldn't matter. The problem I'm having is that execve won't work when calling it on an exectuable assembly file. I think the problem is with my path because I can get the unix shell commands to work, but I can't get executables in the current directory (using ./spy ./executableName where spy is the name of my c program) to run. Here's the code:
#include <stdio.h>
#include <stdlib.h>
#include <sys/user.h>
#include <sys/reg.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/ptrace.h>
#include <sys/types.h>
#include <unistd.h>
#include <string.h>
extern char **environ;
int main(int argc, char* const argv[]) {
pid_t pid;
char filename[50];
char* arglist[argc];
int i = 1,count = 0;
int status;
strcpy(filename, "/bin/");
strcat(filename,argv[1]);
for(i = 1; i< argc; i++)
arglist[i-1] = argv[i];
arglist[argc-1] = 0;
arglist[0] = filename;
if (argc == 1) {
fprintf(stderr,"usage : %s <prog> ...\n",argv[0]);
return -1;
}
pid = fork();
if(pid == 0) {
ptrace(PTRACE_TRACEME, 0, NULL, NULL);
if(execve(filename, arglist, 0) < 0)
fprintf(stdout,"Invalid file.");
}
else {
while(1) {
waitpid(pid,&status,0);
if (WIFEXITED(status))
break;
ptrace(PTRACE_SINGLESTEP, pid,NULL, NULL);
count++;
}
}
return 0;
}
From the source you posted it looks as if you were always prefixing the name passed as parameter with /bin/. So if the file isn't in /bin/ it can not be found, nor run.
Just change these two lines:
strcpy(filename, "/bin/");
strcat(filename,argv[1]);
to be:
strcpy(filename,argv[1]);
Note that having applied this modification the program to be run needs to be specified with its full path.
So to run ls you need to do specfify /bin/ls as parameter to the program.
Some other comments:
So avoid buffer a overflow for long path/file names change:
char filename[50];
to be:
char filename[PATH_MAX];
To get more detailed information on why an execve() might have failed change:
if(execve(filename, arglist, 0) < 0)
fprintf(stdout,"Invalid file.");
to be:
if(execve(filename, arglist, (char*) 0) < 0)
perror("execve() failed");
To detect a possible failure of forking do change:
pid = fork();
to become:
if (-1 == (pid = fork())) then
{
perror("fork() failed");
}
else