Code Monkey home page Code Monkey logo

subprocess.h's People

Contributors

cdwfs avatar fricher avatar ha11owed avatar mjcarroll avatar nburrus avatar pthom avatar rasky avatar sheredom avatar takase1121 avatar timgates42 avatar visualdoj avatar wirtos avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

subprocess.h's Issues

Lib is not spawning terminal instance to run a code

previously im doing this to launch a C++ bin in a separate terminal in linux:

	std::string cmd_linux = fmt::format("$TERMINAL -e \"./{:s}\"", filename);
	FILE* p = popen(cmd_linux.c_str(), "r");
	if (p == NULL)
		return RES_FAIL;
	pclose(p);

but converting it to using subprocess works in that it doesnt throw any error, but there's nothing ran.

	const char* cmd_linux[] = {filename.c_str(), NULL};
	//const char* cmd_linux[] = {"$TERMINAL", "-e", filename.c_str(), NULL}; //doesnt even work
	struct subprocess_s subprocess;
	int result = subprocess_create(cmd_linux, subprocess_option_inherit_environment, &subprocess);
	if (result != 0)
		return RES_FAIL;

the file to be ran is in the same directory as the program. The program to run has a getchar() at then end so it wont close automatically

out of date process_create doc in readme

Readme informs to create a process like so:

const char command_line[] = {"echo", "\"Hello, world!\"", NULL};
struct process_s process;
int result = process_create(command_line, &process);
if (0 != result) {
  // an error occurred!
}

however process_create takes an integer bitfield of process_option_e.

The code also has const char command_line[] = {"echo", "\"Hello, world!\"", NULL};, which isn't valid at least on msvc. Needed to be changed to const char* command_line[] = {"echo", "\"Hello, world!\"", NULL}; (note the type)

After making this change and providing process_option_e::process_option_combined_stdout_stderr and 0, both times this example code gives me a -1 result, and the exit code is -1.

Unicode support on Windows?

Working with character encoding on Windows is really annoying.

Passing UTF-8 characters on the command line using CreateProcessA seems to be impossible. While local code pages seem to be able to handle special characters such as Chinese and Japanese, they don't seem to be able to do that for emoji.

In my case, the way I use it is to convert your commandLineCombined to UTF16LE characters and then call CreateProcessW. My original input was UTF8 characters, and this modification seems to handle UTF8 characters properly.

What do you think of this? Thanks.

Implement: bool subprocess_alive()

When working with an async process, it'd be nice to have a clean method to periodically check if the process is still alive or if it has already finished. Right now, _join(), _destroy() and _terminate() seem to all be about waiting for a process to end - or just destroying the struct and sending it away from the current process (as stated that _destroy() can leave a process running even after main execution has finished).

MSVC linkage errors

I'm getting linkage errors in C++ code that uses process.h; the predeclarations of various internal Windows API calls doesn't match what's eventually defined by windows.h, if both headers are included:

1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\handleapi.h(39,6): error C2732: linkage specification contradicts earlier specification for 'CloseHandle'
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\handleapi.h(38): message : see declaration of 'CloseHandle'
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\handleapi.h(87,6): error C2732: linkage specification contradicts earlier specification for 'SetHandleInformation'
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\handleapi.h(84): message : see declaration of 'SetHandleInformation'
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\namedpipeapi.h(34,6): error C2732: linkage specification contradicts earlier specification for 'CreatePipe'
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\namedpipeapi.h(30): message : see declaration of 'CreatePipe'
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\synchapi.h(382,6): error C2732: linkage specification contradicts earlier specification for 'WaitForSingleObject'
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\synchapi.h(380): message : see declaration of 'WaitForSingleObject'
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\processthreadsapi.h(154,6): error C2732: linkage specification contradicts earlier specification for 'GetExitCodeProcess'
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\processthreadsapi.h(152): message : see declaration of 'GetExitCodeProcess'
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\processthreadsapi.h(371,6): error C2732: linkage specification contradicts earlier specification for 'CreateProcessA'
1>C:\Program Files (x86)\Windows Kits\10\Include\10.0.18362.0\um\processthreadsapi.h(361): message : see declaration of 'CreateProcessA'

