Saturday, 18 May 2013

Vala #7: Process Execution

One of the best things about Linux programming is code-reusability. You can simply call a function in a library or execute a command to get a task done, instead of writing the code from scratch. Since there is a command in Linux for almost every task, it greatly reduces the effort required to develop a program. In this article we will see how Linux commands can be executed as a background process for performing various tasks in a Vala program.

Process.spawn_command_line_sync

public bool spawn_command_line_sync (   string command_line, 
                                        out string standard_output = null, 
                                        out string standard_error = null, 
                                        out int exit_status = null
                                    ) 
                                    throws SpawnError 

The simplest way to execute a process is to call the spawn_command_line_sync function. The first parameter is the command to be executed, the second and third parameters return the data of the StandardOutput and StandardError streams and the fourth parameter returns the exit code. Only the first parameter (the command to execute) is mandatory. We can pass null for the remaining parameters which are optional.

Let's wrap this in a wrapper function to make it easier to use:

Execute a command and get the exit code

public int execute_command_sync (string cmd)
{
    try {
        int exitCode;
        Process.spawn_command_line_sync(cmd, null, null, out exitCode);
        return exitCode;
    }
    catch (Error e){
        log_error (e.message);
        return -1;
    }
}

This function executes a command and returns the exit code. Note that we have wrapped the function call spawn_command_line_sync in a try..catch block since the function is declared to throw an error of type SpawnError. The standard_output and standard_error arguments are null since we are not interested in the output of the command. We are only interested in the exit code which will indicate if the command was executed successfully. Exit code 0 indicates success. Non-zero values indicate errors and warnings.

Examples:

  • Delete a file:
execute_command_sync("rm /path/file");
  • Eject the CD-ROM tray:
execute_command_sync("eject");
  • Turn off the monitor ;). Monitor will turn back on when the mouse is moved or a key is pressed.
execute_command_sync("xset dpms force off");

Since Linux has a command for everything, you can perform an endless number of tasks with this function.

Execute a command and get the output

public string execute_command_sync_get_output (string cmd)
{
    try {
        int exitCode;
        string std_out;
        Process.spawn_command_line_sync(cmd, out std_out, null, out exitCode);
        return std_out;
    }
    catch (Error e){
        log_error (e.message);
        return "";
    }
}

This function executes a command and returns the output of the command as a string.

Examples:

  • Find the process ID of a process (the process ID is useful for killing or freezing the process and changing process priority):
execute_command_sync_get_output("pidof firefox") //Returns the process ID number
  • Get the path of a command:
execute_command_sync_get_output("which firefox");  //Returns /usr/bin/firefox 

Process.spawn_async

The function Process.spawn_command_line_sync is synchronous, i.e. the program will wait for the command to finish before moving to the next line in the code. To execute a process asynchronously (without waiting) we can use the function Process.spawn_async.

public bool spawn_async (   string? working_directory, 
                            string[] argv, 
                            string[]? envp, 
                            SpawnFlags _flags, 
                            SpawnChildSetupFunc? child_setup, 
                            out Pid child_pid
                        ) 
                        throws SpawnError 

The function usage is a little tedious, so let's move to the wrapper function.

Execute a command without waiting

public bool execute_command_async (string[] args)
{
    try {
        Process.spawn_async(null, args, null, SpawnFlags.SEARCH_PATH, null, null);
        return true;
    }
    catch (Error e){
        log_error (e.message);
        return false;
    }
}

This function executes a process and returns without waiting for the process to finish. The name of the process to execute and the argument are passed as an array of strings.

Examples:

  • Shutdown the system after 5 minutes:
execute_command_async(new string[]{"shutdown", "-h", "+5"});

4 comments:

  1. I am trying to find a way of capturing the output of a command-line process in an asynchronous format. This lead me to use Process.spawn_async_with_pipes(). However this seems to block the execution of my application as well. In particular it appears to freeze a confirmation dialog while it waits for the command-line process to finish. For example, I would like to call "dpkg -i" or use "apt-get" and pipe the output to a TextView control in my application as it is streamed. Sort of having an embedded console window. However the output does not show in the TextView control until the process is fully completed and has exited. Is there a solution to grabbing the output in real time?

    ReplyDelete
    Replies
    1. To prevent the application from blocking you need to use threads for reading the standard error and output streams. See this sample code: read_streams.vala

      Two threads are used to read the output and error streams line by line. The lines are saved in 2 global ArrayLists. Now you can call a function periodically for reading the ArrayList and adding it to the TextView. We are using the ArrayList to pass data to the textview since we cannot modify the textview from inside the thread.

      Delete
  2. Tony, your read_sreams.vala looks very promising

    however I don't understand how to call a function periodically for reading the ArrayList and adding it to the TextView.

    Could you add example code?

    ReplyDelete
  3. Good night.

    I'm trying to learn to use the Vala programming to create an application that reads the output of "inxi" command and displays the information in a classified way system (CPU, network, video, etc ...).

    The application would be for Debian, could help with some tips??

    ReplyDelete

If you are reporting an issue and commenting as an anonymous user, please leave your email address so that I can get in touch with you.