Sort users' shells - c

I am trying to this in C - get the users' in /etc/passwd their shells, sort them and then use uniq -c to see how many times a shell is used.
cat /etc/passwd | cut -d ':' -f 7 | sort | uniq -c
The output should be sth like that:
94 /bin/bash
1 /bin/sync
27 /usr/sbin/nologin
What I have done:
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
int main(int argc, char* argv[]){
int a[2];
if((pipe(a))==-1){
printf("error in creating a pipe");
exit(-1);
}
pid_t p = fork();
if(p==-1){
printf("error in fork");
exit(-1);
}
if(p==0){
close(a[0]);
dup2(a[1], 1);
execlp("cat", "cat", "/etc/passwd", NULL);
}
int b[2];
if((pipe(b))==-1){
printf("error in creating the second pipe");
exit(-1);
}
pid_t k=fork();
if(k==-1){
printf("error in the 2nd fork");
exit(-1);
}
if(k==0){
close(b[0]);
dup2(a[0], 0);
dup2(b[1], 1);
execlp("cut", "cut", "-d:", "-f", "7", NULL);
}
int c[2];
if((pipe(c))==-1){
printf("error in the 3rd pipe");
exit(-1);
}
pid_t l=fork();
if(l==-1){
printf("error in 3rd fork");
exit(-1);
}
if(l==0){
close(c[0]);
dup2(b[0], 0);
dup2(c[1], 1);
execlp("sort", "sort", NULL);
}
close(c[1]);
dup2(c[0], 0);
execlp("uniq", "uniq", "-c", NULL);
exit(0);
}
and when I compile it and start it, it looks like it does not terminate, the propt just stays empty and I have to use cntrl+c to stop the program.