You can reproduce the problem by adding #include <windows.h> at the end of test.cpp.

The error starts occurring in 5bdac3a; I think it has something to do with the narrowing of the extern "C" scope, which no longer includes the predeclarations. They're always defined as extern "C" in windows.h; moving them inside an extern "C" scope fixes the linkage error. Is there a reason they were moved outside that scope?

what's the difference of this one with reproc?

I know another repo: reproc
for a long time it's the only available library to handle the process create/read/write.

Now seeems we have a new choice.
Did the author check any feature not supported by reproc but supported here?

Thanks,
AH

subprocess_read_stdout() blocks

Hello.
As you pointed out in the documentation, the subprocess_read_stdout() function is blocking when there is no data available to read.
I need to be able to read and write to a subprocess before joining it, but my problem is that I need the read operation to be asynchronous. Is there an easy solution for changing this behavior?

can subprocess_join tell if the process failed?

Hello,

Thanks for this useful library!
I encountered a problem when calling subprocess_join: it cannot detect whether the process failed.

The following lines taken from the code show the reason:

  if (out_return_code) {
    if (WIFEXITED(status)) {
      *out_return_code = WEXITSTATUS(status);
    } else {
      *out_return_code = 0  // process did not exit normally, but we return 0 as exit status, anyway (!)
    }
  }

Is there another way to check for process failures?

Thanks!

"echo" -> "/bin/echo"

The reading stdout example in the README didn't work for us; stdout was just empty.

However, when we used "/bin/echo" instead of "echo" for the command, it worked!

I imagine changing the example from echo to /bin/echo would make it not work on windows though, so I'm not sure the wisest course here.

Deadlock if synchronous subprocess fills pipe

If a subprocess outputs large amount of data, it deadlocks both parent and subprocess due to pipe blocking:

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

#include "subprocess.h"

int main() {
    const char *command_line[] = {"dd", "if=/dev/zero", "bs=1k", "count=65", NULL};
    struct subprocess_s process;
    int result = subprocess_create(command_line, subprocess_option_search_user_path, &process);
    if (result) {
        fprintf(stderr, "Failed to create subprocess: %d\n", result);
        return 1;
    }
    int proc_return;
    result = subprocess_join(&process, &proc_return);
    if (result) {
        fprintf(stderr, "Failed to join subprocess\n");
        return 1;
    }
    printf("Subprocess returned %d\n", proc_return);
    result = subprocess_destroy(&process);
    if (result) {
        fprintf(stderr, "Failed to join subprocess\n");
        return 1;
    }
    return 0;
}

Notice that subprocess dd writes 65k data to stdout, which is greater than Linux's default pipe buffer size, thus blocking the child. However, since subprocess_read_stdout must be used after joining, the parent process cannot progress either, as it can neither drain the pipe nor wait for child to finish.

Lib just dont work

Hallo,
I was very happy to find this lib.
But it just doesn't work.

I tried it with C and C++ in MVS 2017.
At the example

	const char *command_line[] = { "echo", "\"Hello, world!\"", NULL };
	struct process_s process;
	int result = process_create(command_line, 0, &process);
	if (0 != result) {
		// an error occurred!
	}

I always get the result -1
If I start nodejs instead of echo, a result of 0 comes but the process does not start.

naming conflict with windows process.h

Hey,

I know "process.h" is a really compelling name, but for me at least having such a generic name is a bit of a pain. Including this file it conflicts with: c:\Program Files (x86)\Windows Kits\10\Include\10.0.16299.0\ucrt\process.h, which vs2017 picks up first when including process.h

