Using pipes on Linux to get the process of a user - c

Im trying to make a code that pass the argument 1 as the name of the user whose process used you want to see, Im using pipes to get the users with getent, then I pass the result to greep argv[1] to search in that result for the user of the parameter and then pass it to ps -fu to get the process of that user but Im only getting the process of the user1 (main user) and I don't know why, thank you.
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#define error(a) {perror(a); exit(1);};
int main(int argc, char *argv[])
{
if(argc != 2)
{
error("Incorrect number of arguments");
}
int pfd1[2], pfd2[2], pid;
if(pipe(pfd1) != 0){error("First pipe");}
switch(pid = fork())
{
case -1: error("fork");
case 0:
if(close(1)==-1){error ("close");}
if(dup(pfd1[1]) != 1){error("dup");}
close(pfd1[0]); close(pfd1[1]);
execlp("getent", "getent", "passwd",NULL);
error("getent");
}
close(pfd1[1]);
if(pipe(pfd2) != 0){error("Second pipe");}
switch(pid = fork())
{
case -1: error("fork");
case 0:
close(pfd2[0]);
if(close(0)==-1){error("close");}
if(dup(pfd1[0]) != 0){error("dup");}
close(pfd1[0]);
if(close(1)==-1){error("close");}
if(dup(pfd2[1]) !=1){error("dup");}
close(pfd2[1]);
execlp("grep", "grep", argv[1], NULL);
error("grep");
}
printf("Parent: grep(%d) process launched\n", pid);
close(pfd1[0]);
close(pfd2[1]);
switch(pid = fork())
{
case -1: error("fork");
case 0:
if(close(0)==-1){error("close");}
if(dup(pfd2[0]) !=0){error("dup")};
execlp("ps", "ps", "-fu", NULL);
error("ps");
}
close(pfd2[0]);
while ((pid = wait(NULL)) != -1)
{
printf("Parent: %d process finished\n", pid);
}
return 0;
}

wow.. this is highlevel for me .. i worked with i ex:
ps aux | grep -v root | grep nginx | cut -d\ -f1 | sort | uniq
ps aux | grep -v root | grep apache | cut -d\ -f1 | sort | uniq
thanx for your inspiration ..

Enough with two pipes, one for the ps aux and another for the grep user to filter the results
#include <sys/wait.h>
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#define error(a) {perror(a); exit(1);};
int main(int argc, char *argv[])
{
if(argc != 2){error("Incorrect number of arguments")};
int pfd[2], pid;
char user[100];
sprintf(user, "%s", argv[1]);
if (pipe(pfd) == -1) error("pipe");
printf("parent: pipe created, channels: READ=%d and WRITE=%d\n", pfd[0], pfd[1]);
switch (pid = fork()) {
case -1: error("fork");
case 0: /* 1st child: who */
printf("1st child process created\n");
if (close(1) == -1) error("close");
if (dup(pfd[1]) != 1) error("dup");
close(pfd[0]); close(pfd[1]);
execlp("ps", "ps", "aux", NULL);
error("execlp");
}
printf("parent: ps(%d) process launched\n", pid);
switch (pid = fork())
{
case -1: error("fork");
case 0: /* 2nd child process: wc -l */
printf("2nd child process created\n");
if (close(0) == -1) error("close");
if (dup(pfd[0]) != 0) error("grep");
close(pfd[0]); close(pfd[1]);
execlp("grep", "grep", user, NULL);
error("execlp");
}
printf("parent: grep(%d) process launched\n", pid);
close(pfd[0]); close(pfd[1]);
while ((pid = wait(NULL)) != -1)
{
printf("parent: %d process finished\n", pid);
}
return 0;
}

Related

pipe in C stuck when calling `wc` or `grep`

Problem - when calling ls -l | grep etc, stuck on grep (grep child process does not exit)
trying to run "ls | grep r" with "execvp()" suggests that
need to close file descriptors
wait outside of the forking loop
IMO I have performed both of above but the problem still exists.
Any opinion is welcome, thanks!
Note that below is a hard-coded version for 2 pipes only
#include <sys/wait.h>
#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
int main()
{
int i = 0;
int pfd[2];
if (pipe(pfd) != 0)
{
printf("Error creating pipe\n");
exit(errno);
}
char **ptr = get_pipes(); // pipes as array of strings
char *command = *ptr;
while (command != NULL)
{
if (i == 2)
break; // hard code to ignore all commands after 2nd pipe
char **args = parse_cmd(command); // this parses a space-separated command as arguments
pid_t pid = fork();
if (pid == 0 && i == 0) // 1st pipe, 1st child
{
close(pfd[0]); // close pipe read end
dup2(pfd[1], 1); // set pipe write end to stdout
if (execvp(args[0], args) == -1)
{
fprintf(stderr, "%s: %s\n", args[0], strerror(errno));
exit(EXIT_FAILURE);
}
}
else if (pid == 0 && i == 1) // 2nd pipe, 2nd child
{
close(pfd[1]); // close pipe write end
dup2(pfd[0], 0); // set pipe read end to stdin
if (execvp(args[0], args) == -1)
{
fprintf(stderr, "'%s': %s\n", args[0], strerror(errno));
exit(EXIT_FAILURE);
}
// exit(EXIT_SUCCESS);
}
else if (pid > 0) // parent
{
printf("Parent pid: %d and child's pid is %d\n", (int)getpid(), (int)pid);
}
command = *++ptr;
i++;
}
pid_t zombie_pid;
int status;
do
{
zombie_pid = waitpid(-1, &status, 0);
printf("Child PID %d died with status %d\n", (int)zombie_pid, WEXITSTATUS(status));
} while (zombie_pid > 0);
}

Trying to replicate basic bash pipe but i get a stdin: Input/output error

I am currently working on a university project to basically built my own simple shell. Everything is working great so far. The only thing giving me trouble is pipes. To make it easier for myself to figure out why they are not working as intended I wrote this little testing program where I try to replicate the bash behaviour of cat | ls. But i now sadly get this error cat: stdin: Input/output error and i really can't figure it out.
Here is my program:
#include <unistd.h>
#include <stdlib.h>
int main(void)
{
extern char **environ;
char *argv1[] = {"cat",NULL};
char *argv2[] = {"ls",NULL};
int fd[2];
pid_t pid;
int ret;
pipe(fd);
pid = fork();
if (pid == 0)
{
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
execve("/bin/cat", argv1, environ);
exit (0);
}
else if (pid > 0)
{
close(fd[1]);
dup2(fd[1], STDIN_FILENO);
execve("/bin/ls", argv2, environ);
waitpid(pid, &ret, 0);
}
return (0);
}
You want a pipe like:
ls | cat
But, you're setting this up like:
cat | ls
And, in your current code, for the ls side, you're doing:
close(fd[1]);
dup2(fd[1], STDIN_FILENO);
This is wrong for two reasons:
You're closing the wrong side of the pipe, so the dup2 gets a closed fd as its first argument
You're attaching the output side of the pipe to the command's input side
So, we need to reverse the pipe order and fix the closing.
Also, after doing dup2(X,...) we want to do close(X).
Also, note that doing waitpid _after execve will have no effect unless the execve fails.
Here is the refactored and working code:
#include <unistd.h>
#include <stdlib.h>
#include <sys/wait.h>
int
main(void)
{
extern char **environ;
char *argv_cat[] = { "cat", NULL };
char *argv_ls[] = { "ls", NULL };
int fd[2];
pid_t pid;
int ret;
pipe(fd);
pid = fork();
// we want:
// ls | cat
if (pid == 0) {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execve("/bin/cat", argv_cat, environ);
exit(0);
}
else if (pid > 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
execve("/bin/ls", argv_ls, environ);
waitpid(pid, &ret, 0);
}
return (0);
}
UPDATE:
waitpid after execve is pointless. –
William Pursell
Not quite. It reaps the [stuck] child process so that it doesn't become a child of the init/systemd process.
In the above example, I had forgotten to add a close(STDOUT_FILENO) before the waitpid to "release" the cat process.
Here is the adjusted code:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <errno.h>
#include <sys/wait.h>
int opt_f;
int
main(int argc,char **argv)
{
extern char **environ;
char *argv_cat[] = { "cat", NULL };
char *argv_ls[] = { "ls", NULL };
int fd[2];
pid_t pid;
int status;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
char *cp = *argv;
if (*cp != '-')
break;
cp += 2;
switch (cp[-1]) {
case 'f':
opt_f = ! opt_f;
break;
}
}
pipe(fd);
pid = fork();
// we want:
// ls | cat
if (pid == 0) {
if (opt_f)
fprintf(stderr,"cld: getpid=%d\n",getpid());
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execve("/bin/cat", argv_cat, environ);
exit(0);
}
else if (pid > 0) {
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
const char *ls = opt_f ? "/bin/gooch" : "/bin/ls";
execve(ls, argv_ls, environ);
fprintf(stderr,"execve failure of '%s' -- %s\n",ls,strerror(errno));
// release other process (cat)
close(STDOUT_FILENO);
// reap the child
pid_t ret = waitpid(pid, &status, 0);
fprintf(stderr,"ret=%d pid=%d status=%8.8X\n",ret,pid,status);
}
return (0);
}

