I have Shell awk code:
awk '{sum+=$10; array[NR,1]=$11; array[NR,2]=$10; next;} END {
for (i = 1; i <= 10; i++)
printf "%3.0f'\.' %s \b '\-' %s \b '\-' %3.1f'\%' \n", i, array[i,1], array[i,2], array[i,2]/sum*100
}'
I make program on C:
int
main ()
{
const char *sort[] = { "sort", "-t", " ", "-k", "10", "-r", "-n", "log.txt", 0 };
const char *head[] = { "head", "-n 10", 0 };
const char *awk[] = { "awk", "{sum+=$10; array[NR,1]=$11; array[NR,2]=$10; next;} END { code there }", 0 };
struct command cmd [] = { {sort}, {head}, {awk} };
return fork_pipes (3, cmd);
}
I dont know how a correct to insert in argument - awk with END { loop code ... }
My task. Add to const char *awk[] = { "awk", ... }
'{sum+=$10; array[NR,1]=$11; array[NR,2]=$10; next;} END {
for (i = 1; i <= 10; i++)
printf "%3.0f'\.' %s \b '\-' %s \b '\-' %3.1f'\%' \n", i, array[i,1], array[i,2], array[i,2]/sum*100
}'
All code C:
#include <unistd.h>
struct command
{
const char **argv;
};
int
spawn_proc (int in, int out, struct command *cmd)
{
pid_t pid;
if ((pid = fork ()) == 0)
{
if (in != 0)
{
dup2 (in, 0);
close (in);
}
if (out != 1)
{
dup2 (out, 1);
close (out);
}
return execvp (cmd->argv [0], (char * const *)cmd->argv);
}
return pid;
}
int
fork_pipes (int n, struct command *cmd)
{
int i;
pid_t pid;
int in, fd [2];
/* The first process should get its input from the original file descriptor 0. */
in = 0;
/* Note the loop bound, we spawn here all, but the last stage of the pipeline. */
for (i = 0; i < n - 1; ++i)
{
pipe (fd);
/* f [1] is the write end of the pipe, we carry `in` from the prev iteration. */
spawn_proc (in, fd [1], cmd + i);
/* No need for the write and of the pipe, the child will write here. */
close (fd [1]);
/* Keep the read end of the pipe, the next child will read from there. */
in = fd [0];
}
/* Last stage of the pipeline - set stdin be the read end of the previous pipe
and output to the original file descriptor 1. */
if (in != 0)
dup2 (in, 0);
/* Execute the last stage with the current process. */
return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);
}
int
main ()
{
const char *sort[] = { "sort", "-t", " ", "-k", "10", "-r", "-n", "log.txt", 0 };
const char *head[] = { "head", "-n 10", 0 };
const char *awk[] = { "awk", "{sum+=$10; array[NR,1]=$11; array[NR,2]=$10; next;} END { code there }", 0 };
struct command cmd [] = { {sort}, {head}, {awk} };
return fork_pipes (3, cmd);
}
Assuming you don't know how to create the awk command string in C:
You can just put your awk program inline, but you have to escape all your quotes and backslashes;
Note: you can break that string into multiple lines (at least in C++, so I assume in C too):
const char* awk = "... END { "
"printf(\"%3.0f'\\.' %s \\b '\\-' %s \\b"
" '\\-' %3.1f'\\%' \\n\" "
"}";
Related
Before I start, I just want to say that this is for a school assignment of mine. I'm really close to finishing, except well, since I'm here, obvious to say, I'm stuck on a problem :(.
First of I'll explain what my assignment wants:
The assignment wants me to create a command-line program in C that allows the user to type in N number of /bin/ commands (E.g. > /bin/ps /bin/ls "/bin/which gcc"). Once the user enters the command and hits the enter key, the parent process (the parent process is the program) will create N child processes (i.e. no. of /bin/ commands entered = no. of child processes parent process will create). Each child will run one of the N commands. All the children will be running concurrently, with the parent waiting for each child to terminate.
Once a child terminates, the parent will print whether the command executed successfully or not (E.g. "Command /bin/ps has completed successfully" or "Command /bin/ps has not completed successfully") and once all children have been terminated, the parent will print "All done, bye!"
The issue:
So I've managed to get my child processes to run concurrently, the only issue is that I'm not sure how to pipe the value of the command (like /bin/ps or /bin/which gcc) from the child process to the parent process to print out the success or not message. I've tried putting the write pipe above my execv which allows me to pipe what I want but the execv won't output anything and I can't put my pipe code below my execv because in that case, then while my execv output will show, my pipe won't. I did think that it might be due to close(1) but commenting that out didn't change the result.
So what I am trying to achieve is something like this:
> /bin/ls "/bin/which gcc" /bin/domainname /bin/fake_command
Output:
/usr/bin/gcc
localdomain
Command /bin/which gcc has completed successfully
Command /bin/domainname has completed successfully
a.txt b.c
Command /bin/ls has completed successfully
Command /bin/fake_command has not completed successfully
All done, bye!
>
But right now, I'm getting:
> /bin/ls "/bin/which gcc" /bin/domainname /bin/fake_command
Output:
Command /bin/which gcc has completed successfully
Command /bin/domainname has completed successfully
Command /bin/ls has completed successfully
Command /bin/fake_command has not completed successfully
>
As you can see, my execv output for the /bin/ commands aren't shown in the output.
I've tried searching SO for people who faced this similar issue as me but none of their solutions managed to work for me which is why I'm asking here. If there's anything you're not clear about, please let me know and I will try my best to explain.
The code:
q1.c
#include "q1.h"
int main(int argc, char *argv[])
{
int
child_status,
pipe_array[2];
pid_t child;
char *success_or_fail;
char *msg_buffer = malloc(CHAR_MAX);
if (msg_buffer == NULL)
{
return -1;
}
struct timespec tw = {.tv_sec = 0, .tv_nsec = 10000000L};
Tuple *process_tuple;
size_t tuple_size = sizeof(*process_tuple) + sizeof(pid_t) + sizeof(char *);
process_tuple = calloc(argc, tuple_size);
if (process_tuple == NULL)
{
return -1;
}
// if (pipe(pipe_array) == -1)
// {
// perror("pipe: ");
// return -1;
// }
for (int j = 1; j < argc; j++)
{
child = fork();
if (child == 0)
{
int
executed,
num_of_words,
num_of_chars;
char
string[strlen(argv[j]) + 1],
*backup = argv[j];
snprintf(string, sizeof(string), "%s", argv[j]);
num_of_chars = get_num_of_chars(string);
num_of_words = get_num_of_words(string);
char *command[num_of_chars + 1];
preparing_the_command(num_of_words, string, command);
// close(pipe_array[0]);
// close(1);
// dup2(pipe_array[1], STDOUT_FILENO);
// write(pipe_array[1], backup, sizeof(backup));
process_tuple[j - 1].pid = getpid();
process_tuple[j - 1].command = backup;
printf(" %i-PID -> %i\n %i-Command -> %s\n\n", process_tuple[j - 1].pid, process_tuple[j - 1].pid, process_tuple[j - 1].pid, process_tuple[j - 1].command);
executed = execv(command[0], command);
nanosleep(&tw, 0);
if (executed == -1)
{
exit(EXIT_FAILURE);
}
else
{
exit(EXIT_SUCCESS);
}
}
else if (child == -1)
{
perror("fork() failed: ");
exit(EXIT_FAILURE);
}
}
printf(" PID -> %i\n Command -> %s\n\n", process_tuple[0].pid, process_tuple[0].command);
// while ((child = waitpid(-1, &child_status, 0)) != -1)
// {
// for (int o = 0; o < argc; o++)
// {
// printf(" PID -> %i\n Command -> %s\n\n", process_tuple[o].pid, process_tuple[o].command);
// }
// close(0);
// close(pipe_array[1]);
//
// dup2(pipe_array[0], STDIN_FILENO);
// char *recipient;
//
// read(pipe_array[0], recipient, sizeof(recipient));
// if (!(WIFEXITED(child_status) && (WEXITSTATUS(child_status) == 0)))
// {
// success_or_fail = "not completed successfully";
// }
// else
// {
// success_or_fail = "completed successfully";
// }
// snprintf(msg_buffer, CHAR_MAX, "Command %s has %s\n", recipient, success_or_fail);
// fputs(msg_buffer, stdout);
// }
fputs("All done, bye!\n", stdout);
free(msg_buffer);
return 0;
}
int get_num_of_chars(const char string[])
{
int
i = 0,
num_of_chars = 0;
while (string[i++] != '\0')
{
if (string[i] != ' ' && string[i] != '\t')
{
num_of_chars++;
}
}
return num_of_chars;
}
int get_num_of_words(const char string[])
{
int
i = 0,
num_of_words = 0;
bool is_not_separator = false;
while (string[i++] != '\0')
{
if (string[i] == ' ' || string[i] == '\t')
{
is_not_separator = false;
}
else if (!is_not_separator)
{
is_not_separator = true;
num_of_words++;
}
}
return num_of_words;
}
void preparing_the_command(int num_of_words, char string[], char *command[])
{
char *token;
for (int j = 0; j < num_of_words && (token = strtok_r(string, " ", &string)); j++)
{
command[j] = token;
}
command[num_of_words] = (void *) NULL;
}
q1.h
#ifndef ASSIGNMENT2Q1_Q1_H
#define ASSIGNMENT2Q1_Q1_H
/***************
** LIBRARIES **
***************/
#include <stdbool.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <time.h>
#include <unistd.h>
#include <limits.h>
/*************
** STRUCTS **
*************/
typedef struct
{
pid_t pid;
char *command;
} Tuple;
/*************************
** FUNCTION PROTOTYPES **
*************************/
int get_num_of_chars(const char string[]);
int get_num_of_words(const char string[]);
void preparing_the_command(int num_of_words, char string[], char *command[]);
#endif //ASSIGNMENT2Q1_Q1_H
I'm working on a custom shell that can handle multiple pipes. But every time I execute a new pipeline and check the process with ls -l /proc/pid/fd I get something like in the picture below and the list keeps expanding with every new pipeline executed:
Question: Is this considered as a fd leak? And how do I fix it?
Here's a code snippet for my pipeline execution:
enum PIPES {READ, WRITE};
void execute_pipeline(char*** pipeline)
{
int fd[2];
int fd_backup = 0;
pid_t child_pid;
while (*pipeline != '\0')
{
pipe(fd);
child_pid = fork();
if(child_pid == -1)
{
perror("fork");
exit(1);
}
else if(child_pid == 0)
{
dup2(fd_backup, 0);// (old, new)
close(fd[READ]);
if(*(pipeline + 1) != '\0')
{
dup2(fd[WRITE], 1);
}
execvp((*pipeline)[0], *pipeline);
exit(1);
}
else// Parent process
{
wait(NULL);
close(fd[WRITE]);
fd_backup = fd[READ];
pipeline++;
}
}
}
EDIT
An example how to call execute_pipeline:
char *ls[] = {"ls", "-l", NULL};
char *sort[] = {"sort", "-r", NULL};
char *head[] = {"head", "-n", "3", NULL};
char **pipeline[] = {ls, sort, head, NULL};
execute_pipeline(pipeline);
As tadman pointed out, it is easier to use a command struct to pass things around.
We can't [well, we could but shouldn't] do a wait [in the parent] during pipe construction. That has to be a separate loop later. We would hang the parent after the first child is created.
If the first child had a large amount of output, the kernel pipe buffers might fill up and the first child would block. But, since the second child has not been created, there is nothing to read/drain the first child's output and unblock it.
Also, it is important to close the pipe units after doing dup2 and ensure the previous pipe stage units are closed in the parent.
Here's a refactored version that does all that.
As to your original issue with file descriptor leakage, I think I fixed that by adding some more close calls. The program has some self verification code on this:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <errno.h>
#include <dirent.h>
#include <sys/wait.h>
#define FREEME(ptr_) \
do { \
if (ptr_ == NULL) \
break; \
free(ptr_); \
ptr_ = NULL; \
} while (0)
#define CLOSEME(fd_) \
do { \
if (fd_ < 0) \
break; \
close(fd_); \
fd_ = -1; \
} while (0)
// command control
typedef struct {
unsigned int cmd_opt; // options
int cmd_cldno; // child number
char *cmd_buf; // command buffer
int cmd_argc; // argument count
char **cmd_argv; // arguments
int cmd_pipe[2]; // pipe units
pid_t cmd_pid; // child pid number
int cmd_status; // child status
} cmd_t;
#define CMD_FIRST (1u << 0)
#define CMD_LAST (1u << 1)
char linebuf[1000];
int cmdcount;
cmd_t *cmdlist;
int opt_d;
int opt_l;
#define dbg(fmt_...) \
do { \
if (opt_d) \
printf(fmt_); \
} while (0)
// show open fd's
void
fdshow1(int cldid)
{
char buf[100];
fprintf(stderr,"CLD: %d\n",cldid);
sprintf(buf,"ls -l /proc/%d/fd 1>&2",getpid());
system(buf);
}
// show open fd's
void
fdshow2(int cldid)
{
char dir[100];
char lnkfm[1000];
char lnkto[1000];
int len;
DIR *xf;
struct dirent *ent;
char *bp;
char obuf[1000];
sprintf(dir,"/proc/%d/fd",getpid());
xf = opendir(dir);
bp = obuf;
bp += sprintf(bp,"%d:",cldid);
while (1) {
ent = readdir(xf);
if (ent == NULL)
break;
if (strcmp(ent->d_name,".") == 0)
continue;
if (strcmp(ent->d_name,"..") == 0)
continue;
sprintf(lnkfm,"%s/%s",dir,ent->d_name);
len = readlink(lnkfm,lnkto,sizeof(lnkto));
lnkto[len] = 0;
if (strstr(lnkto,"pipe") != 0)
bp += sprintf(bp," %s-->%s",ent->d_name,lnkto);
switch (ent->d_type) {
case DT_FIFO:
break;
}
}
bp += sprintf(bp,"\n");
fputs(obuf,stderr);
fflush(stderr);
closedir(xf);
}
// show open fd's
void
fdshow(int cldid)
{
fdshow2(cldid);
}
// pipeadd -- add single command to pipe
void
pipeadd(char *buf)
{
char *cp;
char *bp;
char *sv;
cmd_t *cmd;
dbg("pipeadd: buf='%s'\n",buf);
cmdlist = realloc(cmdlist,(cmdcount + 1) * sizeof(cmd_t));
cmd = &cmdlist[cmdcount];
memset(cmd,0,sizeof(cmd_t));
cmd->cmd_pipe[0] = -1;
cmd->cmd_pipe[1] = -1;
cmd->cmd_cldno = cmdcount;
++cmdcount;
bp = buf;
while (1) {
cp = strtok_r(bp," \t",&sv);
bp = NULL;
if (cp == NULL)
break;
cmd->cmd_argv = realloc(cmd->cmd_argv,
(cmd->cmd_argc + 2) * sizeof(char **));
cmd->cmd_argv[cmd->cmd_argc + 0] = cp;
cmd->cmd_argv[cmd->cmd_argc + 1] = NULL;
cmd->cmd_argc += 1;
}
}
// pipesplit -- read in and split up command
void
pipesplit(void)
{
char *cp;
char *bp;
char *sv;
cmd_t *cmd;
printf("> ");
fflush(stdout);
fgets(linebuf,sizeof(linebuf),stdin);
cp = strchr(linebuf,'\n');
if (cp != NULL)
*cp = 0;
bp = linebuf;
while (1) {
cp = strtok_r(bp,"|",&sv);
bp = NULL;
if (cp == NULL)
break;
pipeadd(cp);
}
cmd = &cmdlist[0];
cmd->cmd_opt |= CMD_FIRST;
cmd = &cmdlist[cmdcount - 1];
cmd->cmd_opt |= CMD_LAST;
if (opt_d) {
for (cmd_t *cmd = cmdlist; cmd < &cmdlist[cmdcount]; ++cmd) {
dbg("%d:",cmd->cmd_cldno);
for (int argc = 0; argc < cmd->cmd_argc; ++argc)
dbg(" '%s'",cmd->cmd_argv[argc]);
dbg("\n");
}
}
}
// pipefork -- fork elements of pipe
void
pipefork(void)
{
cmd_t *cmd;
int fdprev = -1;
int fdpipe[2] = { -1, -1 };
for (cmd = cmdlist; cmd < &cmdlist[cmdcount]; ++cmd) {
// both parent and child should close output side of previous pipe
CLOSEME(fdpipe[1]);
// create a new pipe for the output of the current child
if (cmd->cmd_opt & CMD_LAST) {
fdpipe[0] = -1;
fdpipe[1] = -1;
}
else
pipe(fdpipe);
cmd->cmd_pid = fork();
if (cmd->cmd_pid < 0) {
printf("pipefork: fork fail -- %s\n",strerror(errno));
exit(1);
}
// parent the input side for the next pipe stage
if (cmd->cmd_pid != 0) {
CLOSEME(fdprev);
fdprev = fdpipe[0];
continue;
}
// connect up our input to previous pipe stage's output
if (fdprev >= 0) {
dup2(fdprev,0);
CLOSEME(fdprev);
}
// connect output side of our pipe to stdout
if (fdpipe[1] >= 0) {
dup2(fdpipe[1],1);
CLOSEME(fdpipe[1]);
}
// child doesn't care about reading its own output
CLOSEME(fdpipe[0]);
if (opt_l)
fdshow(cmd->cmd_cldno);
// off we go ...
execvp(cmd->cmd_argv[0],cmd->cmd_argv);
}
CLOSEME(fdpipe[0]);
CLOSEME(fdpipe[1]);
if (opt_l)
fdshow(-1);
}
// pipewait -- wait for pipe stages to complete
void
pipewait(void)
{
pid_t pid;
int status;
int donecnt = 0;
while (donecnt < cmdcount) {
pid = waitpid(0,&status,0);
if (pid < 0)
break;
for (cmd_t *cmd = cmdlist; cmd < &cmdlist[cmdcount]; ++cmd) {
if (pid == cmd->cmd_pid) {
cmd->cmd_status = status;
++donecnt;
break;
}
}
}
}
// pipeclean -- free all storage
void
pipeclean(void)
{
for (cmd_t *cmd = cmdlist; cmd < &cmdlist[cmdcount]; ++cmd)
FREEME(cmd->cmd_argv);
FREEME(cmdlist);
cmdcount = 0;
}
// main -- main program
int
main(int argc,char **argv)
{
char *cp;
--argc;
++argv;
for (; argc > 0; --argc, ++argv) {
cp = *argv;
if (*cp != '-')
break;
switch (cp[1]) {
case 'd':
opt_d = ! opt_d;
break;
case 'l':
opt_l = ! opt_l;
break;
default:
break;
}
}
while (1) {
pipesplit();
pipefork();
pipewait();
pipeclean();
}
return 0;
}
Let's be accurate regarding the file descriptors and bear in mind that during fork and execvp file descriptors are inherited by child processes unless marked as w/ CLOEXEC flag. Check the man pages:
fork(2):
The child inherits copies of the parent's set of open file descriptors. Each file descriptor in the child refers to the same open file description (see open(2)) as the corresponding file descriptor in the parent.
open(2):
By default, the new file descriptor is set to remain open across an execve(2) (i.e., the FD_CLOEXEC file descriptor flag described in fcntl(2) is initially disabled); the O_CLOEXEC flag, described below, can be used to change this default. The file offset is set to the beginning of the file (see lseek(2)).
But this behaviour, I supposed, exactly what you were relying on by calling fork after pipe...
No other words, lets' draw this:
stdin, stdout
/\
/ \
/ \
/ \
/ R; W; \
/ \
Child - - Parent
stdin/out, R(del),W stdin/out, R(fd_backup), W(del)
/\
/ \
/ \
/ \
/ R1; W1;\
/ \
Child - - Parent
stdin/out, R(fd_backup), stdin, stdout
R1(del), W1 R(fd_backup - old);
R1(fd_backup - new); W1(del)
/ \
/ \
/ \
/R2; W2;\
/ \
/ \
Child - - Parent
stdin, stdout, stdin, stdout
R(fd_backup - old), R (fd_backup - old),
R1(fd_backup - new), R1 (fd_backup - new),
R2(del),W2 R2 (fd_backup - newest!),
I hope the picture is self explanatory.
Child processes will die anyway and all their fds will be closed (so no issue with them). But the parent process is left with 3 opened fds and they keep growing with each pipe executed.
I'm working on this command shell program and I wonder why I use malloc in one place and not the others? I use malloc for the tmpvariable, why not for the other variables? Why is it that one variable needs dynamic memory and not the others?
struct command
{
const char **argv;
};
int
spawn_proc (int in, int out, struct command *cmd)
{
pid_t pid;
if ((pid = fork ()) == 0)
{
if (in != 0)
{
dup2 (in, 0);
close (in);
}
if (out != 1)
{
dup2 (out, 1);
close (out);
}
return execvp (cmd->argv [0], (char * const *)cmd->argv);
}
return pid;
}
int
fork_pipes (int n, struct command *cmd)
{
int i;
pid_t pid;
int in, fd [2];
/* The first process should get its input from the original file descriptor 0. */
in = 0;
/* Note the loop bound, we spawn here all, but the last stage of the pipeline. */
for (i = 0; i < n - 1; ++i)
{
pipe (fd);
/* f [1] is the write end of the pipe, we carry `in` from the prev iteration. */
spawn_proc (in, fd [1], cmd + i);
/* No need for the write and of the pipe, the child will write here. */
close (fd [1]);
/* Keep the read end of the pipe, the next child will read from there. */
in = fd [0];
}
/* Last stage of the pipeline - set stdin be the read end of the previous pipe
and output to the original file descriptor 1. */
if (in != 0)
dup2 (in, 0);
/* Execute the last stage with the current process. */
return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);
}
int
main (int argc, char ** argv)
{
printf("in main...");
int i;
if (argc == 1) {
const char *printenv[] = { "printenv", 0};
const char *sort[] = { "sort", 0 };
const char *less[] = { "less", 0 };
struct command cmd [] = { {printenv}, {sort}, {less} };
return fork_pipes (3, cmd);
}
if (argc > 1) {
char *tmp;
// Compute required buffer length
int len = 1; // adds 1 to the length to account for the \0 terminating char
for( i=1; i<argc; i++)
{
len += strlen(argv[i]) + 2; // +2 accounts for length of "\\|"
}
// Allocate buffer
tmp = (char*) malloc(len);
tmp[0] = '\0';
// Concatenate argument into buffer
int pos = 0;
for( i=1; i<argc; i++)
{
pos += sprintf(tmp+pos, "%s%s", (i==1?"":"|"), argv[i]);
}
printf("tmp:%s", tmp);
fflush(stdout); // force string to be printed
const char *printenv[] = { "printenv", 0};
const char *grep[] = { "grep", "-E", tmp, NULL};
const char *sort[] = { "sort", 0 };
const char *less[] = { "less", 0 };
struct command cmd [] = { {printenv}, {grep}, {sort}, {less} };
return fork_pipes (4, cmd);
free(tmp);
}
}
Since the other pointers point to constant values, the data is already put into memory by the compiler. You cannot change them or the data they point to (the data is literal and resides in read-only memory block). The tmp variable will point into a mutable part of memory so you need to allocate it as such.
Of course you could allocate the memory statically so that you wouldn't need malloc for that either, but dynamic allocations are, as the name says, dynamic so you can allocate whatever amount you need defined run time and nor compile time. Like in this case, the amount of memory is not known while compiling.
If no args to main then my program should do printenv | sort | less and I've achieved that functionality. If main has arguments then the program should do printenv | grep <parameter list> | sort | less and my problem is that debugging is not working. I can try statement printf in my code and it doesn't do anything. Why? And why is the latter part of my requirement not working? What is wrong with the program?
The expected output is printenv | grep <parameter list> | sort | less. For example I would like to query the environment variables so that executing a.out JOBS COMPIZ UPSTART should be doing the same as a printenv | grep -e 'JOBS\|COMPIZ\|UPSTART' | sort | less.
Instead I get unexpected output when trying to fork a chain of commands.
#include <sys/types.h> /* definierar bland annat typen pid_t */
#include <errno.h> /* definierar felkontrollvariabeln errno */
#include <stdio.h> /* definierar stderr, dit felmeddelanden skrivs */
#include <stdlib.h> /* definierar bland annat exit() */
#include <unistd.h> /* definierar bland annat fork() */
struct command
{
const char **argv;
};
int
spawn_proc (int in, int out, struct command *cmd)
{
pid_t pid;
if ((pid = fork ()) == 0)
{
if (in != 0)
{
dup2 (in, 0);
close (in);
}
if (out != 1)
{
dup2 (out, 1);
close (out);
}
return execvp (cmd->argv [0], (char * const *)cmd->argv);
}
return pid;
}
int
fork_pipes (int n, struct command *cmd)
{
int i;
pid_t pid;
int in, fd [2];
/* The first process should get its input from the original file descriptor 0. */
in = 0;
/* Note the loop bound, we spawn here all, but the last stage of the pipeline. */
for (i = 0; i < n - 1; ++i)
{
pipe (fd);
/* f [1] is the write end of the pipe, we carry `in` from the prev iteration. */
spawn_proc (in, fd [1], cmd + i);
/* No need for the write and of the pipe, the child will write here. */
close (fd [1]);
/* Keep the read end of the pipe, the next child will read from there. */
in = fd [0];
}
/* Last stage of the pipeline - set stdin be the read end of the previous pipe
and output to the original file descriptor 1. */
if (in != 0)
dup2 (in, 0);
/* Execute the last stage with the current process. */
return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);
}
int
main (int argc, char ** argv)
{
printf("in main...");
int i;
if (argc == 1) {
const char *printenv[] = { "printenv", 0};
const char *sort[] = { "sort", 0 };
const char *less[] = { "less", 0 };
struct command cmd [] = { {printenv}, {sort}, {less} };
return fork_pipes (3, cmd);
}
if (argc > 1) {
char *tmp = argv[1];
for( i=1; i<argc-1; i++)
{
sprintf(tmp, "%s%s%s", tmp, "|", argv[i]);
}
const char *printenv[] = { "printenv", 0};
const char *grep[] = { "grep", "-E", tmp, NULL};
const char *sort[] = { "sort", 0 };
const char *less[] = { "less", 0 };
struct command cmd [] = { {printenv}, {grep}, {sort}, {less} };
return fork_pipes (4, cmd);
}
}
Part of the problem is that you are writing to a read-only memory segment by writing to argv[1] (due to the tmp = argv[1] statement). It is further aggravated by the fact that you are more than likely writing beyond the size of argv[1]. Instead you should concatenate the string to a new writable buffer of sufficient size.
To concatenate the string into the tmp variable you can use code similar to the following:
// Compute required buffer length
int len = 1; // adds 1 to the length to account for the \0 terminating char
for( i=1; i<argc; i++)
{
len += strlen(argv[i]) + 2; // +2 accounts for length of "\\|"
}
// Allocate buffer
tmp = (char*) malloc(len);
tmp[0] = '\0';
// Concatenate argument into buffer
int pos = 0;
for( i=1; i<argc; i++)
{
pos += sprintf(tmp+pos, "%s%s", (i==1?"":"|"), argv[i]);
}
printf("tmp:%s", tmp);
fflush(stdout); // force string to be printed
...
free(tmp);
As far as why the output does not appear, it is most likely due to the fact that printf is line buffered. In other words, it typically won't be printed until an end-of-line (\n) has to be printed or a fflush explicitly forces the buffer to be printed to the console.
Note: don't forget to free() the variable tmp once you are done with it.
Although my program works correctly in all cases, it doesn't use a pipe to connect the output of the first of two commands to the second when they're separated by a pipe symbol. I wrote the output of the first command to a file, then redirected the standard input of the second command to the file when the process to run that command was run. I need to use a pipe system call to create the pipe and obtain the file descriptors
for the pipe, and then run the two processes at the same time. It is a homework question and I have done 99% of the work but somehow am not able to get the pipe system call working... what I've been trying is that for an input like: Command 1 | Command 2
inside the child process for command 2 I close FD[0] then dup FD[1] and for command 1 close FD[1] then dup FD[1] and close FD[0].
I am hell confused with the file descriptors when using pipe.... I have to use a pipe
Any sort of help is appreciated. Execute function is where I am forking the processes.
Here's my code...
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <regex.h>
/* Global Variables */
extern char **environ; /* Environmental Variables */
char *pathList[10]; /* List of paths from the $PATH */
int pathCount; /* Count of the # of paths in $PATH */
char *pathSet; /* Variable through which $PATH is retrieved */
int hasPipe = 0;
int cmdNo = 0;
/* This function takes the 'finalPath', the full path to executable,argList[],the
full command-line input arguments and argCount, the number of arguments from
command-line as input. It the creates a child process, in turn invokes the
execve() that finally executes the executable in 'finalPath' with the arguments
in 'argText' all stored into the args[] appropriately. Child process also handles
input and output file re-direction.
*/
void execute(char *finalPath, char *argList[], int argCount)
{
int k,fd,ofound,pos,i; /* flags and temporary variables */
pid_t pid; /* process ID */
int status, which;
char msg[100];
char *args[4]; /* argument list for execve() */
int spCase = 0;
ofound = 0;
pos=0;
pid = fork(); /* Creating a new process using fork() */
if (pid == -1) /* Checking for errors in process creation */
{
write(1,"Fork failed.\n",12);
exit(1);
}
/**************************
Checking for parent process
***************************/
if (pid != 0)
{
which = wait(&status);
if (which == -1)
{
write(1,"Wait failed.\n",12);
exit(1);
}
if (status & 0xff)
{ /* Case of abnormal termination */
sprintf(msg,"ERROR: <dShell> # process %d terminated abnormally for reason %d\n",which, status & 0xff);
write(1,msg,strlen(msg));
}
else
{ /* Case of normal termination */
sprintf(msg,"process %d terminated normally with status %d\n",which, (status >> 8) & 0xff);
write(1,msg,strlen(msg));
}
}
/*************************
Checking for child process
**************************/
if (pid == 0)
{
char argText[50];
argText[0] = '\0';
int std_fd;
if(cmdNo==0 && hasPipe)
{
close(1);
std_fd = open("temp.out", O_WRONLY | O_CREAT | O_TRUNC, S_IRWXU);
dup(std_fd);
}
else if(cmdNo==1 && hasPipe)
{
close(0);
std_fd = open("temp.out", O_RDONLY);
dup(std_fd);
}
/* Finding the first re-direction operator */
for( i = 0; i < argCount ; ++i)
{
if( ofound != 1 && ofound != 2)
{
if( strcmp(argList[i],"<") == 0 )
{
fd = open(argList[i+1],O_RDONLY);
if (fd < 0)
{
sprintf(msg,"ERROR: %s could not be opened\n", argList[i+1]);
write(1, msg, strlen(msg));
exit(5);
}
ofound = 1;
strcpy(argText,"\0");
close(0);
dup(fd);
close(fd);
}
else if(strcmp(argList[i],">") == 0)
{
fd = open(argList[i+1],O_CREAT | O_WRONLY, 0777);
pos = i;
ofound = 2;
strcpy(argText,"\0");
if (fd < 0)
{
sprintf(msg,"ERROR: %s could not be opened\n", argList[i+1]);
write(1, msg, strlen(msg));
exit(5);
}
close(1);
dup(fd);
close(fd);
}
}
}
/* If input re-direction operator is found check for an output re-direction along with it */
if(ofound == 1)
{
for( k = 0; k < argCount && ofound != 2; ++k)
{
if( strcmp(argList[k],">") == 0 )
{
fd = open(argList[k+1],O_CREAT | O_WRONLY , 0777);
spCase = 1;
ofound = 2;
strcpy(argText,"\0");
if (fd < 0)
{
sprintf(msg,"ERROR: %s could not be opened\n", argList[k+1]);
write(1, msg, strlen(msg));
exit(5);
}
close(1);
dup(fd);
close(fd);
}
}
}
/* If the re-direction operators are not found */
if( ofound == 0 )
{
for(i = 1; i < argCount; ++i)
{
strcat(argText, argList[i]);
strcat(argText, " ");
}
spCase = 2;
}
/* Case when both arguments and output re-direction operators are found */
if (spCase == 0)
{
if(pos == 0)
{
for( i = 3; i<argCount; ++i)
{
strcat(argText, argList[i]);
strcat(argText," ");
}
}
if(pos == argCount - 2)
{
for( i = 1; i<argCount - 2; ++i)
{
strcat(argText, argList[i]);
strcat(argText," ");
}
}
}
argText[strlen(argText)-1] = '\0'; /*because I added an extra space so trimming that*/
/* Running the execve */
args[0] = finalPath;
if(strlen(argText) == 0) /* checking if argText is populated */
{
args[1] = NULL;
}
else
{
args[1] = argText;
args[2] = NULL;
}
/* Execute command,if it returns that means it failed and need to display error and exit */
execve(args[0], args, environ);
sprintf(msg, "ERROR! execve() failed");
write(1, msg, strlen(msg));
}
}
/*******************************************************************************
This function checks if the path is accessible and continues to execute the
command. If the path does not exist of is not accessible, variable 'retFlag'
is used to return 0 to the calling function.
********************************************************************************/
int checkPath(char *exepath, char *argList[], int argCount, int flag)
{
char *finalPath;
int retFlag = flag;
if(access(exepath,X_OK) == 0)
{
finalPath = exepath;
retFlag = 1;
execute(finalPath,argList,argCount);
return retFlag;
}
else
return retFlag;
}
/**********************************************************************************
This function checks if the first argument is a path and if so calls checkPath().
Else it gets the paths set to the $PATH variable, tokenizes it, pads it with the
first token of input command and calls checkPath(). If the correct path is established,
the variable 'found' is used to kick out of the for loop.
************************************************************************************/
void setPath(char *argList[], int argCount)
{
char *exepath;
char com[50];
char emsg[80];
char *command;
int i,found = 0;
/* Seperating the command if redirection is used */
if( strcmp(argList[0],"<") == 0 || strcmp(argList[0],">") == 0 )
{
command = argList[2];
}
else
command = argList[0];
/* In case of no redirection, storing the commands and arguments into a array */
if(strcmp(command,"#") == 0) /* Checking for comment statements */
{
write(1,"ERROR: No command(s) found. Only comment present/n",48);
}
else
{
if(strstr(command,"/")) /* Checking if the entire path is given as a part of the command */
{
exepath = command;
found = checkPath(exepath,argList,argCount,0);
}
else /* building the path and storing it in 'com' */
{
for(i = 0; i< pathCount && found != 1; i++)
{
sprintf(com,"%s%s%s",pathList[i],"/",command);
exepath = com;
found = checkPath(exepath,argList,argCount,0);
}
}
if(found == 0)
{
sprintf(emsg,"%s%s",command,":COMMAND DOES NOT EXIST");
write(1,emsg,sizeof(emsg));
write(1,"\n",1);
}
}
}
/* Tokenizes commands into words */
void tokens(char *cmdStr)
{
char cmd[100];
strcpy(cmd,cmdStr);
char *result;
char delims[] = " , ";
char *argList[20];
int argCount = 0;
/*Tokenize the individual command into strings */
result = strtok(cmd,delims);
while( result != NULL )
{
argList[argCount] = result;
result = strtok( NULL, delims );
++argCount;
}
setPath(argList,argCount);
}
/* Tokenizes multiple commands into single commands */
void tokenize(char *inputStr)
{
int i,cmdCount = 0;
char *cmdResult;
char *cmdStr[100];
char delimiters[] = "|";
cmdResult = strtok(inputStr, delimiters);
while(cmdResult != NULL)
{
cmdStr[cmdCount]=cmdResult;
cmdResult = strtok(NULL, delimiters);
cmdCount++;
}
if( cmdCount > 1 )
hasPipe = 1;
else
hasPipe = 0;
for( i=0; i<cmdCount ; i++)
{
cmdNo = i%cmdCount;
tokens(cmdStr[i]);
}
}
int main(int argc, char *argv[])
{
char prompt[8]; /* String that stores the personalized prompt */
char *path; /* Temporary variable used for tokenization*/
char ch; /* Temporary variable used in read() */
int chCount; /* # of characters read from the prompt */
int entry; /* return variable of read() */
int flag; /* Flag to go read the next command when newline is found */
regex_t reIgnore;
char pattern[20]="^\\s*$|^#.*";
/* Tokenizing the paths asociated with the $PATH and storing them in a array declared globally */
pathCount = 0;
pathSet = getenv("PATH");
if ( !pathSet)
{
write(1, "ERROR: PATH environment does not exist.\n", 40);
exit(1);
}
path = strtok(pathSet,":");
while(path != NULL)
{
pathList[pathCount] = path;
path = strtok(NULL,":");
++pathCount;
}
/* Checks for blanks and tabs in Step 2 */
if ( regcomp(&reIgnore, pattern, REG_EXTENDED) )
{
write(1, "Error. \n",9);
exit(2);
}
sprintf(prompt,"<dShell> # "); /* Storing the personalized shell prompt into 'prompt' */
/* Reading the input from command line and passing it to tokenize() */
while(1)
{
char inputStr[100]; /* String into which inputs are read into */
chCount = 0;
flag = 0;
hasPipe = 1;
write(1,prompt,strlen(prompt)); /* Printing out the personalized shell prompt */
/* This will read a character 1 by 1 until it reaches the end of file */
entry = read(0,&ch,1);
if(!entry)
exit(0);
/* Reading the input and storing it in inputStr as long as newline is not encountered */
while( entry != 0 && flag == 0 )
{
/* A newline has been found so a new command will need to be executed */
/* The inputStr till this point is sent to tokenize() */
if( ch == '\n' )
{
inputStr[chCount] = '\0';
flag = 1;
if(chCount > 0) {
if(strcmp(inputStr,"exit") == 0)
exit(3);
else
tokenize(inputStr);
}
}
inputStr[chCount] = ch;
chCount++;
if(flag == 0)
entry = read( 0, &ch, 1 );
}
}
}
See the man page for pipe(2). It has this example:
#include <sys/wait.h>
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
int
main(int argc, char *argv[])
{
int pipefd[2];
pid_t cpid;
char buf;
assert(argc == 2);
if (pipe(pipefd) == -1) {
perror("pipe");
exit(EXIT_FAILURE);
}
cpid = fork();
if (cpid == -1) {
perror("fork");
exit(EXIT_FAILURE);
}
if (cpid == 0) { /* Child reads from pipe */
close(pipefd[1]); /* Close unused write end */
while (read(pipefd[0], &buf, 1) > 0)
write(STDOUT_FILENO, &buf, 1);
write(STDOUT_FILENO, "\n", 1);
close(pipefd[0]);
_exit(EXIT_SUCCESS);
} else { /* Parent writes argv[1] to pipe */
close(pipefd[0]); /* Close unused read end */
write(pipefd[1], argv[1], strlen(argv[1]));
close(pipefd[1]); /* Reader will see EOF */
wait(NULL); /* Wait for child */
exit(EXIT_SUCCESS);
}
}