I asked about my code and the answer was that it is incorrect. perror usage in this case Now I wonder how I can adjust and improve so that I no longer will have these errors?
execvp does not return, except if an error occurs, so if all works, the enclosing function will never return.
the value 'i' is already past the end of the array in 'cmd' due to the prior loop, so 'cmd[i].argv[0] is not correct.
cmd is not an array, of struct command, so should not be indexed
the first entry in cmd.argv is a pointer to an array where the last entry is NULL. the execvp will work on that (and only that) array so all other pointers to arrays will be ignored
There are a large number of errors in the code. for instance, the first time through the loop in fork_pipe() 'in' contains garbage. the second parameter passed to execvp() needs to be a pointer to character strings, with a final NULL pointer. That final NULL pointer is missing, There are plenty more problems
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
struct command
{
const char **argv;
};
/* Helper function that spawns processes */
int spawn_proc (int in, int out, struct command *cmd) {
pid_t pid;
if ((pid = fork ()) == 0) {
if (in != 0) {
/*if (dup2(in, 0) == -1) {
perror("dup2 failed");
exit(1);
}*/
dup2 (in, 0);
close (in);
}
if (out != 1) {
dup2 (out, 1);
close (out);
}
if (execvp(cmd->argv [0], (char * const *)cmd->argv) < 0) {
perror("execvp failed");
exit(1);
}
} else if (pid < 0) {
perror("fork failed");
exit(1);
}
return pid;
}
/* Helper function that forks pipes */
int fork_pipes (int n, struct command *cmd) {
int i;
int in, fd [2];
for (i = 0; i < n - 1; ++i) {
pipe (fd);
spawn_proc (in, fd [1], cmd + i);
close (fd [1]);
in = fd [0];
}
dup2 (in, 0);
/*return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);*/
if (execvp (cmd [i].argv [0], (char * const *)cmd [i].argv) < 0) {
perror("execvp failed");
exit(1);
} else {
return execvp (cmd [i].argv [0], (char * const *)cmd [i].argv);
}
}
int main (int argc, char ** argv) {
int i;
if (argc == 1) { /* There were no arguments */
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) { /* I'd like an argument */
if (strncmp(argv[1], "cd", 2) && strncmp(argv[1], "exit", 2)) {
char *tmp;
int len = 1;
for( i=1; i<argc; i++)
{
len += strlen(argv[i]) + 2;
}
tmp = (char*) malloc(len);
tmp[0] = '\0';
int pos = 0;
for( i=1; i<argc; i++)
{
pos += sprintf(tmp+pos, "%s%s", (i==1?"":"|"), 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);
free(tmp);
} else if (! strncmp(argv[1], "cd", 2)) { /* change directory */
printf("change directory to %s\n" , argv[2]);
chdir(argv[2]);
} else if (! strncmp(argv[1], "exit", 2)) { /* change directory */
printf("exit\n");
exit(0);
}
}
exit(0);
}
Transferring comments into (part of) an answer.
You don't need the test on
execvp()
(if it returns, it failed), but you do need the error reporting and exit call after it.The '
cmd
is not an array' comment seems to be bogus; insidefork_pipes()
, it is an array. It is not used as an array insidespawn_proc()
.I think the comment 'The first entry in
cmd.argv
is a pointer to an array where the last entry is NULL. Theexecvp
will work on that (and only that) array so all other pointers to arrays will be ignored' is bogus too. I think they overlooked that you're creating an array of struct command's.I think the comment 'the value
i
is already past the end of the array incmd
due to the prior loop, socmd[i].argv[0]
is not correct' is incorrect because the loop isfor (i = 0; i < n - 1; i++)
soi
isn-1
after the loop, and the arraycmd
has elements0..n-1
to address.However, the value of
in
in the first call tospawn_proc()
is indeed garbage. Probably you can simply set it to0
(STDIN_FILENO
) and be OK, but you need to verify that. But the comment about the second argument toexecvp()
is peculiar — the cast should be absent, but otherwise the code looks OK to me. I should add that I've not yet run a compiler over any of this, so anything I've said so far stands to be corrected by a compiler. But I'm not doing the analysis casually either… Are you compiling with your compiler set fussy:gcc -Wall -Wextra -Werror
as a minimum (I use more options!)?In
fork_pipes()
, theif
test onexecvp()
and theelse
clause are weird. You just need calls toexecvp()
,perror()
andexit()
.These comments above stand basically accurate. Here is some modified code, but the modifications are mostly cosmetic. The
err_syserr()
function is based on what I use for reporting errors. It is a varargs function that also reports the system error. It is better thanperror()
because (a) it can format more comprehensively and (b) it exits.I was getting compilation warnings like:
The easiest way to fix these is to place the
const
in the correct place instruct command
and to remove theconst
from the argument lists for the various commands.Other changes are more cosmetic than really substantive (the uninitialized
in
was the only serious bug to fix). I've use my error reporting code, and checked some extra system calls (thedup2()
ones, for example), and cleaned up theexecvp()
and error reporting. I moved the tests forexit
andcd
ahead of the general code to avoid repeating the tests. Also, you were usingstrncmp()
and the test forexit
was only looking atex
, butex
is a system command… Usestrcmp()
. I usestrcmp(x, y) == 0
in the condition; the relational operator in use mimics the relational operation I'm testing (sostrcmp(x, y) >= 0
tests forx
greater than or equal toy
, etc.).Modern POSIX does not require
#include <sys/types.h>
as an include. The other headers include it as necessary.Source:
ft13.c
Compiled to
ft13
.Example runs
Sample 1:
Sample 2: