Search code examples
processglibstdiovalagio

spawn async process not working with pkexec


My current gksudo command works with Process.spawn_async_with_pipes. however if I switch gksudo with pkexec it does not show the pkexec window and directly completes the command without the prompt and returns nothing.

When I use Process.spawn_command_line_sync with the same pkexec command it brings up the prompt to ask for the password and the commands executes fine and returns result.

My key reason to use pkexec is to use polkit and not prompt the user for subsequent uses of command which requires root permissions.

My method for Process.spawn_async_with_pipes code is shown below.

I need help in how I can make pkexec work as a background process i.e. the prompt should block the gui but once the user provides the password it should return control back to the gui and continue execution in the background. This is exactly what happens with gksudo.

Thanks in advance

This is the async method

public int execute_sync_multiarg_command_pipes(string[] spawn_args) {
        debug("Starting to execute async command: "+string.joinv(" ", spawn_args));
        spawn_async_with_pipes_output.erase(0, -1); //clear the output buffer
        MainLoop loop = new MainLoop ();
        try {
            string[] spawn_env = Environ.get();
            Pid child_pid;

            int standard_input;
            int standard_output;
            int standard_error;

            Process.spawn_async_with_pipes ("/",
                spawn_args,
                spawn_env,
                SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
                null,
                out child_pid,
                out standard_input,
                out standard_output,
                out standard_error);

            // capture stdout:
            IOChannel output = new IOChannel.unix_new (standard_output);
            output.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
                return process_line (channel, condition, "stdout");
            });

            // capture stderr:
            IOChannel error = new IOChannel.unix_new (standard_error);
            error.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
                return process_line (channel, condition, "stderr");
            });

            ChildWatch.add (child_pid, (pid, status) => {
                // Triggered when the child indicated by child_pid exits
                Process.close_pid (pid);
                loop.quit ();
            });
            loop.run ();
        } catch(SpawnError e) {
            warning("Failure in executing async command ["+string.joinv(" ", spawn_args)+"] : "+e.message);
            spawn_async_with_pipes_output.append(e.message);
        }
        debug("Completed executing async command["+string.joinv(" ", spawn_args)+"]...");
        return 0;
    }`

This is how it is being called:

execute_sync_multiarg_command_pipes({"pkexec", COMMAND_USING_SUDO[6]});

Solution

  • Spawning an async pkexec command works:

    public static int main (string[] args) {
        MainLoop loop = new MainLoop ();
        try {
            string[] spawn_args = {"pkexec", "ls", "-l", "-h"};
            string[] spawn_env = Environ.get ();
            Pid child_pid;
    
            Process.spawn_async ("/",
                spawn_args,
                spawn_env,
                SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
                null,
                out child_pid);
    
            ChildWatch.add (child_pid, (pid, status) => {
                // Triggered when the child indicated by child_pid exits
                Process.close_pid (pid);
                loop.quit ();
            });
    
            loop.run ();
        } catch (SpawnError e) {
            stdout.printf ("Error: %s\n", e.message);
        }
        return 0;
    }
    

    The prompt comes up and the resulting file listing is from my "/root" directory like expected.

    So the problem must be somewhere else.

    It also works with pipes:

    private static bool process_line (IOChannel channel, IOCondition condition, string stream_name) {
        if (condition == IOCondition.HUP) {
            stdout.printf ("%s: The fd has been closed.\n", stream_name);
            return false;
        }
    
        try {
            string line;
            channel.read_line (out line, null, null);
            stdout.printf ("%s: %s", stream_name, line);
        } catch (IOChannelError e) {
            stdout.printf ("%s: IOChannelError: %s\n", stream_name, e.message);
            return false;
        } catch (ConvertError e) {
            stdout.printf ("%s: ConvertError: %s\n", stream_name, e.message);
            return false;
        }
    
        return true;
    }
    
    public static int main (string[] args) {
        MainLoop loop = new MainLoop ();
        try {
            string[] spawn_args = {"pkexec", "ls", "-l", "-h"};
            string[] spawn_env = Environ.get ();
            Pid child_pid;
    
            int standard_input;
            int standard_output;
            int standard_error;
    
            Process.spawn_async_with_pipes ("/",
                spawn_args,
                spawn_env,
                SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
                null,
                out child_pid,
                out standard_input,
                out standard_output,
                out standard_error);
    
            // stdout:
            IOChannel output = new IOChannel.unix_new (standard_output);
            output.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
                return process_line (channel, condition, "stdout");
            });
    
            // stderr:
            IOChannel error = new IOChannel.unix_new (standard_error);
            error.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
                return process_line (channel, condition, "stderr");
            });
    
            ChildWatch.add (child_pid, (pid, status) => {
                // Triggered when the child indicated by child_pid exits
                Process.close_pid (pid);
                loop.quit ();
            });
    
            loop.run ();
        } catch (SpawnError e) {
            stdout.printf ("Error: %s\n", e.message);
        }
        return 0;
    }
    

    Even this (which now includes your example code fragment) works:

    StringBuilder spawn_async_with_pipes_output;
    
    private static bool process_line (IOChannel channel, IOCondition condition, string stream_name) {
        if (condition == IOCondition.HUP) {
            stdout.printf ("%s: The fd has been closed.\n", stream_name);
            return false;
        }
    
        try {
            string line;
            channel.read_line (out line, null, null);
            stdout.printf ("%s: %s", stream_name, line);
        } catch (IOChannelError e) {
            stdout.printf ("%s: IOChannelError: %s\n", stream_name, e.message);
            return false;
        } catch (ConvertError e) {
            stdout.printf ("%s: ConvertError: %s\n", stream_name, e.message);
            return false;
        }
    
        return true;
    }
    
    public int execute_sync_multiarg_command_pipes(string[] spawn_args) {
            debug("Starting to execute async command: "+string.joinv(" ", spawn_args));
            spawn_async_with_pipes_output.erase(0, -1); //clear the output buffer
            MainLoop loop = new MainLoop ();
            try {
                string[] spawn_env = Environ.get();
                Pid child_pid;
    
                int standard_input;
                int standard_output;
                int standard_error;
    
                Process.spawn_async_with_pipes ("/",
                    spawn_args,
                    spawn_env,
                    SpawnFlags.SEARCH_PATH | SpawnFlags.DO_NOT_REAP_CHILD,
                    null,
                    out child_pid,
                    out standard_input,
                    out standard_output,
                    out standard_error);
    
                // capture stdout:
                IOChannel output = new IOChannel.unix_new (standard_output);
                output.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
                    return process_line (channel, condition, "stdout");
                });
    
                // capture stderr:
                IOChannel error = new IOChannel.unix_new (standard_error);
                error.add_watch (IOCondition.IN | IOCondition.HUP, (channel, condition) => {
                    return process_line (channel, condition, "stderr");
                });
    
                ChildWatch.add (child_pid, (pid, status) => {
                    // Triggered when the child indicated by child_pid exits
                    Process.close_pid (pid);
                    loop.quit ();
                });
                loop.run ();
            } catch(SpawnError e) {
                warning("Failure in executing async command ["+string.joinv(" ", spawn_args)+"] : "+e.message);
                spawn_async_with_pipes_output.append(e.message);
            }
            debug("Completed executing async command["+string.joinv(" ", spawn_args)+"]...");
            return 0;
        }
    
    public static int main (string[] args) {
        spawn_async_with_pipes_output = new StringBuilder ();
        execute_sync_multiarg_command_pipes ({"pkexec", "ls", "-l", "-h"});
        return 0;
    }