1

EDIT: I wrote a C program that generates invertible functions and writes them into R files that are executed and opened with popen. I am running this program on Ubuntu 16.04. My program compiles, but the R script never finishes/hangs

#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <math.h>
#include <stdlib.h>
#define num_func 10

char * makeInvert(){
    char * buffer = malloc(200 * sizeof(char));
    double a = (double)(rand()%5)-5.0; 
    double b = (double)(rand()%5)-5.0; 
    double c = (double)(rand()%5)-5.0; 
    double n = (double)(rand()%5)-5.0;
    n *= 2;
    n += 1;
    sprintf(buffer, "%g(x + %g)^%g + %g == y", a, b, n, c);
    return buffer;

}

int main() {
  printf("entered main \n");
  int i;
  char * buff1;

  for (i = 0; i < num_func; i++) {
        char R[5000];
    buff1 = makeInvert();

    strcpy(R, "library(Ryacas)\n");
    strcat(R, "yacas(\"Solve(");
    strcat(R, buff1);
    strcat(R, ", x)\")\n\n");
    //sleep(1);
    char filename[100];
    strcpy(filename, "computation/");
    strcat(filename, "inverse");
    strcat(filename, ".R");
    FILE * f = fopen(filename, "w");    
    if (!f) {       //validate file is open 
            printf("cant open file");
     }
    fputs(R, f);
    printf("before fclose");
    fflush(stdout);
    fclose(f);
        f = NULL;
    printf("after fclose");
    fflush(stdout);

    char path[5000];
    char command[300];
    strcpy(command,"Rscript ");
    strcat(command, "computation/inverse");

    strcat(command, ".R");
    printf("command %s\n", command);
    FILE * fp = popen(command, "r");

    if (!fp) {       //validate file is open 
            printf("cant open file");
            fflush(stdout);
     }

    printf("after popen\n");
    fflush(stdout);

    while (fgets(path, sizeof(path)-1, fp) != NULL) {
           printf("output: %s\n", path);

        }
    fclose(fp);

  }
  return 0;
}

Output from running strace -f program_name

