C subprocess from Python: sub.stdin.write IOError Broken Pipe

1.4k views Asked by At

I am getting a Broken Pipe error when writing a large quantity of data very fast to a C subprocess.

So I am running a c subprocess from a python script:

process = subprocess.Popen("./gpiopwm", stdin=subprocess.PIPE)
while True:
    process.stdin.write("m2000\n")
    print "bytes written"

Sectio of main loop of gpiopwm.c:

printf("1\n");
while (1) {
    fgets(input,7,stdin);  // Takes input from python script
    printf("2\n");

    numbers = input+1;      // stores all but first char of input 
    char first = input[0];  // stores first char of input

    if (first=='m') {
        printf("3\n");
        printf("%s\n",numbers);
    }
}

However, the output from this is as follows:

1
bytes written
Traceback (most recent call last):
     File "serial-receive-to-pwm.py", line 20, in <module>
     process.stdin.write("m2000\n")
IOError: [Errno 32] Broken pipe

The C program evidently breaks at the fgets line, as 2 is never printed. What have I done wrong? How can I avoid this?

EDIT: I've updated the fgets line so that it does not include the dereference argument, but am still getting the broken pipe error.

EDIT: input is initialized as char *input="m2000";

1

There are 1 answers

5
Some programmer dude On BEST ANSWER

If you try running your C program from the console, you will see that it crashes. And if you run in a debugger, you will see that it's on this line:

fgets(*input,7,stdin);

It seems like input is a character array, and when you dereference it with *input you are passing not a pointer but a single char value. This leads to undefined behavior and the crash.

That line should have given you if not an error then a very big warning message from the compiler. Don't ignore warning messages, they are often an indicator of you doing something wrong and possibly dangerous.


A general tip: When developing a program that should be called from another program, like you do here, test the program first to make sure it works. If it doesn't work, then fix it first.

A final tip: Remember that fgets includes the newline in the destination string. You might want to check for it and remove it if it's there.


With the last edit, showing the declaration of input we know the real problem: You're trying to modify constant data, and also you want to write beyond the bounds of the data as well.

When you make input point to a literal string, you have to remember that all literal strings are read only, you can not modify a literal string. Trying to do so is undefined behavior. To make it worse, your string is only six characters long, but you try to write seven characters to it.

First change the declaration and initialization of input:

char input[16] = "m2000\n";

This will declare it as an array, located on the stack and that can be modified. Then do

while (fgets(input, sizeof(input), stdin) != NULL) { ... }

This accomplishes two things: First by using sizeof(input) as the size, you can be sure that fgets will never write out of bounds. Secondly, by using the fgets call in the loop condition the loop will end when the Python script is interrupted, and you won't loop forever failing to read anything and then work on data that you've never read.