最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

Segmentation fault when executing a Python script in a C program? - Stack Overflow

programmeradmin0浏览0评论

I need to execute some python and C at the same time. I tried using Python.h:

#include <Python.h>

int python_program(char* cwd)
{  
  char* python_file_path;
  FILE* fd;
  int run;

  python_file_path = malloc(sizeof(char) * (strlen(cwd) + strlen("src/query.py") + 1));
  strcpy(python_file_path, cwd);
  strcat(python_file_path, "src/query.py");
  fd = fopen(python_file_path, "r");

  Py_Initialize(); 


  run = PyRun_AnyFile(fd, "query.py"); //this part is where the bug occur i think

  Py_Finalize();

  free(python_file_path);
}

int main(int argc, char *argv[])
{
  char cwd_buffer[64];
  

  getcwd(cwd_buffer, sizeof(cwd_buffer));
  python_program(cwd_buffer);
  
  return 0;
}

...but there's an error with segmentation fault.

26057 segmentation fault (core dumped)  ../spotify-viewer-cli

I isolated the Python.h part and it's the problem. So how can I execute the python file in my C program?

I need to execute some python and C at the same time. I tried using Python.h:

#include <Python.h>

int python_program(char* cwd)
{  
  char* python_file_path;
  FILE* fd;
  int run;

  python_file_path = malloc(sizeof(char) * (strlen(cwd) + strlen("src/query.py") + 1));
  strcpy(python_file_path, cwd);
  strcat(python_file_path, "src/query.py");
  fd = fopen(python_file_path, "r");

  Py_Initialize(); 


  run = PyRun_AnyFile(fd, "query.py"); //this part is where the bug occur i think

  Py_Finalize();

  free(python_file_path);
}

int main(int argc, char *argv[])
{
  char cwd_buffer[64];
  

  getcwd(cwd_buffer, sizeof(cwd_buffer));
  python_program(cwd_buffer);
  
  return 0;
}

...but there's an error with segmentation fault.

26057 segmentation fault (core dumped)  ../spotify-viewer-cli

I isolated the Python.h part and it's the problem. So how can I execute the python file in my C program?