You have to close all these open pipes.
if(p==0){
close(a[0]);
dup2(a[1], 1);
execlp("cat", "cat", "/etc/passwd", NULL);
}
close(a[1];
...
if(k==0){
close(b[0]);
dup2(a[0], 0);
dup2(b[1], 1);
execlp("cut", "cut", "-d:", "-f", "7", NULL);
}
close(a[0]);
close(b[1]);
...
if(l==0){
close(c[0]);
dup2(b[0], 0);
dup2(c[1], 1);
execlp("sort", "sort", NULL);
}
close(b[0]);
close(c[1]);
...

the problem is that there are a bunch of pipes that aren't properly closed. This is why read() calls block.
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
char buf[1024];
int main(int argc, char* argv[]){
int a[2];
if((pipe(a))==-1){
fprintf(stderr, "error in creating a pipe");
exit(-1);
}
pid_t p = fork();
if(p==-1){
fprintf(stderr, "error in fork");
exit(-1);
}
if(p==0){
fprintf(stderr, "starting cat (%d)\nstdout = a[1], stdin = closed\n", p);
close(a[0]);
dup2(a[1], 1);
execlp("cat", "cat", "/etc/passwd", NULL);
}
close(a[1]); /* the parent process is not going to write to a[1] */
int b[2];
if((pipe(b))==-1){
fprintf(stderr, "error in creating the second pipe");
exit(-1);
}
pid_t k=fork();
if(k==-1){
fprintf(stderr, "error in the 2nd fork");
exit(-1);
}
if(k==0){
fprintf(stderr, "starting cut (%d)\nstdout = b[1], stdin = a[0]\n", k);
close(b[0]);
dup2(a[0], 0);
dup2(b[1], 1);
execlp("cut", "cut", "-d:", "-f", "7", NULL);
}
close(b[1]);
int c[2];
if((pipe(c))==-1){
fprintf(stderr, "error in the 3rd pipe");
exit(-1);
}
pid_t l=fork();
if(l==-1){
fprintf(stderr, "error in 3rd fork");
exit(-1);
}
if(l==0){
fprintf(stderr, "starting sort (%d)\nstdout = c[1], stdin = b[0]\n", l);
close(c[0]);
dup2(b[0], 0);
dup2(c[1], 1);
execlp("sort", "sort", NULL);
}
fprintf(stderr, "Exec uniq\nstdout = stdout, stdin = c[0]\n");
close(c[1]);
dup2(c[0], 0);
execlp("uniq", "uniq", "-c", NULL);
exit(0);
}

Related

How do you create n children, connect them through pipes and execute commands

I'm trying to simulate bash pipe like ls | grep ma | wc but for some reason it seems like my third child process that executes wc just hangs. I can't find the culprit, please help.
Here's my code:
#include <stddef.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define CMD_NUM 3
int main(void)
{
pid_t pids[CMD_NUM];
int *pipeFd;
int pipeFds[CMD_NUM - 1][2];
for (int i = 0; i < CMD_NUM - 1; i++)
if (pipe(pipeFds[i]) == -1)
perror("Error creating pipes");
pids[0] = fork();
if (pids[0] == 0)
{
printf("I'm in 1r if\n");
pipeFd = pipeFds[0];
close(pipeFd[0]);
close(1);
dup2(pipeFd[1], 1);
char* argv[] = {"ls", "-l", NULL};
execvp("ls", argv);
perror("Error ls execvp");
exit(1);
}
for (int i = 1; i < CMD_NUM - 1; i++)
{
printf("I'm in for\n");
pids[i] = fork();
if (pids[i] == 0)
{
close(0);
close(1);
pipeFd = pipeFds[i - 1];
close(pipeFd[1]);
dup2(pipeFd[0], 0);
pipeFd = pipeFds[i];
close(pipeFd[0]);
dup2(pipeFd[1], 1);
char* argv[] = {"grep", "ma", NULL};
execvp("grep", argv);
perror("Error in execvp");
exit(1);
}
}
pids[CMD_NUM - 1] = fork();
if (pids[CMD_NUM - 1] == 0)
{
printf("I'm in 2n if\n");
pipeFd = pipeFds[CMD_NUM - 2];
close(pipeFd[1]);
close(0);
dup2(pipeFd[0], 0);
char* argv[] = {"wc", NULL};
perror("hello");
execvp(argv[0], argv);
perror("Error en execvp");
exit(1);
}
for (int i = 0; i < CMD_NUM - 1; i++)
{
printf("I'm closing pipes\n");
pipeFd = pipeFds[i];
close(pipeFd[0]);
close(pipeFd[1]);
}
printf("I'm waiting for my last child\n");
waitpid(pids[CMD_NUM - 1], NULL, 0);
}
I'm only waiting for the last child because it's a requirement.

Using sed with the execvp to cut part of stout. C

I'm using a function that echo a string and redirect output to a sed input in c.
If i echo a string like "hello: bye bye", i need to cut everything before the ":". So i buildt a function that fork and pipe for this but sed won't recognize my regex:
void sender (char * str_ ,char * pipe_ ,char **args_) {
int fd[2];
int pid;
char* cmd1[] = {"echo", str_,NULL};
char* sed[] = {"sed","'[^:]*$'",NULL};
int status;
pid = fork();
if (pid == 0) {
if(pipe(fd) < 0){
exit(100);
}
pid = fork();
if (pid == 0) {
close(fd[0]);
dup2(fd[1], 1);
close(fd[1]);
execvp(cmd1[0], cmd1);
printf("Error in execvp1\n");
}else{
close(fd[1]);
wait(&status);
dup2(fd[0],0);
close(fd[0]);
dup2(1,2);
execvp(sed[0],sed);
printf("Error in execvp2\n");
}
}
else{
close(fd[0]);
close(fd[1]);
wait(&status);
wait(&status);
}
}
The output is error for every line read because of sed:expression -e #1, character 1: unknown command: `''
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
void pipe_exec(int pfd[], char *cmd_args[], int redirect_output)
{
printf("%s, pid %d\n", cmd_args[0], getpid());
if (redirect_output)
dup2(pfd[1], 1);
else
dup2(pfd[0], 0);
close(pfd[0]);
close(pfd[1]);
execvp(cmd_args[0], cmd_args);
printf("Error in execvp\n");
exit(1);
}
void sender(char *str_, char *unused1, char **unused2)
{
int status, pid, fd[2];
char *cmd1[] = { "echo", str_, NULL };
char *sed[] = { "sed", "s/[^:]*://", NULL };
if (pipe(fd) < 0)
exit(100);
pid = fork();
if (pid == 0)
pipe_exec(fd, cmd1, 1);
pid = fork();
if (pid == 0)
pipe_exec(fd, sed, 0);
close(fd[0]);
close(fd[1]);
wait(&status);
wait(&status);
}
int
main(void)
{
sender("hello: bye bye", NULL, NULL);
return (0);
}

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;
}

Two pipes in c program and the second one does not work as expected

I am currently making some tests with the pipes and forks in C.
I am trying to copy the behavior of this shell command in my program:
cat < test | wc
The test file contains only a little text.
Here is my program's code:
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/uio.h>
#include <sys/wait.h>
#include <stdlib.h>
// cat < test | wc
int main(int ac, char **av)
{
int p1[2];
int pid1;
printf("\nBegin of the test...\n");
if (pipe(p1) == -1)
{
perror("Pipe1");
exit(1);
}
else
{
pid1 = fork();
if (pid1 > 0)
{
int p2[2];
int pid2;
close(p1[0]);
if (pipe(p2) == -1)
{
perror("Pipe1");
exit(1);
}
else
{
pid2 = fork();
if (pid2 > 0)
{
int pid3;
close(p1[1]);
pid3 = fork();
if (pid3 > 0)
{
close(p2[1]);
wait(0);
printf("All is done !\n");
}
else if (pid3 == 0)
{
close(p2[1]);
dup2(p2[0], 0);
execve("/usr/bin/wc", av, NULL);
}
wait(0);
}
else if (pid2 == 0)
{
int fd;
char buffer[20];
int ret;
close(p2[0]);
dup2(p2[1], 1);
fd = open("test", O_RDONLY);
if (fd >= 0)
{
while ((ret = read(fd, buffer, 20)) > 0)
write(p1[1], buffer, ret);
close(fd);
close(p1[1]);
}
else
{
perror("Open file error");
exit(1);
}
close(p1[1]);
exit(1);
}
}
wait(0);
}
else if (pid1 == 0)
{
close(p1[1]);
dup2(p1[0], 0);
execve("/bin/cat", av, NULL);
}
}
printf("\nEnd of the program.\n");
return (1);
}
The output is:
Begin of the test...
All is done !
Content of the test file
0 0 0
End of the program.
Can someone explain to me why the cat output is not redirected to the wc through the pipe 2?

Thread feeding other MultiThreading

I see it's easy to open pipe between two process using fork, but how we can passing open pipe to threads.
Assume we need to pass out of PROGRAM A to PROGRAM B "may by more than one thread",
PROGRAM B send his output to PROGRAM C
EDIT:
I come again after modifying the code to become more easy for reading.
#include <stdio.h>
#include <stdlib.h>
#include <pthread.h>
#include <fcntl.h>
void *thread1(void *arg) {
int status, fd[2];
pid_t pid;
pipe(fd);
pid = fork();
if (pid == 0) {
int fd2 = *((int *) (arg));
dup2(STDIN_FILENO, fd2);
close(fd[0]);
dup2(fd[1], STDOUT_FILENO);
close(fd[1]);
execvp("PROGRAM B", NULL);
exit(1);
} else {
close(fd[1]);
dup2(fd[0], STDIN_FILENO);
close(fd[0]);
execl("PROGRAM C", NULL);
wait(&status);
return NULL;
}
}
int main(void) {
FILE *fpipe;
char *command = "PROGRAM A";
char buffer[1024];
if (!(fpipe = (FILE*) popen(command, "r"))) {
perror("Problems with pipe");
exit(1);
}
char* outfile = "out.dat";
//FILE* f = fopen (outfile, "wb");
//int fd = fileno( f );
int fd[2];
fd[0] = open(outfile, O_WRONLY);
pthread_t thid;
if (pthread_create(&thid, NULL, thread1, fd) != 0) {
perror("pthread_create() error");
exit(1);
}
int len;
while (read(fpipe, buffer, sizeof (buffer)) != 0) {
len = strlen(buffer);
write(fd[0], buffer, len);
}
pclose(fpipe);
return (0);
}
For intra-process messaging, POSIX queues will probably suit your needs better than pipes. Check out man mq_overview (or online).

Resources