{"action":"created","issue":{"url":"https://api.github.com/repos/golang/go/issues/12873","labels_url":"https://api.github.com/repos/golang/go/issues/12873/labels{/name}","comments_url":"https://api.github.com/repos/golang/go/issues/12873/comments","events_url":"https://api.github.com/repos/golang/go/issues/12873/events","html_url":"https://github.com/golang/go/issues/12873","id":110322100,"number":12873,"title":"runtime: C popen hangs when linked with Go shared library","user":{"login":"pavel-odintsov","id":2744166,"avatar_url":"https://avatars.githubusercontent.com/u/2744166?v=3","gravatar_id":"","url":"https://api.github.com/users/pavel-odintsov","html_url":"https://github.com/pavel-odintsov","followers_url":"https://api.github.com/users/pavel-odintsov/followers","following_url":"https://api.github.com/users/pavel-odintsov/following{/other_user}","gists_url":"https://api.github.com/users/pavel-odintsov/gists{/gist_id}","starred_url":"https://api.github.com/users/pavel-odintsov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pavel-odintsov/subscriptions","organizations_url":"https://api.github.com/users/pavel-odintsov/orgs","repos_url":"https://api.github.com/users/pavel-odintsov/repos","events_url":"https://api.github.com/users/pavel-odintsov/events{/privacy}","received_events_url":"https://api.github.com/users/pavel-odintsov/received_events","type":"User","site_admin":false},"labels":[],"state":"open","locked":false,"assignee":null,"milestone":{"url":"https://api.github.com/repos/golang/go/milestones/24","html_url":"https://github.com/golang/go/milestones/Go1.6","labels_url":"https://api.github.com/repos/golang/go/milestones/24/labels","id":1096159,"number":24,"title":"Go1.6","description":"","creator":{"login":"josharian","id":67496,"avatar_url":"https://avatars.githubusercontent.com/u/67496?v=3","gravatar_id":"","url":"https://api.github.com/users/josharian","html_url":"https://github.com/josharian","followers_url":"https://api.github.com/users/josharian/followers","following_url":"https://api.github.com/users/josharian/following{/other_user}","gists_url":"https://api.github.com/users/josharian/gists{/gist_id}","starred_url":"https://api.github.com/users/josharian/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/josharian/subscriptions","organizations_url":"https://api.github.com/users/josharian/orgs","repos_url":"https://api.github.com/users/josharian/repos","events_url":"https://api.github.com/users/josharian/events{/privacy}","received_events_url":"https://api.github.com/users/josharian/received_events","type":"User","site_admin":false},"open_issues":332,"closed_issues":177,"state":"open","created_at":"2015-05-05T02:39:06Z","updated_at":"2015-10-08T01:51:40Z","due_on":"2016-01-31T08:00:00Z","closed_at":null},"comments":9,"created_at":"2015-10-07T21:33:20Z","updated_at":"2015-10-08T15:16:22Z","closed_at":null,"body":"Hello, Folks!\r\n\r\nI have used dynamic library builded with Go:\r\n```bash\r\ngo build -buildmode=c-shared -o libgobgp.so *.go\r\n```\r\n\r\nAnd I link this dynamic library with my C++ code which uses daemonization and popen system call:\r\n```C\r\n// Call fork function\r\nint do_fork() {\r\n    int status = 0;\r\n\r\n    switch (fork()) {\r\n    case 0:\r\n        // It's child\r\n        break;\r\n    case -1:\r\n        /* fork failed */\r\n        status = -1;\r\n        break;\r\n    default:\r\n        // We should close master process with _exit(0)\r\n        // We should not call exit() because it will destroy all global variables for programm\r\n        _exit(0);\r\n    }\r\n\r\n    return status;\r\n}\r\n\r\nvoid redirect_fds() {\r\n    // Close stdin, stdout and stderr\r\n    close(0);\r\n    close(1);\r\n    close(2);\r\n\r\n    if (open(\"/dev/null\", O_RDWR) != 0) { \r\n        // We can't notify anybody now\r\n        exit(1);\r\n    }    \r\n\r\n    // Create copy of zero decriptor for 1 and 2 fd's\r\n    // We do not need return codes here but we need do it for suppressing complaints from compiler\r\n    int first_dup_result  = dup(0);\r\n    int second_dup_result = dup(0);\r\n}\r\n\r\nstd::vector<std::string> exec(std::string cmd) {\r\n    std::vector<std::string> output_list;\r\n\r\n    FILE* pipe = popen(cmd.c_str(), \"r\");\r\n    if (!pipe) return output_list;\r\n\r\n    char buffer[256];\r\n    while (!feof(pipe)) {\r\n        if (fgets(buffer, 256, pipe) != NULL) {\r\n            size_t newbuflen = strlen(buffer);\r\n\r\n            // remove newline at the end\r\n            if (buffer[newbuflen - 1] == '\\n') {\r\n                buffer[newbuflen - 1] = '\\0';\r\n            }    \r\n\r\n            output_list.push_back(buffer);\r\n        }    \r\n    }    \r\n\r\n    pclose(pipe);\r\n    return output_list;\r\n}\r\n\r\nint main () {\r\n    if (daemonize) {\r\n        int status = 0;\r\n\r\n        printf(\"We will run in daemonized mode\\n\");\r\n\r\n        if ((status = do_fork()) < 0) {\r\n            // fork failed\r\n            status = -1;\r\n        } else if (setsid() < 0) {\r\n            // Create new session\r\n            status = -1;\r\n        } else if ((status = do_fork()) < 0) {\r\n            status = -1;\r\n        } else {\r\n            // Clear inherited umask\r\n            umask(0);\r\n\r\n            // Chdir to root\r\n            int chdir_result = chdir(\"/\");\r\n\r\n            // close all descriptors because we are daemon!\r\n            redirect_fds();\r\n        }\r\n    }\r\n \r\n    // this code become zombie\r\n    exec(\"ip link show\");\r\n\r\n    // this code will be never called\r\n    while (true) {\r\n \r\n   }\r\n}\r\n```\r\n\r\nWhen I run my tool in normal non daemon code everything work perfectly.\r\n\r\nBut when I use --daemonize with my toolkit everything become weird and \"/sbin/ip\" become zombie:\r\n```bash\r\n15747 root       20   0  297M 18332 12408 S  0.0  0.1  0:00.00 ├─ ./fastnetmon --daemonize\r\n15748 root       20   0     0     0     0 Z  0.0  0.0  0:00.00 │  └─ sh\r\n\r\ncat /proc/15748/status\r\nName:   sh\r\nState:  Z (zombie)\r\n```\r\n\r\nIn strace I saw:\r\n```bash\r\n[pid 15475] write(1, \"11: eth5: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000\\\\    link/ether a0:36:9f:0c:8d:d6 brd ff:ff:ff:ff:ff:ff\\n\", 171 <unfinished ...>\r\n[pid 15473] <... read resumed> \"10: eth6: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000\\\\    link/ether a0:36:9f:0c:8d:d4 brd ff:ff:ff:ff:ff:ff\\n\", 4096) = 171\r\n[pid 15475] <... write resumed> )       = 171\r\n[pid 15473] read(4,  <unfinished ...>\r\n[pid 15475] exit_group(0)               = ?\r\n[pid 15473] <... read resumed> \"11: eth5: <BROADCAST,MULTICAST,PROMISC,UP,LOWER_UP> mtu 1500 qdisc mq state UP mode DEFAULT group default qlen 1000\\\\    link/ether a0:36:9f:0c:8d:d6 brd ff:ff:ff:ff:ff:ff\\n\", 4096) = 171\r\n[pid 15473] read(4,  <unfinished ...>\r\n[pid 15475] +++ exited with 0 +++\r\n[pid 15474] <... wait4 resumed> [{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 15475\r\n[pid 15474] --- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=15475, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---\r\n[pid 15474] rt_sigreturn()              = 15475\r\n[pid 15474] exit_group(0)               = ?\r\n[pid 15474] +++ exited with 0 +++\r\n<... read resumed> \"\", 4096)            = 0\r\n--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=15474, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---\r\nrt_sigprocmask(SIG_SETMASK, NULL, ~[KILL STOP], 8) = 0\r\nsigaltstack({ss_sp=0xc820032000, ss_flags=0, ss_size=32672}, NULL) = 0\r\ngettid()                                = 15473\r\nrt_sigprocmask(SIG_SETMASK, ~[ILL TRAP BUS FPE KILL SEGV STKFLT CHLD STOP PROF], NULL, 8) = 0\r\nfutex(0xc820030110, FUTEX_WAIT, 0, NULL\r\n```\r\n\r\nI have found so much signal reconfigurations from Go's runtime side and assume this problems related with Go runtime itself. And we have got conflict between Golang's signal handlers and popen's SIGCHILD signal call.\r\n\r\nDo you have any workaround for this case?\r\n"},"comment":{"url":"https://api.github.com/repos/golang/go/issues/comments/146576172","html_url":"https://github.com/golang/go/issues/12873#issuecomment-146576172","issue_url":"https://api.github.com/repos/golang/go/issues/12873","id":146576172,"user":{"login":"pavel-odintsov","id":2744166,"avatar_url":"https://avatars.githubusercontent.com/u/2744166?v=3","gravatar_id":"","url":"https://api.github.com/users/pavel-odintsov","html_url":"https://github.com/pavel-odintsov","followers_url":"https://api.github.com/users/pavel-odintsov/followers","following_url":"https://api.github.com/users/pavel-odintsov/following{/other_user}","gists_url":"https://api.github.com/users/pavel-odintsov/gists{/gist_id}","starred_url":"https://api.github.com/users/pavel-odintsov/starred{/owner}{/repo}","subscriptions_url":"https://api.github.com/users/pavel-odintsov/subscriptions","organizations_url":"https://api.github.com/users/pavel-odintsov/orgs","repos_url":"https://api.github.com/users/pavel-odintsov/repos","events_url":"https://api.github.com/users/pavel-odintsov/events{/privacy}","received_events_url":"https://api.github.com/users/pavel-odintsov/received_events","type":"User","site_admin":false},"created_at":"2015-10-08T15:16:22Z","updated_at":"2015-10-08T15:16:22Z","body":"But I hit another issue with dlclose.\r\n\r\nSo when I use code linke this: https://github.com/pavel-odintsov/gobgp_api_cpp_client/blob/master/gobgp_api_client.cc#L265 and call dlclose before toolkit exit I got segmentation fault.\r\n\r\n```bash\r\nLD_LIBRARY_PATH=\"/opt/grpc_0_11_1_7a94236d698477636dd06282f12f706cad527029/lib;/opt/protobuf_3.0.0_alpha4/lib;/opt//libgobgp_1_0_0/lib/\" gdb ./gobgp_api_client \r\nGNU gdb (Debian 7.7.1+dfsg-5) 7.7.1\r\nCopyright (C) 2014 Free Software Foundation, Inc.\r\nLicense GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>\r\nThis is free software: you are free to change and redistribute it.\r\nThere is NO WARRANTY, to the extent permitted by law.  Type \"show copying\"\r\nand \"show warranty\" for details.\r\nThis GDB was configured as \"x86_64-linux-gnu\".\r\nType \"show configuration\" for configuration details.\r\nFor bug reporting instructions, please see:\r\n<http://www.gnu.org/software/gdb/bugs/>.\r\nFind the GDB manual and other documentation resources online at:\r\n<http://www.gnu.org/software/gdb/documentation/>.\r\nFor help, type \"help\".\r\nType \"apropos word\" to search for commands related to \"word\"...\r\nReading symbols from ./gobgp_api_client...done.\r\n(gdb) run\r\nStarting program: /usr/src/gobgp_api_cpp_client/gobgp_api_client \r\n[Thread debugging using libthread_db enabled]\r\nUsing host libthread_db library \"/lib/x86_64-linux-gnu/libthread_db.so.1\".\r\nwarning: File \"/usr/local/go/src/runtime/runtime-gdb.py\" auto-loading has been declined by your `auto-load safe-path' set to \"$debugdir:$datadir/auto-load\".\r\nTo enable execution of this file add\r\n\tadd-auto-load-safe-path /usr/local/go/src/runtime/runtime-gdb.py\r\nline to your configuration file \"/root/.gdbinit\".\r\nTo completely disable this security protection add\r\n\tset auto-load safe-path /\r\nline to your configuration file \"/root/.gdbinit\".\r\nFor more information about this security protection see the\r\n\"Auto-loading safe path\" section in the GDB manual.  E.g., run from the shell:\r\n\tinfo \"(gdb)Auto-loading safe path\"\r\n[New Thread 0x7ffff4c71700 (LWP 22000)]\r\n[New Thread 0x7fffeffff700 (LWP 22001)]\r\n[New Thread 0x7fffef7fe700 (LWP 22002)]\r\n[New Thread 0x7fffeeffd700 (LWP 22003)]\r\n[New Thread 0x7fffee7fc700 (LWP 22004)]\r\n[New Thread 0x7fffedffb700 (LWP 22005)]\r\n[Thread 0x7fffedffb700 (LWP 22005) exited]\r\nList of announced prefixes for route family: 65537\r\n\r\nPrefix: 10.10.20.33/32\r\nNLRI: {\"nlri\":{\"prefix\":\"10.10.20.33/32\"},\"attrs\":[{\"type\":1,\"value\":2},{\"type\":3,\"nexthop\":\"10.10.1.99\"}]}\r\n\r\n\r\nList of announced prefixes for route family: 65669\r\n\r\nPrefix: [destination:10.0.0.0/24][protocol: tcp][source:20.0.0.0/24]\r\nNLRI: {\"nlri\":{\"value\":[{\"type\":1,\"value\":{\"prefix\":\"10.0.0.0/24\"}},{\"type\":3,\"value\":[{\"op\":129,\"value\":6}]},{\"type\":2,\"value\":{\"prefix\":\"20.0.0.0/24\"}}]},\"attrs\":[{\"type\":1,\"value\":2},{\"type\":14,\"nexthop\":\"0.0.0.0\",\"afi\":1,\"safi\":133,\"value\":[{\"value\":[{\"type\":1,\"value\":{\"prefix\":\"10.0.0.0/24\"}},{\"type\":3,\"value\":[{\"op\":129,\"value\":6}]},{\"type\":2,\"value\":{\"prefix\":\"20.0.0.0/24\"}}]}]},{\"type\":16,\"value\":[{\"type\":128,\"subtype\":8,\"value\":\"10:10\"}]}]}\r\n\r\nProgram received signal SIGSEGV, Segmentation fault.\r\n[Switching to Thread 0x7fffee7fc700 (LWP 22004)]\r\n0x00007ffff50c91a1 in ?? ()\r\n(gdb) bt\r\n#0  0x00007ffff50c91a1 in ?? ()\r\n#1  0x00007ffff50932b3 in ?? ()\r\n#2  0x000000c820066110 in ?? ()\r\n#3  0x0000000000000000 in ?? ()\r\n(gdb) quit\r\nA debugging session is active.\r\n\r\n\tInferior 1 [process 21996] will be killed.\r\n\r\nQuit anyway? (y or n) y\r\n```\r\n\r\nDo you have any options to shutdown Golang's runtime correctly? "}}