#Broken pipe error (sigpipe)

260 messages · Page 1 of 1 (latest)

sterile dew
#

Need some help with a program that will exec two different commands, with the output of first going into input of second, or with 3 or more, it chains the cmds.
relevant code in paste bin :)

https://pastebin.com/dSAQmyGC

half tuskBOT
#

When your question is answered use !solved to mark the question as resolved.

Remember to ask specific questions, provide necessary details, and reduce your question to its simplest form. For tips on how to ask a good question use !howto ask.

sterile dew
#

All the important functions have pipe, exec, or smth along those lines in their name

#

would appreciate any help or advice

void python
#

may i suggest asan?

what do you suppose this c for (int i = 0; i < pm->pipeCount; i++) { int fp[PIPE_ENDS]; pipe(fp); pm->pipes[i] = fp; }

#

does exactly?

sterile dew
#

is this wrong?

#

should i use malloc?

void python
void python
sterile dew
#

tbh, id say yes?

sterile dew
#

ill send what its at currently

tender knot
void python
# sterile dew https://pastebin.com/UkxmMX6J

Well that code is incorrect (logically) do you really want only 2 pipes?

And regardless of your answer - it doesn't seem you call pipe on them at all (i.e. you never initialize them)

#

you could use

int (*pipes)[PIPES_ENDS) = malloc(sizeof *pipes * pipes_amount);```

where `PIPES_ENDS` is `2`.
Otherwise you can manually `*alloc` each and every pipe
void python
sterile dew
#

ah okay

sterile dew
#

@void python what actually is the best way for me to do what im trying to do

sterile dew
#

i only ever need at most 2 pipes

#

one to get the read end of the previous commands, and one to write my current command output to

#

and thats for when i have 3 commands

#

if i have 2 commands, only 1 pipe is need (number of commands - 1)

#

so if i just instantiate pipes as i need id be all fine and dandy

#

and i only need to make 2

void python
#

the robust solution would be either the first one i suggested or the last. you can choose between the 2

sterile dew
#

what was the first thing you suggested sorry?

#

the second was the malloc an array for size of 2 * number of pipes?

#

ohh right, in the link

#

oops ahaha

void python
#

if you go ahead and look at the link posted you'll find a similar program with a similar solution(s) [one i wrote sometime ago to help someone on some other server). that program isn't perfect - far from it - but you can use it as a reference should you want to.

sterile dew
#

so actually, that was what i tried

#

ill see if i can get it back, one second

#
PipeManager* pipe_manager(Params params) {
    PipeManager* pm = malloc(sizeof(PipeManager));
    pm->pipeCount = params.cm->numCmds - 1;
    //pm->stdoutfp = dup(STDOUT_FILENO);
    pm->pipes = malloc(sizeof(int[PIPE_ENDS]) * 2);
    for (int i = 0; i < pm->pipeCount; i++) {
        pipe(pm->pipes[i]);
    }
    return pm;
}
#

smth like this?

void python
sterile dew
#
typedef struct {
    int** pipes;
    int pipeCount;
    int stdoutfp;
} PipeManager;
void python
# sterile dew ```c PipeManager* pipe_manager(Params params) { PipeManager* pm = malloc(siz...

that won't work. this is pmuch the same as the last solution i suggested. if you're to use it - take note of the way i iterate through the array there.

to give some intuition:

    // will create a large continuous block (array) in memory. 
    pm->pipes = malloc(sizeof(int[PIPE_ENDS]) * 2); 
    for (int i = 0; i < pm->pipeCount; i++) { // will iterate over _each_ int
        pipe(pm->pipes[i]);  // pipes expects a block (or array) of the size of 2 ints
    }```
meaning if you call `pipe` on `pipes[0]` - it'll init both `pipes[0]` _and_ `pipes[1]`. calling `pipe` on `pipes[1]` again - is a mistake
sterile dew
void python
#

the first

sterile dew
#

so, the struct should be like this:

typedef struct {
    int (*pipes)[PIPE_ENDS];
    int pipeCount;
    int stdoutfp;
} PipeManager;
void python
#

not quite. int (*pipes)[PIPE_ENDS]. i.e. pipes is a pointer to an array for size 2

#

if you haven't used pointers to array before - consider picking one of the other 2 options

sterile dew
#

i have used, but only for chars*

#

like, char** pointers

#

in a previous thing i was keeping a dictionary of words and adding/removing from it as needed

void python
#

those aren't the same. meaning you haven't. please consider another solution

sterile dew
#

i mean, i think i understand how they work

#

when u initialise ur pointer to an array

#

the pointer points to the very first object

void python
#

no

sterile dew
#

oh, im rly misunderstanding what happens then

void python
#

when one init a ptr to an array - the ptr points to an array

sterile dew
#

id like to learn/practise if its important

#

especially if its a common practise (which i assume it is)

void python
#

it points to the array itself. not the first object of said array

sterile dew
#

ohhhh, when we dereference

#

we get the whole array

void python
# sterile dew id like to learn/practise if its important

consider this then:

void foo(int **mat, size_t rows, size_t cols);

void bar(int mat[ROWS][COLS], size_t rows, size_t cols);

void baz(int (*mat)[COLS], size_t rows, size_t cols);

void func(size_t rows, size_t cols, int mat[rows][cols]);

int main(void) {
  int mat[1][1] = {0};
  foo(mat, 1, 1); // incorrect
  bar(mat, 1, 1); // correct but is a 'lie'
  baz(mat, 1, 1); // correct and true
  func(1, 1, mat); // correct but VLA
}```
sterile dew
#

VLA?

void python
#

variable length array (which in the above example is one of the 'good' uses of it)

sterile dew
#

and what do you mean by lie?

void python
#

int mat[ROWS][COLS] as a function parameter hides the true type of mat. since mat is a ptr to a ptr when one pass it to a function, a pointer decay happens turning int[1][1] into int (*)[1] (i.e. a pointer to an array for size one

sterile dew
#

ohh i see

#

but why doesnt this happen on the final occurence?

#

like, func

void python
#

because of it being a VLA. meaning its type is that VLA and ofc we cannot talk about VLAs without https://stackoverflow.com/a/54163435 (which i really recommend reading when you got the time)

sterile dew
#

also, to get the 1,1 element, id use *((mat + 1) + 1)

void python
sterile dew
#

that was silly of me actually

#

so my struct:

typedef struct {
    int (*pipes)[PIPE_ENDS];
    int pipeCount;
    int stdoutfp;
} PipeManager;
#

then if ive got a pointer to my struct i can do:

pm->pipes = malloc((sizeof(*pipes) * pipeCount);
sterile dew
#

OOPS

#

thats what i was going to do first, but i didnt know if it would typematch

void python
#

one can always try 🙂

void python
#

still no

sterile dew
#

wait what?

void python
#

the parenthasis change the meaning of the expression in this case

sterile dew
#

oh really??

#

dont you need parentheses for sizeof?

#

oh wait, this

#

i bracketed wrong i think

#
pm->pipes = malloc((sizeof(*pipes) * pipeCount);
#

i want pipecount number of *pipes sized memory

void python
#

yep
sizeof(*pipes * pipeCount) is just UB
sizeof *pipes * ... means take the size of whatever is the type of *pipes (i.e. an array of size of 2) and multiply it by some amount

sterile dew
#

i just forgot to change the bracketing from the previous thing i typed ahha

#

so now

#

do i want to open all my pipes at once?

void python
void python
#

well now you do

sterile dew
#

thank you!

sterile dew
void python
void python
sterile dew
#

okay so i wont worry about closing them then

void python
sterile dew
#

tbh i thought it was a potential reason, i didnt think this was the case till my program started hanging

#

but that was probably because of other reasons

void python
sterile dew
#
pm->pipes = malloc(sizeof(*pipes) * pm->pipeCount);
#

am i writing this wrong?

void python
#

yes. count your parenthesis

sterile dew
#

malloc doesnt need parentheses?

void python
#

it most certainly does

sterile dew
#
uqfindexec.c:249:33: error: ‘pipes’ undeclared (first use in this function); did you mean ‘pipe’?
     pm->pipes = malloc(sizeof (*pipes) * pm->pipeCount);
                                 ^~~~~
                                 pipe
uqfindexec.c:249:33: note: each undeclared identifier is reported only once for each function it appears in
void python
#

how many ( do you have as oppose to ) in that expression?

sterile dew
#

am i blind?

void python
sterile dew
#

oh right ahha

#

that was just a silly mistype ahhha

sterile dew
void python
#

all good. as for the error is there an identifier (a var) named pipes anywhere?

sterile dew
#

oh i see what i need to do

#
pm->pipes = malloc (sizeof *pm->pipes * pm->pipeCount);
#

that compiles

#

i realise it doesnt know what pipes comes from

void python
sterile dew
#

then, would opening all my pipes now be logical?

void python
#

again - up to you. but yes - you can init them all

sterile dew
#
PipeManager* pipe_manager(Params params) {
    PipeManager* pm = malloc(sizeof(PipeManager));
    pm->pipeCount = params.cm->numCmds - 1;
    //pm->stdoutfp = dup(STDOUT_FILENO);
    pm->pipes = malloc(sizeof *pm->pipes * pm->pipeCount);
    for (int i = 0; i < pm->pipeCount; i++) {
        pipe(pm->pipes[i]);
    }
    return pm;
}
#

i believe this would work since it indexes each int[2]

void python
#

yep. looks like you got the idea

sterile dew
#

great!!

#

now i guess i can close and open as i need

#

also, each fork is completely memory independent right?

#

unless i have shared memory

#

i think

void python
#

yes to both (beside shared memory blocks which aren't present in your case)

sterile dew
#

great~

#

ill try writing up the file openers now, and could you check?

void python
#

depends on the hour - but i promise to check in every once in a while

sterile dew
#

okay ty!

void python
#

np

sterile dew
#
void cmd_pipeliner(Params params, int cmd) {
    if (params.cm->numCmds == 1) {
        return;
    }
    if (cmd == 0) {
        close(params.pm->pipes[cmd][READ_END]);
        dup2(params.pm->pipes[cmd][WRITE_END], STDOUT_FILENO);
        close(params.pm->pipes[cmd][WRITE_END]);
    }
    else if ((cmd + 1) == params.cm->numCmds) {
        close(params.pm->pipes[cmd - 1][WRITE_END]);
        dup2(params.pm->pipes[cmd - 1][READ_END], STDIN_FILENO);
        close(params.pm->pipes[cmd - 1][READ_END]);
    } else {
        dup2(params.pm->pipes[cmd - 1][READ_END], STDIN_FILENO);
        close(params.pm->pipes[cmd - 1][READ_END]);
        dup2(params.pm->pipes[cmd][WRITE_END], STDOUT_FILENO);
        close(params.pm->pipes[cmd][WRITE_END]);
    }
    for (int i = 0; i < params.pm->pipeCount; i++) {
        //close(params.pm->pipes[i][READ_END]);
        //close(params.pm->pipes[i][WRITE_END]);
    }
}
#

okay i THINK, this is right?

#

if i have an array of cmds, and im passing in the index of the cmd

#

e.g. {echo, tr A-Z a-z, wc} or smth

#

id have 2 pipes

#

fork0 redirects its STDOUT to writing end of pipe0, fork1 redirects reading end of pipe1 to STDIN, and its STDOUT to write end of pipe1, then fork2 will redirect pipe1 read end to STDIN

#

and voila magic?

void python
#

i'm a bit confused by this block if i'm honest

    else if ((cmd + 1) == params.cm->numCmds) { // why +1
        close(params.pm->pipes[cmd - 1][WRITE_END]);  // why -1
        dup2(params.pm->pipes[cmd - 1][READ_END], STDIN_FILENO); // why -1
        close(params.pm->pipes[cmd - 1][READ_END]); // why -1
    } else {
     ```
sterile dew
#
Params prepare_command(Params params)
{
    DirectoryManager dm = directory_manager_initialiser(params);
    params.stats = stats_initialiser();
    params.pm = pipe_manager(params);
    int childrenCount = 1;
    pid_t* children = malloc(sizeof(pid_t) * childrenCount);
    for (int i = 0; i < dm.entryCount; i++) {
        int wstatus;
        AbsPath path
                = sbuf_initialiser(params.directory, dm.entries[i]->d_name);
        for (int j = 0; j < params.cm->numCmds; j++) {
        pid_t child;
            if (!S_ISDIR(path.sbuf.st_mode) && !(child = fork())) {
                params = every_file_handler(params, path);
                execute_commands(params, path.absolutePath, path.path, j);
            }
            if ((j + 1) < params.cm->numCmds) {
                close(params.pm->pipes[j][WRITE_END]);
            }
            children[childrenCount - 1] = child;
            childrenCount++;
            wait(NULL);
            waitpid(child, &wstatus, 0);
            update_statistics(params.stats, wstatus);
        }
    }
    free_directory(dm);
    return params;
}
sterile dew
#

then u want to redirect its input from the pipe, but you dont want to redirect its output

void python
sterile dew
#

so for [echo, wc]

#

pipes = [pipe0]

#

index of wc = 1, so we want pipes[0]

#

i think thats okay?

void python
#

aight. fair enough. that logic seem to be correct

sterile dew
#

so, i tried running just then and my program was hanging

#

so im not sure where

#

this is what happens in terminal

void python
#

did your parent close all pipes it didn't need?

sterile dew
#

uhh, i think so

#

1 second

#

ill send what my parent looks like

#
Params prepare_command(Params params)
{
    DirectoryManager dm = directory_manager_initialiser(params);
    params.stats = stats_initialiser();
    params.pm = pipe_manager(params);
    int childrenCount = 1;
    pid_t* children = malloc(sizeof(pid_t) * childrenCount);
    for (int i = 0; i < dm.entryCount; i++) {
        int wstatus;
        AbsPath path
                = sbuf_initialiser(params.directory, dm.entries[i]->d_name);
        for (int j = 0; j < params.cm->numCmds; j++) {
        pid_t child;
            if (!S_ISDIR(path.sbuf.st_mode) && !(child = fork())) {
                params = every_file_handler(params, path);
                execute_commands(params, path.absolutePath, path.path, j);
            }
            if ((j + 1) < params.cm->numCmds) {
                close(params.pm->pipes[j][WRITE_END]);
            }
            children[childrenCount - 1] = child;
            childrenCount++;
            waitpid(child, &wstatus, 0);
            update_statistics(params.stats, wstatus);
        }
    }
    free_directory(dm);
    return params;
}
#
s4773517@moss:~/repo/trunk/a3$ ./uqfindexec "echo {} | wc"
      1       1       9
uqfindexec
      0       0       0
uqfindexec.c
      0       0       0
uqfindexec.o
Segmentation fault
#

this is my current output

#

oh no, i fixed it

#

i forgot to change my close condition

void python
sterile dew
#

so actually, yea this is one of the things we need to implement, parallel vs linear running of the cmds

#

the wait(NULL) was smth i forgot to delete from my testing btw

void python
sterile dew
#

so, if i keep my array of pids

#

i can wait for all the pids if i put it outside of the j loop right?

#

actually, id have a separate loop to reap the children

void python
#

since i'm not sure about the purpose of your outer loop - it may or may not be correct 🙂

sterile dew
#

although i realise im actually missing some input

#
s4773517@moss:~/repo/trunk/a3$ ls
Makefile  testfiles  uqfindexec  uqfindexec.c  uqfindexec.o
#

but

#
s4773517@moss:~/repo/trunk/a3$ ./uqfindexec "echo {} | wc"
      1       1       9
uqfindexec
      0       0       0
uqfindexec.c
      0       0       0
uqfindexec.o
#

and this shouldnt even be printing the program names

#
s4773517@moss:~/repo/trunk/a3$ demo-uqfindexec "echo {} | wc"
      1       1       9
      1       1      11
      1       1      13
      1       1      13
#

this is the expected output

#

do i need to flush my buffer?

#

fflush(stdout)?

sterile dew
void python
#

probably, though i'm not sure what echo {} suppose to do other than printing {} to stdout

sterile dew
#

so echo {} just runs echo on all the files in the directory

#

ive just made it so my program will run a cmd on every file in a directory if {} is supplied

#

it just replaces {} with the file name

#
s4773517@moss:~/repo/trunk/a3$ ./uqfindexec "ls | wc"
      5       5      56
Makefile  testfiles  uqfindexec  uqfindexec.c  uqfindexec.o
      0       0       0
Makefile  testfiles  uqfindexec  uqfindexec.c  uqfindexec.o
      0       0       0
Makefile  testfiles  uqfindexec  uqfindexec.c  uqfindexec.o
      0       0       0
#
s4773517@moss:~/repo/trunk/a3$ demo-uqfindexec "ls | wc"
      5       5      56
      5       5      56
      5       5      56
      5       5      56
#

my output vs what should happen

sterile dew
sterile dew
#

just in my program

#

its a special input u can give it

#

the {}

void python
#

ah. i see. check you implementation of it first then i suppose

sterile dew
#

so my {} is definitely implemented correctly (without piping i mean)

#

like im super sure of it

void python
#

couldn't hurt to check

sterile dew
#

the fact that the file names are even appearing in the terminal suggests that im doing something weird

#

(to me at least)

#

like, no stdout from the first command should reach terminal right?

#

if ive piped properly?

void python
#

oh - i see your ls has the same problem. yeah that might be a piping issue

void python
#

well if ls is picking up things from stdin which shouldn't be there - it means someone put them there

sterile dew
void python
# void python did your parent close all pipes it didn't need?

on that note - i forgot. if you create all your pipes before launching your processes - it means that each and every process have those mapping of said pipes. you'd need to close them in each and every child (after you configured them correctly ofc)

sterile dew
#

do i close the reading and writing end of all pipes?

#

or do i leave the reading end open for the next fork?

void python
#

i really do suggest you use that repo i linked as a reference

sterile dew
#

ah okay

void python
#

start in main and skim through it

sterile dew
#

how long did it take u to make this shell

#

i might try this in the holidays

void python
#

can't remember it was a long time ago. couple days maybe? it's not a well written one

sterile dew
#

how long after u started programming in c did u try it?

void python
#

well it was uploaded 8 months ago so 6 years ish

#

closer to 5

#

seems like it took one friday. again - its not a well written one. its good enough for a reference though

sterile dew
#

okay ill keep readng through

half tuskBOT
#

@sterile dew Has your question been resolved? If so, type !solved :)

sterile dew
#

okay so i seem to have fixed one of the issues

#
s4773517@moss:~/repo/trunk/a3$ ./uqfindexec "echo {} | wc"
      1       1       9
uqfindexec
      0       0       0
uqfindexec.c
      0       0       0
uqfindexec.o
      0       0       0
#

im actually getting my final output

sterile dew
#

@void python okay so i know what the issue is, my dup2 is actually failing

#

after the first command

#

im not sure why/how to fix, but i have identified dup2 fails

#

im pretty dumb for not recognising that wc is returning 0 for all the files

#

other than first

#

okay so im getting a bad file descriptor

#

when i print, both of the forks have the same file descriptor

#

OH OKAY

#

i needed to reinitialise my pipes for every file i go over

#

hell yes

#

its half working!

void python
#

glad you managed to figure it out.
minor comment though - when an API (or functions) returns something to indicate an error - one should check said value.

if one 1000% (yes one thousand percent) sure the value will never indicate an error one should cast the return value to void. by doing it one indicates to all readers of said code that:

  • they are aware said function returns something
  • yet they choose to ignore it willingly
    in case of misbehaving programs - those casts would be pretty high on the 'things to check' list
sterile dew
#

i was pretty confident it shouldnt ever error (assuming i didnt write bad code lmao)

#

but iill do the void thing