How can I redirect stdout back to the terminal in a multi process c program?

I'm trying to write a c program that is the equivalent of the linux command ps -aux | sort -r -n -k 5 but I'm not getting any output
Here's my code
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char ** argv){
int pipes[2];
int r;
r = pipe(pipes);
if (r < 0) {
fprintf(stderr, "pipe failed\n\n"); // stderr is a FILE* variable for the standard error file (terminal)
exit(2);
}
int saved_stdout = dup(1);
int pid = fork();
if(pid > 0){
// Parent
pid = fork();
if(pid > 0){
// Parent
wait(NULL);
}else if (pid == 0){
// Child 1
printf("Child 1\n");
dup2(pipes[1], 1);
close(pipes[0]);
close(pipes[1]);
execlp("/bin/ps", "ps", "-aux", (char*) NULL);
exit(0);
}else{
fprintf(stderr, "FORK FAILED\n\n");
return 1;
}
}else if (pid == 0){
// Child 2
printf("Child 2\n");
dup2(pipes[0], 0);
close(pipes[0]);
close(pipes[1]);
dup2(saved_stdout, 1);
close(saved_stdout);
execlp("/bin/sort", "sort", "-r", "-n", "-k", "5", (char*)NULL);
exit(0);
}else{
fprintf(stderr, "FORK FAILED\n\n");
return 1;
}
wait(NULL);
printf("Exiting parent\n");
}
The output I get is this
Child 1
Child 2
Exiting parent
I doesn't actually print the execlp command, I've tried saving stdout to variable saved_stdout which is the solution I found in another answer, but that doesn't seem to work.
How can I redirect stdout back to the terminal?
Strange my output with your code is:
Child 1
Child 2
and the program don't stop. Or you sure that your output is valid ?
Whatever, your problem is that you don't close your pipe in your parents. Just add:
close(pipes[0]);
close(pipes[1]);
In your both parents (before your two call to wait()).
Plus saved_stdout is useless in your case, because you only change stdout in your child1. saved_stdout and 1 describe the same file in your child2.

Program stuck on Pipe (exec ls grep sort)