Share Improve this question edited Nov 20, 2024 at 16:41 genpfault 52.2k12 gold badges91 silver badges149 bronze badges asked Nov 20, 2024 at 16:37 YukiYuki 931 silver badge5 bronze badges 4
  • 4 Please add some error checking. At the very least you should check the value returned by fopen. – G.M. Commented Nov 20, 2024 at 16:40
  • You're also missing includes, there are unused variables, no return in python_program(). Start from fixing simple mistakes, compile with cc main.c -o main -Wall -Wextra -pedantic and then if there are no errors any more compile with -g and run your binary under valgrind to identify the line that triggers segfault. – Arkadiusz Drabczyk Commented Nov 20, 2024 at 16:51
  • 2 I'm quite sure that fopen() will indeed return NULL; Debug the filename python_file_path :-) – Ingo Leonhardt Commented Nov 20, 2024 at 17:04
  • Add the command to build and run. Also you could add some printfs to see where it crashes (if you don't want to debug). [SO]: Welcome to Stack Overflow. Check [SO]: How do I ask a good question? or [SO]: How to create a Minimal, Reproducible Example (reprex (mcve)) for more asking related details. – CristiFati Commented Nov 20, 2024 at 17:32
Add a comment  | 

5 Answers 5

Reset to default 0

Golden rule: error handling is not an option but a hard requirement in programming (pointed out by answers and comments).

Failing to include it might work for a while, but almost certainly will come back and bite in the ass at a later time, and it will do it so hard that someone (unfortunately, often not the same person who wrote the faulty code) will spend much more time (than writing it in the 1st place) fixing subtle errors (or crashes).

Also, reading the documentation for the used functions, might save precious time too, avoiding all kinds of errors generated by passing to them arguments based on some false assumptions.

Same case here (Undefined Behavior):

  • [Man7]: getcwd(3) doesn't end the path with a separator (/)

    • Computed script path doesn't exist

      • fopen fails (returns NULL)

        • PyRun_AnyFile SegFaults

I created a MCVE ([SO]: How to create a Minimal, Reproducible Example (reprex (mcve))), and also added some printf statements useful to identify the culprit (the preferred option would be to go step by step using a debugger (e.g.: [SourceWare]: GDB: The GNU Project Debugger)).

  • dir00/code00.py

    #!/usr/bin/env python
    
    import os
    import sys
    
    
    def main(*argv):
        print(f"From Python - file: {os.path.abspath(__file__)}")
    
    
    if __name__ == "__main__":
        print(
            "Python {:s} {:03d}bit on {:s}\n".format(
                " ".join(elem.strip() for elem in sys.version.split("\n")),
                64 if sys.maxsize > 0x100000000 else 32,
                sys.platform,
            )
        )
        rc = main(*sys.argv[1:])
        #print("\nDone.\n")
        #sys.exit(rc)
    
  • main00.c:

    #include <errno.h>
    #include <stdio.h>
    #include <unistd.h>
    
    #include <Python.h>
    
    #define PY_SCRIPT "code00.py"
    #define FULL_PY_SCRIPT "dir00/" PY_SCRIPT
    
    
    int runPyFile(const char *wd)
    {  
        char *script;
        FILE *fp;
        int res;
    
        script = malloc(sizeof(char) * (strlen(wd) + strlen(FULL_PY_SCRIPT) + 2));
        if (!script) {
            printf("malloc error: %d\n", errno);
            return -1;
        }
        strcpy(script, wd);
        strcat(script, "/");  // @TODO - cfati
        strcat(script, FULL_PY_SCRIPT);
        printf("script path: %s\n", script);
        if (access(script, F_OK)) {  // Extra check
            printf("Script doesn't exist\n");
            return -2;
        }
        fp = fopen(script, "r");
        if (!fp) {
            printf("fopen error: %d\n", errno);
            free(script);
            return -3;
        }
        free(script);
        Py_Initialize();
        res = PyRun_SimpleFile(fp, PY_SCRIPT);  // Call this function directly (skip PyRun_AnyFile layer)
        if (res) {
            printf("PyRun_SimpleFile error\n");
        }
        Py_Finalize();
        fclose(fp);
        return res;
    }
    
    
    int main(int argc, char *argv[])
    {
        char cwd[PATH_MAX];
    
        if (!getcwd(cwd, sizeof(cwd))) {
            printf("getcwd error: %d\n", errno);
            return -1;
        }
        printf("cwd (check its end): %s\n", cwd);
        int res = runPyFile(cwd);
        if (res) {
            // Some extra handling (or exit function if it's more complex)
        } else {
            printf("Script ran fine\n");
        }
    
        printf("\nDone.\n\n");
        return res;
    }
    

Output:

[cfati@cfati-5510-0:/mnt/e/Work/Dev/StackExchange/StackOverflow/q079208182]> ~/sopr.sh
### Set shorter prompt to better fit when pasted in StackOverflow (or other) pages ###

[064bit prompt]> tree
.
+-- dir00
¦   +-- code00.py
+-- main00.c

1 directory, 2 files
[064bit prompt]>
[064bit prompt]> PY_VER="3.11"
[064bit prompt]> gcc -fPIC -I/usr/include/python${PY_VER}  -o test${PY_VER} -L/usr/lib/$(uname -m)-linux-gnu main00.c -lpython${PY_VER}
[064bit prompt]> ls
dir00  main00.c  test3.11
[064bit prompt]>
[064bit prompt]> ./test${PY_VER}
cwd (check its end): /mnt/e/Work/Dev/StackExchange/StackOverflow/q079208182
script path: /mnt/e/Work/Dev/StackExchange/StackOverflow/q079208182/dir00/code00.py
Python 3.11.3 (main, Apr  5 2023, 14:15:06) [GCC 9.4.0] 064bit on linux

From Python - file: /mnt/e/Work/Dev/StackExchange/StackOverflow/q079208182/code00.py
Script ran fine

Done.

I tried using Python.h (maybe it's not the most performant solution).

Python.h is one detail -- and not even the most important -- of embedding a Python interpreter inside your C program. "Using Python.h" is not a great way of describing that. However, embedding Python in your C program is generally a pretty performant way to run Python code.

Your general approach looks ok, but there are some issues with the details. Very likely you would detect a problem earlier on, and more manageably, if your code actually looked, as any robust C program must do. Potential issues include:

  • main()'s cwd_buffer might not be large enough to hold an absolute path to the current working directory. In that case, getcwd() would fail, returning a null pointer and setting errno to ENAMETOOLONG. You would detect this by verifying that the return value is not null.

  • getcwd() might also fail for a variety of other, less likely reasons, which again you would detect by checking whether a null pointer was returned.

  • python_program()'s call to malloc() might fail, returning a null pointer and setting errno appropriately. You would detect this by verifying that the return value is not null.

  • The code

      strcpy(python_file_path, cwd);
      strcat(python_file_path, "src/query.py");
    

    might not -- and probably does not -- construct a correct path to the Python file. It would almost certainly be incorrect if cwd did not contain a trailing slash character, and I expect that it will not. Although you cannot detect such an issue directly, see next.

  • You do not check whether opening the Python file succeeds, which it probably won't on account of the previous issue. If opening it fails for that or any of the other possible reasons, then fopen() will fail, returning a null pointer and setting errno appropriately. You would detect this by checking whether the return value is null. If your program reaches the call to PyRun_AnyFile() and the segfault is thrown there, then having failed to open the file is a likely explanation.

Add check to see if you pass the correct fd

fd = fopen(python_file_path, "r");
if(!fd)
{ 
    printf("Cant open '%s'\n", python_file_path);
    /* handle this error - abort return or something else */
}

It looks like you're running on a *nix system so you're missing a "/" in strcat(python_file_path, "src/query.py"); line to separate cwd from /src/query.py. It should be:

strcat(python_file_path, "/src/query.py");

Without it the name of the Python script to run is:

/home/user/c-code/segfault-pythonsrc/query.py

while it should be:

/home/user/c-code/segfault-python/src/query.py

You would know that BTW if you checked value of fd as suggested in other answers. You should also reserve a space for it while mallocing:

python_file_path = malloc(sizeof(char) * (strlen(cwd) + strlen("/src/query.py") + 1));

I didn't need the Python.h library, what i did is using system() in the sys/ library. I used named pipes for transmitting data between programs.

发布评论

评论列表(0)

  1. 暂无评论