execve("/usr/bin/test", ["test"], [/* 20 vars */]) = 0
brk(NULL)                               = 0x16ef000
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f95f15a9000
access("/etc/ld.so.preload", R_OK)      = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=53425, ...}) = 0
mmap(NULL, 53425, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f95f159b000
close(3)                                = 0
access("/etc/ld.so.nohwcap", F_OK)      = -1 ENOENT (No such file or directory)
open("/lib/x86_64-linux-gnu/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0P\t\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1868984, ...}) = 0
mmap(NULL, 3971488, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f95f0fbc000
mprotect(0x7f95f117c000, 2097152, PROT_NONE) = 0
mmap(0x7f95f137c000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c0000) = 0x7f95f137c000
mmap(0x7f95f1382000, 14752, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f95f1382000
close(3)                                = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f95f159a000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f95f1599000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f95f1598000
arch_prctl(ARCH_SET_FS, 0x7f95f1599700) = 0
mprotect(0x7f95f137c000, 16384, PROT_READ) = 0
mprotect(0x60a000, 4096, PROT_READ)     = 0
mprotect(0x7f95f15ab000, 4096, PROT_READ) = 0
munmap(0x7f95f159b000, 53425)           = 0
brk(NULL)                               = 0x16ef000
brk(0x1710000)                          = 0x1710000
open("/usr/lib/locale/locale-archive", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=1668976, ...}) = 0
mmap(NULL, 1668976, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f95f1400000
close(3)                                = 0
close(1)                                = 0
close(2)                                = 0
exit_group(1)                           = ?
+++ exited with 1 +++

When I run Rscript computation/inverse.R directly, it hangs and doesn't output anything.


I believe it is hanging on the fgets because when I run the program on gdb for the backtrace, I see this:

after popen 
^C
Program received signal SIGINT, Interrupt.
0x00007ffff7b04230 in __read_nocancel ()
    at ../sysdeps/unix/syscall-template.S:84
84  ../sysdeps/unix/syscall-template.S: No such file or directory.
(gdb) backtrace
#0  0x00007ffff7b04230 in __read_nocancel ()
    at ../sysdeps/unix/syscall-template.S:84
#1  0x00007ffff7a875e8 in _IO_new_file_underflow (fp=0x6034f0) at fileops.c:592
#2  0x00007ffff7a8860e in __GI__IO_default_uflow (fp=0x6034f0) at genops.c:413
#3  0x00007ffff7a7bc6a in __GI__IO_getline_info (fp=fp@entry=0x6034f0, 
    buf=buf@entry=0x7fffffffd1d0 "", n=4998, delim=delim@entry=10, 
    extract_delim=extract_delim@entry=1, eof=eof@entry=0x0) at iogetline.c:60
#4  0x00007ffff7a7bd78 in __GI__IO_getline (fp=fp@entry=0x6034f0, 
    buf=buf@entry=0x7fffffffd1d0 "", n=<optimized out>, delim=delim@entry=10, 
    extract_delim=extract_delim@entry=1) at iogetline.c:34
#5  0x00007ffff7a7ab7d in _IO_fgets (buf=0x7fffffffd1d0 "", n=<optimized out>, 
    fp=0x6034f0) at iofgets.c:53
#6  0x0000000000401288 in main () at test.c:123
(gdb) frame 6
#6  0x0000000000401288 in main () at test.c:123
123     while (fgets(path, sizeof(path)-1, fp) != NULL) {

After reading this post, I added this code after I popen

    int fd = fileno(fp);

    int flags;
    flags = fcntl(fd, F_GETFL, 0);
    flags |= O_NONBLOCK;
    fcntl(fd, F_SETFL, flags);

The post explains that, "In Linux (or any Unix-y OS), you can mark the underlying file descriptor used by popen() to be non-blocking. [Using the above code] if there is no input available, fgets will return NULL with errno set to EWOULDBLOCK." When I run gdb now, I get:

after popen 
^C
Program received signal SIGINT, Interrupt.
0x00007ffff785f188 in _IO_new_proc_close (fp=0x6034f0) at iopopen.c:339
339 iopopen.c: No such file or directory.
(gdb) bt
#0  0x00007ffff785f188 in _IO_new_proc_close (fp=0x6034f0) at iopopen.c:339
#1  0x00007ffff7869960 in _IO_new_file_close_it (fp=fp@entry=0x6034f0) at fileops.c:172
#2  0x00007ffff785d3ef in _IO_new_fclose (fp=0x6034f0) at iofclose.c:58
#3  0x00000000004013f9 in main () at test.c:130
(gdb) frame 3
#3  0x00000000004013f9 in main () at test.c:130
130     fclose(fp);
(gdb) 
maddie
  • 1,854
  • 4
  • 30
  • 66
  • Learn about [poll(2)](http://man7.org/linux/man-pages/man2/poll.2.html) and take some time to read [*Advanced Linux Programming*](http://advancedlinuxprogramming.com/) – Basile Starynkevitch Jul 26 '17 at 16:53
  • if `popen` doesn't work, why are you continuing on and trying to read from `fp`? You should stop when you print out the error message. – Chris Turner Jul 26 '17 at 16:56
  • @ChrisTurner if the error message you are referring to is, "can't open file", it never prints that out – maddie Jul 26 '17 at 17:01
  • `strcat(R, "library(Ryacas)\n");` --> `strcpy(R, "library(Ryacas)\n");` – BLUEPIXY Jul 26 '17 at 17:08
  • @BLUEPIXY same problem even with that edit – maddie Jul 26 '17 at 17:13
  • Aside from the fact that your question clearly has the misleading "popen doesn't work" in the title, you still should fix your code so that if you do get that error message (or any others), it should break out of the loop or at least continue on to the next iteration – Chris Turner Jul 27 '17 at 08:59

3 Answers3

0

I didn't went through the entire code, but I spotted this flaw:

char R[5000];
buff1 = makeInvert();
strcat(R, "library(Ryacas)\n");
strcat(R, "yacas(\"Solve(");

You strcat-ing "library..." onto an uninitialized string. You should use strcpy for the first string as you did it elsewhere or insert R[0]='\0';.

user5329483
  • 1,260
  • 7
  • 11
0

If you use non-blocking I/O, fgets() will exit as soon as no output is available. That does not seem to be what you want. You want to read the whole output then exit. I would recommend removing the fcntl() part.

The issue seems to be that your R script is not finishing. I would recommend running your program through strace -f and post the output. Also, have you tried to run "Rscript computation/inverse.R" directly and make sure it does return the output you're expecting and is exiting (if not fgets will get stuck)? When your process gets blocked, did you see if the Rscript is still running (I expect it does and that's why your process is stuck)

You're still have some unrelated issues: you're closing the popen() fd with fclose(), instead of pclose(). If fopen(filename, "w") fails, you do not exit so it'll crash.

Guillaume
  • 2,044
  • 12
  • 11
  • you are completely right - the Rscript is still running. Do you think if I write the Rscript in a non-main function (And can therefore return null if fopen fails), the Rscript will finish? – maddie Jul 26 '17 at 18:08
  • If I run your script manually, the R process takes 100% cpu. So it's either taking a very long time or process of Ryacas is buggy and goes into an infinite loop. I am not quite sure what the expected runtime of the script should be – Guillaume Jul 26 '17 at 18:37
0

the current posted code seems to have been edited since the comments and prior answer were posted.

To avoid confusion, ADD edits, do not overlay the original question.

when calling any of the heap allocation functions (malloc, calloc, realloc)

  • the expression: sizeof(char) is defined as 1 and multiplying anything by 1 has no effect. Suggest removing that expression.
  • always check (!=NULL) the returned value to assure the operation was successful
  • When some operation fails, any error message should be output to stderr, not stdout so use perror() which will also output the reason the OS thinks the operation failed.

After announcing an unrecoverable error event, the program should exit, not continue on as if everything were 'good'.

it is poor programming practice to include header files that are not being used. Suggest removing the include statements for: math.h and fctrl.h

when using popen(), the resulting file descriptor pointer should be closed via pclose(), not fclose()

I took the latest posted code and performed several simplifications and corrections, resulting in the following:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>

#define num_func 10


char * makeInvert( void );


char * makeInvert()
{
    char * buffer = malloc(200);
    if( !buffer )
    {
        perror( "malloc failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, malloc successful

    double a = (double)(rand()%5)-5.0;
    double b = (double)(rand()%5)-5.0;
    double c = (double)(rand()%5)-5.0;
    double n = (double)(rand()%5)-5.0;
    n *= 2;
    n += 1;
    sprintf(buffer, "%g(x + %g)^%g + %g == y", a, b, n, c);
    return buffer;
} // end function: makeInvert


int main( void )
{
    printf("entered main \n");
    int i;
    //char * buff1;

    for (i = 0; i < num_func; i++)
    {
        FILE * f = fopen( "inverse.R", "w");
        if (!f)
        {       //validate file is open
                //printf("cant open file");
                perror( "fopen for computation/inverse.R for write failed" );
                exit( EXIT_FAILURE );
        }

        // implied else, fopen successful

        char R[5000];

        strcpy(R, "library(Ryacas)\nyacas(\"Solve(");
        strcat(R, makeInvert());
        strcat(R, ", x)\")\n\n");
        fputs(R, f);

        //printf("before fclose\n");
        //fflush(stdout);
        fclose(f);
        f = NULL;

        //printf("after fclose\n");
        //fflush(stdout);

        //char command[300];
        //strcpy(command, "Rscript computation/inverse.R");
        //printf("command %s\n", command);

        FILE * fp = popen( "Rscript inverse.R", "r");
        if (!fp)
        {       //validate file is open
                //printf("cant open file");
                //fflush(stdout);
                perror( "popen for inverse.R for read failed" );
                exit( EXIT_FAILURE );
        }

        // implied else, popen successful

        //printf("after popen\n");
        //fflush(stdout);

        char path[5000];
        while (fgets(path, sizeof(path)-1, fp) != NULL)
        {
               printf("output: %s\n", path);
        }

        //fclose(fp);
        pclose( fp );
    }
    return 0;
}

Amongst other things, note the consistent indenting, for the purpose of readability. The compiler doesn't care, but us humans do.

Note: R is not installed on my linux 16.04

The resulting .R file contains:

library(Ryacas)
yacas("Solve(-2(x + -4)^-5 + -4 == y, x)")

the output from running the modified code is:

entered main 
sh: 1: Rscript: not found
sh: 1: Rscript: not found
sh: 1: Rscript: not found
sh: 1: Rscript: not found
sh: 1: Rscript: not found
sh: 1: Rscript: not found
sh: 1: Rscript: not found
sh: 1: Rscript: not found
sh: 1: Rscript: not found
sh: 1: Rscript: not found
user3629249
  • 16,402
  • 1
  • 16
  • 17
  • it seems that the `Rscript` is expecting something to cause it to exit that is not in the produced inverse.R file. I suspect the `Rscript` is an interactive script, so it waiting for something to be input.. I suspect the `inverse.R` script needs one more line that says: `quit()` – user3629249 Jul 27 '17 at 20:59