I'm trying to make a program that executes the following commands connecting the output of one to the input of the next using pipes and taking two arguments DIR (directory) and ARG (filetype, example: jpg).
ls DIR -laR | grep ARG | sort
Here's the code:
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Invalid arguments. <dir> <arg>\n");
exit(1);
}
int pipe_fd1[2];
int pipe_fd2[2];
pid_t ls_pid, grep_pid;
int status;
pipe(pipe_fd1);
pipe(pipe_fd2);
ls_pid = fork();
if (ls_pid == 0) { //first child ls DIR -laR
dup2(pipe_fd1[1], STDOUT_FILENO);
close(pipe_fd1[0]);
execlp("ls", "ls", argv[1], "-laR", NULL);
} else if (ls_pid > 0) {
grep_pid = fork();
if (grep_pid == 0) { //second child grep ARG
dup2(pipe_fd1[0], STDIN_FILENO);
dup2(pipe_fd2[1], STDOUT_FILENO);
close(pipe_fd1[1]);
close(pipe_fd2[0]);
waitpid(ls_pid, &status, 0);
execlp("grep", "grep", argv[2], NULL);
} else if (grep_pid > 0) { //parent sort
dup2(pipe_fd2[0], STDIN_FILENO);
close(pipe_fd2[1]);
waitpid(grep_pid, &status, 0);
execlp("sort", "sort", NULL);
}
}
return 0;
}
It seems to be stuck? Not sure why?
You never close pipe_fd1 on the parent, so grep and sort doen't know when to stop reading input: because the pipe read and write ends are never closed on the parent, the reader blocks waiting for more input that will never arrive. You need to close it.
Also, you don't need waitpid(): the way pipes work ensures that input flows linearly and in order throughout the pipe.
Here's the working version with these issues addressed:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(int argc, char *argv[]) {
if (argc != 3) {
printf("Invalid arguments. <dir> <arg>\n");
exit(1);
}
int pipe_fd1[2];
int pipe_fd2[2];
pid_t ls_pid, grep_pid;
pipe(pipe_fd1);
ls_pid = fork();
if (ls_pid == 0) { //first child ls DIR -laR
dup2(pipe_fd1[1], STDOUT_FILENO);
close(pipe_fd1[0]);
execlp("ls", "ls", argv[1], "-laR", NULL);
} else if (ls_pid > 0) {
dup2(pipe_fd1[0], STDIN_FILENO);
close(pipe_fd1[1]);
pipe(pipe_fd2);
grep_pid = fork();
if (grep_pid == 0) { //second child grep ARG
dup2(pipe_fd2[1], STDOUT_FILENO);
close(pipe_fd2[0]);
execlp("grep", "grep", argv[2], NULL);
} else if (grep_pid > 0) { //parent sort
dup2(pipe_fd2[0], STDIN_FILENO);
close(pipe_fd2[1]);
execlp("sort", "sort", NULL);
}
}
return 0;
}

Piping between multiple child processes