This wouldn't be a problem if I were just adding it to my project normally, as I could just put it in a folder, but since I'm using a package manager and grab your repo - my only easy option is to add your repository as an include directory. Because you don't have a dist/ or include/ directory or anything like that, I don't have a way to be selective about including the exact file I want to.

I'm going to work around this by skipping the package manager for this dependency, but I'd like to make the suggestion that maybe you could have some branded name? Like Sean Barrett's STB? Would be pretty handy for avoiding conflicts!

my 2 cents.
thanks ๐Ÿ‘

Bug with empty environment

I was having an issue where starting a process via you lib was failing in a strange way. After debugging I realized that there is a subtle bug in how you call CreateProcessA. By default you do this:

char *used_environment = SUBPROCESS_NULL;

Which is then fed into the CreateProcessFunction, except instead of it receiving a nullptr, it receives a valid ptr to an empty string. This breaks the semantics so ends up running the process with an empty environment instead of using the inherited environment.

I hacked it as follows just to get it to work, but I'm not entirely sure what the idiomatic c way to that is (I'm mostly a c++ prog).

I did this at the CreateProcessA callsite:
( used_environment[0] == SUBPROCESS_NULL ) ? SUBPROCESS_NULL : used_environment

Do not execute `commandLineCombined` when we have only one command parameter

First of all, thank you for this project, it's the most convenient C/C++ library I've seen so far!

This question is out of my personal preference.

Sometimes I like to use a cmd like this:

const char* command_line[] = {
"foo \"bar\" \"\\\\\"\" ",
NULL
};

And this is the current msvc implementation in the project:

  // Combine commandLine together into a single string
  len = 0;
  for (i = 0; commandLine[i]; i++) {
    // For the ' ' and two '"' between items and trailing '\0'
    len += 3;

    for (j = 0; '\0' != commandLine[i][j]; j++) {
      switch (commandLine[i][j]) {
      default:
        break;
      case '\\':
        if (commandLine[i][j + 1] == '"') {
          len++;
        }

        break;
      case '"':
        len++;
        break;
      }
      len++;
    }
  }

Well, I didn't look closely at the code, but when I had only one command, I observed the wrong escape.

As mentioned above, this is likely to be a complex command line if there is only one command. It is better to follow the user's input and just take the command line without modifying it.

What do you think about this? Thanks!

Replacement of execvp for execv break compatibility.

Hi! I recently updated the library with the latest version and it wasn't working for me. I analyzed the changes of the new version and found out that the call that was using execvp now is replaced for execv (in subprocess_create_ex). The problem is that breaks the behavior since execv expects a path (the path to the binary file) and execvp expects a file (a binary file name or a path, if you provide the binary file name it will search in PATH the binary name provided). It's also inconsistent with the other exec calls since execve is being used (which expects also a file). For the moment it just made that change locally but I just wanted to let you know this.
Thanks for the library.

Can stdout and stderr be the same file?

Is it possible to make stdout and stderr be the same FILE*? This can be
useful when for getting the full context of a processes failure, or if the
intention is to show its output in order as if in a terminal.

Permanently redirect stdout on child process

I have the following go code that I'm converting to c++

        javaPath := getJavaPath()

	cmd := exec.Command(javaPath, "-m", "pvphero/com.pvphero.PvpHero", "-live", "-hidden")

	if dev {
		cmd = exec.Command(javaPath, "-p", "E:\\Sync\\Insignia\\hd-client\\out\\production\\classes;E:\\Sync\\Insignia\\hd-client\\out\\production\\resources", "-m", "pvphero/com.pvphero.PvpHero", "-hidden", "-live")
		cmd.Stdout = os.Stdout
		cmd.Stderr = os.Stderr
	}

	fmt.Println("Launching client", cmd)

	_ = cmd.Start()

I was wondering is there any way to redirect the stdout of my child process to share the same one as its parent process? Or will I just need to make another thread that loops/polls for stdout messages? I tried looking through the tests you have on here but couldn't find any similar use case.

PS. Your library is awesome.