I'm trying to process input through different child process from one parent. I can make it though the first 3 children, but after that, I can't seem to get any input into or anything of out sort.
Here is my code.
#include <unistd.h>
#include <stdio.h>
#include <sys/wait.h>
int main(int argc, char** argv)
{
pid_t pid;
int pipes_1[2];
int pipes_2[2];
pipe(pipes_1);
pipe(pipes_2);
switch(pid=fork())
{
case 0:
dup2(pipes_1[0], 0);//copy stdin onto pipe 1 read
dup2(pipes_2[1], 1);//copy stdout onto pipe 2 write
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("sed", "sed","s/[^a-zA-Z]/ /g", "test.txt", (char*)NULL);
break;
default:
break;
}
switch(pid = fork())
{
case 0:
dup2(pipes_2[0],0); //copy std onto pipes 2 read
dup2(pipes_1[1],1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("tr", "tr", "[A-Z]", "[a-z]", (char*)NULL);
break;
default:
break;
}
switch(pid=fork())
{
case 0:
dup2(pipes_1[0], 0);
dup2(pipes_2[1], 1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("awk", "awk", "{for(i = 1; i <= NF; i++) {print $i;}}", (char*)NULL);
break;
default:
break;
}
switch(pid=fork())
{
case 0:
dup2(pipes_2[0], 0);
//dup2(pipes_1[1], 1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("sort", "sort", (char*)NULL);
default:
break;
}
wait();
return 0;
}
I know the formatting is bad. But can you see what I'm doing wrong? I think it may have something to do with only using two pipes.
EDIT: Updated code with 4 pipes.
int main(int argc, char** argv)
{
pid_t pid;
int pipes_1[2];
int pipes_2[2];
int pipes_3[2];
int pipes_4[2];
pipe(pipes_1);
pipe(pipes_2);
pipe(pipes_3);
pipe(pipes_4);
switch(pid=fork())
{
case 0:
dup2(pipes_1[0], 0);//copy stdin onto pipe 1 read
dup2(pipes_2[1], 1);//copy stdout onto pipe 2 write
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("sed", "sed","s/[^a-zA-Z]/ /g", "test.txt", (char*)NULL);
break;
default:
break;
}
switch(pid = fork())
{
case 0:
dup2(pipes_2[0],0); //copy std onto pipes 2 read
dup2(pipes_3[1],1);
close(pipes_3[0]);
close(pipes_3[1]);
close(pipes_2[0]);
close(pipes_2[1]);
execlp("tr", "tr", "[A-Z]", "[a-z]", (char*)NULL);
break;
default:
break;
}
switch(pid=fork())
{
case 0:
dup2(pipes_3[0], 0);
dup2(pipes_4[1], 1);
close(pipes_3[0]);
close(pipes_3[1]);
close(pipes_4[0]);
close(pipes_4[1]);
execlp("awk", "awk", "{for(i = 1; i <= NF; i++) {print $i;}}", (char*)NULL);
break;
default:
break;
}*/
switch(pid=fork())
{
case 0:
dup2(pipes_4[0], 0);
//dup2(pipes_1[1], 1);
close(pipes_4[0]);
close(pipes_4[1]);
//close(pipes_2[0]);
//close(pipes_2[1]);
execlp("sort", "sort", (char*)NULL);
default:
break;
}
wait();
return 0;
}
You seem to have a pipeline:
sed … | tr … | awk … | sort
You create just two pipes, where three are needed. Create the third pipe and handle it correctly, and you'll be OK.
Adapting second code. Note that only three pipes are needed for four processes (and in general N-1 pipes are needed for N processes).
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
static inline void error(const char *msg)
{
perror(msg);
exit(EXIT_FAILURE);
}
int main(void)
{
int pid;
int pipes_1[2];
int pipes_2[2];
int pipes_3[2];
pipe(pipes_1);
pipe(pipes_2);
pipe(pipes_3);
if ((pid = fork()) == 0)
{
dup2(pipes_1[1], 1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
close(pipes_3[0]);
close(pipes_3[1]);
execlp("sed", "sed", "s/[^a-zA-Z]/ /g", "test.txt", (char *)NULL);
error("Failed to exec sed");
}
printf("sed: %d\n", pid);
if ((pid = fork()) == 0)
{
dup2(pipes_1[0], 0);
dup2(pipes_2[1], 1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
close(pipes_3[0]);
close(pipes_3[1]);
execlp("tr", "tr", "[A-Z]", "[a-z]", (char *)NULL);
error("Failed to exec tr");
}
printf("tr: %d\n", pid);
if ((pid = fork()) == 0)
{
dup2(pipes_2[0], 0);
dup2(pipes_3[1], 1);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
close(pipes_3[0]);
close(pipes_3[1]);
execlp("awk", "awk", "{for(i = 1; i <= NF; i++) {print $i;}}", (char *)NULL);
error("Failed to exec awk");
}
printf("awk: %d\n", pid);
if ((pid = fork()) == 0)
{
dup2(pipes_3[0], 0);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
close(pipes_3[0]);
close(pipes_3[1]);
execlp("sort", "sort", (char *)NULL);
error("Failed to exec sort");
}
printf("sort: %d\n", pid);
close(pipes_1[0]);
close(pipes_1[1]);
close(pipes_2[0]);
close(pipes_2[1]);
close(pipes_3[0]);
close(pipes_3[1]);
int status;
int corpse;
while ((corpse = wait(&status)) > 0)
printf("PID %d died 0x%.4X\n", corpse, status);
return 0;
}
Sample input:
Happy Go Lucky!
PENULTIMATE DESTINY#
missing all upper-case=
What gives?
Digital 023123098 Diarrhea
Sample output:
sed: 74841
tr: 74842
awk: 74843
sort: 74844
PID 74841 died 0x0000
PID 74842 died 0x0000
PID 74843 died 0x0000
all
case
destiny
diarrhea
digital
gives
go
happy
lucky
missing
penultimate
upper
what
PID 74844 died 0x0000
The process ID information is primarily diagnostic. If you pipe the output of the program to a filter, you will get different output (because of buffering, etc), but the difference is in the sequence in which the sorted data appears vs the diagnostics. Print the diagnostics to standard error, or add a fflush(stdout) after each printf() and you'll get something similar to the shown output more routinely.

Resources