posix_spawn does not build on Android & Emscripten (Wasm)

The changes introduced here de3cbec produce compilation errors on these two platforms: https://github.com/aardappel/lobster/actions/runs/2278815862

Now I am totally fine #ifdeffing support out for those 2 platforms, since it doesn't make any sense to spawn processes there anyway. But the curious thing is that the old fork code DID compile on those platforms.

So it is more of a question if you want to #ifdef it in your library for maximum modularity, or if you want users to #ifdef it :) I don't mind either way. Feel free to close if WillNotFix.

documentation unclear: subprocess_create needs full path ?

Thank you for sharing this nice library.

I was using it in one of my project but I stumbled in a problem on linux about PATH resolution not done.

Code snippet is:

  const char *command_line[] = {"xrdb", "-query", NULL};
  struct subprocess_s process;
  int result = subprocess_create(command_line, 0, &process);

but it fails, it seems the full path is needed. The only way I found to make it work was to provide full path and using the inherit environment option.

Now this is confusing to me. Looking at the documentation my understanding was that the full path is not needed. I addition the documentation says absolutely nothing about that.

There is an easy way to have PATH resolution for the binary out-of-the-box ?
Shouldn't the documentation says something about the PATH ?

PS: I found a related issue that lead to the PR that introduced create_subprocess_ex but I failed to understand the relation with the PATH issue I had.

Deadlock on Windows

Hey,

It seems I get a deadlock if the process I launch has a relatively long output (works at 300 characters, but doesn't work at 450 or more it seems).

What I do is basically:

subprocess_create(...);
subprocess_join(...);
while (fgets(buffer, sizeof(buffer), subprocess_stdout(&process)))
	...
subprocess_destroy(...);

and it blocks inside join (in WaitForSingleObject()), but only if the output is long (otherwise it works).

If I try to do the join after the fgets, then it blocks inside fgets (even for small outputs).

Any idea? Am I doing something wrong?

Incorrect argument quoting on Windows

Yeah. I'm also speechless. Apparently on Windows you MUSTN'T quote too little or too much.

#include <stdio.h>
#include <stddef.h>
#include <malloc.h>
#include "subprocess.h"

int main() {
        const char *cmd[] = { "cmd", "/c", "echo hello", NULL };
        struct subprocess_s sub;
        int r = subprocess_create(cmd, 0, &sub);
        FILE *p = subprocess_stderr(&sub);
        char bruh[32];
        while (fgets(bruh, 32, p)) {
                printf("%s\n", bruh);
        }
        int pr;
        subprocess_join(&sub, &pr);
        return 0;
}

The code is a bit messy, but its obvious this is your typical run of the mill echo example adapted for Windows.
The only problem is it doesn't work.

Apparently cmd.exe doesn't like its flags to be quoted. This library quotes every argument, so something from the above example would produce "cmd" "/c" "echo hello world". Perfectly fine for you and me (and sh), but for cmd... it interpreted "/c" "echo hello world" as /c "\"echo hello world"! Suddenly there's an extra quote there. The solution is apparently

const char *cmd[] = { "cmd", "/c echo hello", NULL };

This entire thing is just mind-boggling.
Apparently the gold standard's force parameter is actually important! The only way I see to fix this problem is to not quote unescaped parameter, but I am not entirely sure (also pretty hard to do).

EDIT: some potential fixes:

454a455
>   int need_quoting;
625,626c626,631
<     // For the ' ' and two '"' between items and trailing '\0'
<     len += 3;
---
>     // For the trailing \0
>     len++;
>
>     // Quote the argument if it has space or tabs in it
>     if (strpbrk(commandLine[i], "\t\v ") != NULL)
>       len += 2;
659c664,668
<     commandLineCombined[len++] = '"';
---
>
>     need_quoting = strpbrk(commandLine[i], "\t\v ") != NULL;
>     if (need_quoting) {
>       commandLineCombined[len++] = '"';
>     }
678c687,690
<     commandLineCombined[len++] = '"';
---
>
>     if (need_quoting) {
>       commandLineCombined[len++] = '"';
>     }

Support for "CREATE_NO_WINDOW" when launching process

Hi,

Thanks for the great lib, it's saved me a lot of pain. Would it be able to add support for the 'CREATE_NO_WINDOW' creation flag for the windows CreateProcess call?

I've hacked it locally since I dont ever want the process windows to show up but it might be useful for others as well.

Issue with quotes in command line

Hi, first of all thanks for this library, I've been looking for something nice like this for a while. I'm having an issue trying to unzip a file using 7z and I'm not sure how to fix it. I think it has to do with the way subprocess wraps things in quotes. I was hoping something like this would work:

const char* command_line[] = { "7za.exe", "x C:/myfile.zip -oC:/mydir/ -y", nullptr };

However the final command line ends up being something like:

"7za.exe" "x C:/myfile.zip -oC:/mydir/ -y"

and 7zip interprets that second part as a single argument. Is there a way for me to directly pass the arguments without adding the quotes? Would that even be the correct solution?

Thanks!

Verboser output of child in case of invalid/unknown command

We are currently validating various libs for a simple process handling in C++ (came from Boost.Process...) and are quite excited about subprocess.h

However, when doing some basic tests we might have found a part of the lib, which could be improved to ease troubleshooting for the user/developer.
E. g. when somebody gives an invalid command (e.g. lss) no further information is given. In contrast, other libs give some hint in the form of execv's perror message or a dedicated error code. In case you are interested i appended you our patch file. With this update it is possible to check the child's stdout/stderr for the error message or simply provide it to the user.

If you have any further questions please feel free to ask.

subprocess.perror.patch.txt

Adding lifetime documentation

Hey there, great little library. Thank you so much for releasing it!

There's something missing from the header's docs that would be immensely helpful for consumers: lifetime requirements of parameters.

For example, subprocess_create_ex's environment array parameter - does the memory pointed to by the pointer value need to out-live the subprocess's? Or can that memory be freed directly after the call returns?

Adding that sort of information would be really helpful. :)

Should subprocess_option_search_user_path use the PATH from any environment to subprocess_create_ex?

@nburrus curious on your take on this - your test case custom_search_path sets the PATH in a custom enivornment, and then calls subprocess_create_ex with this PATH and subprocess_option_search_user_path - which only works if you use the PATH provided in environment. I'm not sure if this is the best approach or not, trying to get my head around it at present. I don't think this would work on windows because we don't and won't modify the PATH, and it is affecting me trying to move over to posix_spawn.

Any thoughts? At present my thinking is I'll rework the test so that you cannot use a PATH specified in a custom environment.

MinGW support

By default subprocess.h will not work on MinGW because of _MSC_VER.

I managed to get it to compile (and at least work (create, read stderr and join)) by:

  • removing line 353 (definition for _alloca) and instead #include <malloc.h>
  • -D_MSC_VER=1920 (this should be replaced by #ifdef _WIN32 and similiar
  • -D_DLL as gcc doesn't like it when it tries to compare to something undefined.

Windows subprocess_stdin and subprocess_option_enable_async?

Hello.

A question:

For a complete ready to bake example see final code block. Short story, using the handle returned from subprocess_stdin(&process);,
a call like int ret = fputs(text.c_str(), p_stdin); always returns 0, as does the subsequent subprocess_read_stdout().

Any thoughts on this? Have I missed something in the documentation?

And a suggestion:

subprocess_read_stdout() blocks. On Windows, at least, it can be made non-blocking by changing the last argument to 0. Might this not be made an optional argument?

Many thanks.

   // Means we've got an async read!
    if (error == errorIoPending) {
      if (!GetOverlappedResult(handle,
                               SUBPROCESS_PTR_CAST(LPOVERLAPPED, &overlapped),
                               &bytes_read, 0)) { // changed from 1 (wait) to 0 (don't wait)
        const unsigned long errorIoIncomplete = 996;
        const unsigned long errorHandleEOF = 38;
        error = GetLastError();

Built with cl main.cpp in a VS2022 command shell.

#include <iostream>
#include <sstream>
#include "subprocess.h"

int main(int argc,char* argv[])
{
	int ret = -1;
	try
	{
		struct subprocess_s process { 0 };
		// run a basic shell
		//const char* command_line[] = { "cmd.exe", "/K", nullptr};
		//const char* command_line[] = { "cmd.exe", "/K r:\\apps\\mingw\\msys\\1.0\\bin\\ls.exe -als", nullptr};
		const char* command_line[] = { "cmd.exe", "/K ls.exe -als", nullptr}; // this correctly says ls.exe cannot be found so is alive
		//const char* command_line[] = { "cmd.exe", "/K", "echo %path%", nullptr };
		int result = subprocess_create(command_line,
			subprocess_option_combined_stdout_stderr |
			subprocess_option_no_window |
			subprocess_option_enable_async,
			&process);
		if (0 != result) {
			// an error occurred!
		}
		
		// hack for the moment.
		_sleep(1000);

		char buffer[255];
		int chars = 0;
		do
		{
			// modified to NOT block
			chars = subprocess_read_stdout(&process, buffer, 254);
			if (chars)
			{
				buffer[chars] = '\0';
				std::cout << buffer;
			}
		} while (chars > 0);

		while (true)
		{
			std::string text;
			std::getline(std::cin, text);

			FILE* p_stdin = subprocess_stdin(&process);
			int ret = fputs(text.c_str(), p_stdin);
			std::cout << "Wrote " << ret << std::endl;
			if (ret)
			{
				do
				{
					chars = subprocess_read_stdout(&process, buffer, 254);
					if (chars)
					{
						buffer[chars] = '\0';
						std::cout << buffer;
					}
				} while (chars > 0);
			}
		}

		//
		result = subprocess_destroy(&process);
		if (0 != result) {
			// an error occurred!
		}

		ret = 0;
	}
	catch (std::exception& ex)
	{
		std::cout << "exception: " << ex.what();
	}
	catch (...)
	{
		std::cout << "unknown exception.";
	}
	return ret;
}

Separate implementation from declaration

It's a common practice to make single header C libraries with implementation guards, this allow isolating the implementation details code from the main program, thus avoiding variable name conflicts, speeding up compile time if the user include from multiple C files, and also making the library more friendly to generate libraries for other programming languages (that's my intent).

See other awesome single header C libraries such as sokol_gfx.h, miniaudio.h, as examples.

Example of possible change

The user would use like this:

#define SUBPROCESS_IMPL
#include "subprocess.h"

And somewhere in the C header

#ifdef SUBPROCESS_IMPL
/* all function implementations */
#include "subprocess.h"

Although this would break user code when he updates the library, you could negate the ifdef like STB libraries do in some headers, with a define like SUBPROCESS_ONLY_IMPL.

API declaration qualifier

It's also a good practice to allow the user to change the library API declaration qualifier, for example I would like to add static all the library functions, again sokol and miniaudio are good examples on that. Usually the compiler inline static functions.

Discussion

Separating implementation from the declaration at first looks that this would make harder for the C compiler to inline the code, but the compiler can usually inline functions by itself when the static qualifier is used. Also not everybody want to all their functions to be inlined as may increase binary size, also build times, so it's

Post Notes

I was about to ask the same for your JSON library. Although the implementation is inlined to be fast, would still be good to separate the implementation code, and this can be done without much penalties.

unable to read from stdout

Hi I tried a sample program with cat command but was not able to get required output. The code just hangs at first call to fgets. Am I doing something wrong?

#include <stdio.h>
#include <string.h>
#include "subprocess.h"
int main()
{
    const char *command_line[] = {"/usr/bin/cat", NULL};
    struct subprocess_s subprocess;
    int result = subprocess_create(command_line, 0, &subprocess);
    if (0 != result) {
        puts("an error occurred!");
        return 1;
    }
    FILE* p_stdout = subprocess_stdout(&subprocess);
    FILE* p_stdin = subprocess_stdin(&subprocess);
    FILE* p_stderr = subprocess_stderr(&subprocess);
    char msg[] = "test";
    char resp[2000] = {0};

    puts(msg);
    fputs(msg, p_stdin);

    memset(resp, 0, 2000);
    fgets(resp, 4, p_stdout);
    puts(resp);

    puts(msg);
    fputs(msg, p_stdin);

    memset(resp, 0, 2000);
    fgets(resp, 4, p_stdout);
    puts(resp);

    getchar();
    return 0;
}

Subprocess does not have internet access?

I've got a really weird bug that I cant seem to figure out. I'm using your library to spawn a child java process for my game client.

The problem is when I start the client using subprocess.h, it does not have any internet access at all and throws a bunch of exceptions.

java.net.UnknownHostException: No such host is known (js5.pvpherodev.com)
        at java.base/java.net.Inet4AddressImpl.lookupAllHostAddr(Native Method)
        at java.base/java.net.InetAddress$PlatformNameService.lookupAllHostAddr(InetAddress.java:932)
        at java.base/java.net.InetAddress.getAddressesFromNameService(InetAddress.java:1505)
        at java.base/java.net.InetAddress$NameServiceAddresses.get(InetAddress.java:851)
        at java.base/java.net.InetAddress.getAllByName0(InetAddress.java:1495)
        at java.base/java.net.InetAddress.getAllByName(InetAddress.java:1354)
        at java.base/java.net.InetAddress.getAllByName(InetAddress.java:1288)
        at java.base/java.net.InetAddress.getByName(InetAddress.java:1238)
        at pvphero/com.pvphero.client.Signlink.run(Signlink.java:165)
        at java.base/java.lang.Thread.run(Thread.java:832)

Here is the C++ code which spawns that process

void Updater::LaunchClient() {
    auto dev = true;

    struct subprocess_s process{};
    int result = -1;

    if (dev) {
        const char *command_line[] = {"java.exe", "-p",
                                      R"(E:\Sync\Insignia\hd-client\out\production\classes;E:\Sync\Insignia\hd-client\out\production\resources)",
                                      "-m", "pvphero/com.pvphero.PvpHero", /*"-hidden",*/ "-live", NULL};
        result = subprocess_create(command_line, subprocess_option_combined_stdout_stderr, &process);
    } else {
        const char *command_line[] = {getJavaPath().c_str(), "-m", "pvphero/com.pvphero.PvpHero",
                                      "-live", /*"-hidden",*/ NULL};
        result = subprocess_create(command_line, 0, &process);
    }

    if (0 != result) {
        std::cout << "Failed" << std::endl;
    }
}

If I run my client from command line with the exact same params as in the C++ code it works.

Screenshot of running the process spawned manually from command line (works with internet access):
https://dl.dropboxusercontent.com/s/1566obcus51qxsy/ProcessHacker_iHNItCSlU1.png

Screenshot of the running process spawned via C++/subprocess.h (no internet access and fails to resolve domain):
https://dl.dropboxusercontent.com/s/tazv5eaykbeomvj/ProcessHacker_8pD2audHMF.png

Any idea what could be causing this?

vector<std::string> of commandline args

I have the need in my c++ program of modifying the command line args given certain conditions. This favors using a vector<std::string> instead of the current approach.

#include <subprocess.h>
#include <initializer_list>
#include <vector>

char *convert(const std::string & s)
{
   char *pc = new char[s.size()+1];
   std::strcpy(pc, s.c_str());
   return pc; 
}

int cmd(std::initializer_list<std::string>  args)
{
    std::vector<char*> vc;
    std::transform(args.begin(), args.end(), std::back_inserter(vc), convert);
    // vc.push_back(NULL);

    struct subprocess_s subprocess;
    int result = subprocess_create(&vc[0], 0, &subprocess);
    if (0 != result) {
        printf("error occurred.");
    };  

    std::cout << "vc.size(): " << vc.size() << std::endl;

    for ( size_t i = 0 ; i < vc.size() ; i++ )
        std::cout << vc[i] << std::endl;

    for ( size_t i = 0 ; i < vc.size() ; i++ )
        delete [] vc[i];

    return 0;
}

The problem I have is that if I push_back(NULL) to add a NULL sentinel I get an error and it doesn't work as expected.. Do I have to modify subprocess.h itself with this approach and just drop the sentinel requirement or is there a way of making something like the above work?

Drop "C++" as a claimed targeted language.

C code is very rarely appropriate as C++ code. It is non-idiomatic, resource-unsafe, and difficult to combine with common C++ libraries and other idiomatic code.

I'd say it is "also usable in C++", but not beyond that.

Commands that need to be run as root/sudo fail silently

This may be a command specific problem but commands that need to be run as root fail silently.

Example:

int subprocess_join_wrapper(struct subprocess_s & subproc) {
    int process_return;
    int result = subprocess_join(&subproc, &process_return);

    std::cout << "STDOUT" << std::endl;
    FILE* p_stdout = subprocess_stdout(&subproc);
    char command_output[32];
    fgets(command_output, 32, p_stdout);
    std::cout << "\n";

    if (0 != result) {
        std::cout << "STDERR" << std::endl;

        FILE* p_stderr = subprocess_stderr(&subproc);
        fgets(command_output, 32, p_stderr);
        std::cout << "\n";
    }

    return result;
}

void run_command(const char *command[], struct subprocess_s & subprocess ) {
    std::cout << "Running Command: ";
    print_command(command);
    std::cout << "Process Created: " << subprocess_create(command, 0, &subprocess) << std::endl;
    int join_result = subprocess_join_wrapper(subprocess);
    std::cout << "Process Joined: " << join_result << std::endl;
}

void LinkAdd(std::string linkName) {
    struct subprocess_s subprocess{};
   const char *deviceCreation[] = {"/bin/ip", "link", "add", "dev", linkName, "type", "bridge", nullptr};

    std::cout << "Adding link for " << linkName << "\n";
    run_command(deviceCreation, subprocess);
    std::cout << "Finished Adding link for  " << linkName << "\n";
}

int main() {
  LinkAdd("newLink");
}

If the executable isnt run as root the output is:

Adding link for newLink
Running Command: /bin/ip link add dev newLink type bridge
Process Created: 0
STDOUT

STDERR

Process Joined: 0

While running the same command on the command line gives
RTNETLINK answers: Operation not permitted

I would like to output "RTNETLINK answers: Operation not permitted" or I would like to capture a failure when a command that needs to be run as root is not run as root.

Binary is not found if the full path is not provided when using subprocess_create without environment options

In RHEL 7.3, when using subprocess_create with a 0 for the environment option, if I don't specify the full path of the binary, the call to execvp fails and returns 255.

    const char * command_line[] = { "sleep", "10", NULL };

    struct subprocess_s subprocess;

    int result = subprocess_create(command_line, 0, &subprocess);

    if (0 != result)
    {
        std::cerr << "Error with subprocess" << std::endl;
    }

    int retcode = 0;
    
    result = subprocess_join(&subprocess, &retcode);
    
    if (0 != result)
    {
        std::cerr << "Error joining subprocess" << std::endl;
    }

    std::cout << "Ret code: " << retcode << std::endl; // This prints 255

However, if I use any environment option, like subprocess_option_inherit_environment, your lib uses execve instead, which does need to have the full path. Here's a better explanation for that behavior.

To my understanding, the behavior should be consistent in both cases.

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.