Detecting The Sudo User In Phing

I use Phing for a lot of different tasks, it helps me to automate things that I would otherwise mess up if left to my own devices. Prime candidates for Phing scripts are things that I don't do that much and forget how to do them, or that have a number of complex steps. The only problem I have found is that because many of the Phing scripts I create rely on system changes (eg, configuring an Apache server) they therefore require system changing privileges. Normally I would just prefix the Phing command with sudo, but every now and then I forget all about that step and the build fails. This can be dangerous as I am then left with a build that failed, which might leave a system partly configured or even take a server offline.

This got me to thinking about what Phing could do to remind me if I forget to use sudo on my sudo requiring scripts. There must a better way of reminding myself that I should be running the script as sudo rather than just having the build fail on me. After looking through the Phing documentation and a bit of searching online I found nothing that does this.

The @phingofficial channel on Twitter (who are awesome by the way) suggested that I could use the output of 'whoami' or 'id -u' to test if a user was running as sudo. This is quite easy to do using the <exec> task, which I can use to set a property. Here are a couple of examples.

The following sets the property 'currentuser' to be the output of the 'whoami' command. If the script is being run as sudo then this will be 'root' otherwise it will be the username of the current user.

<exec command="whoami" outputProperty="currentuser" />

The following sets the property 'currentuser' to be the output of the 'id -u' command. If the script is being run as sudo then this will be '0', otherwise it will be the ID of the current user (1000 on my localhost).

<exec command="id -u" outputProperty="currentuser" />

If you are on Windows then you might be able to use the following command, which will produce an access denied message if the user is not an administrator. If you have any other suggestions on how to detect the administrator user on Windows via the command line (or even through PHP) then I'd be happy to hear from you.

With that in place I can use the property to create a boolean value that I can use later in the script using the <condition> task. Assuming that I use the 'whoami' command from the above examples I can do the following.

<!-- Create the currentuser property -->
<exec command="whoami" outputProperty="currentuser" />

<!-- Set the 'issudo' property to be true if the user is root -->
<condition property="issudo">
  <equals arg1="${currentuser}" arg2="root" />
</condition>

<!-- Fail the build if the user is not root -->
<if>
  <not>
    <equals arg1="${issudo}" arg2="true" />
  </not>
  <then>
    <fail message="You must run this build as sudo!" />
  </then>
</if>

Alternatively, I could just a single <if> task to do the same sort of check.

<!-- Fail the build if the user is not root -->
<if>
  <not>
    <equals arg1="${currentuser}" arg2="root" />
  </not>
  <then>
    <fail message="You must run this build as sudo!" />
  </then>
</if>

The <fail> task here will just stop all execution of the script and show that the build has failed to the user, along with the message text.

I could have left if there, but I wanted to take it further by creating a custom Phing task that will then create a property called 'issudo' that will tell me if the user has used sudo to run the build script. This involves creating a PHP class that checks the sudo status using PHP, which should therefore work on any platform. To integrate with Phing you just need to create a class that extends Task and implements a method called main(). The main() method is executed when the object is used so it is within this method that the work needs to be done to check the user and create the property.

To find out if a user has run a PHP script using sudo is quite easy on Linux hosts. The $_SERVER array will contain a few elements that are to do with who the origin user was and what command was run when used on the command line. If we look for the presence of SUDO_COMMAND within the $_SERVER array then we can be pretty sure that the user is running the script as a sudo user. Again, if you have more information on how this works on Windows I'd be happy to hear from you.

To set a property that can be used in the Phing build file the $this->project->setProperty() method needs to be called. This will create a property with a given name and takes two parameters.

  • $name : The name of property to set.
  • $value : The value of the property. If the property name already exists then this will overrite the previous value.

The following class was placed into the same directory as my build script with the name of IsSudo.php. The call to log() just prints out a message to the terminal so that we can see that the class has been run.

/**
 * Sets a phing property called 'issudo' that can be used to see if the user has root privilages.
 *
 */
class IsSudo extends Task {

  /**
   * Set the property.
   */
  public function main() {
    $issudo = false;
    
    $this->log('Determining sudo.');

    if (isset($_SERVER['SUDO_COMMAND'])) {
      $issudo = true; 
    }
    
    $this->project->setProperty('issudo', $issudo);
  }
}

The final step here is to tell Phing that we have a class that we want to be used. This is done using the <taskdef> task. In it's simplest terms this task takes two parameters of the name of the task we want to create and the name of the class that contains the code behind the task. The following code creates a task called <issudo>, which can be found in the class IsSudo.

This allows us to use the custom issudo task that creates a property called ${issudo}, which can then be used to see if the user has run the script as sudo.

<!-- Create issudo property -->
<issudo />

<!-- Check issudo -->
<if>
  <not>
    <istrue value="${issudo}" />
  </not>
  <then>
    <fail message="You must run this build as sudo!" />
  </then>
</if>

This works as intended and will stop the execution of the build file if I've forgotten to run it via sudo.

I did think that it might be possible to integrate with the comparison operators within Phing and create an element that works in much the same way as the <istrue> or <isset> tasks. After some investigation I don't think there is a way of doing this without altering the Phing source code.

Update : @Beryllium9 on twitter suggested that a good way of forcing a sudo check is to create a single sudo target and then require that as a prerequisite to running any sudo requiring targets. The way to do this is to use the depends property of the target element. Adding a depends attribute to the target element will make Phing run the named target first before running the current target. This is the case even if the default target is being called.

As an example the following build file will only run the main target if it has been run using sudo.

<?xml version="1.0"?>
<project name="sudotest" default="main">

  <target name="sudotest">
    <!-- Create the currentuser property -->
    <exec command="whoami" outputProperty="currentuser" />

    <!-- Set the 'issudo' property to be true if the user is root -->
    <condition property="issudo">
      <equals arg1="${currentuser}" arg2="root" />
    </condition>

    <!-- Fail the build if the user is not root -->
    <if>
      <not>
        <equals arg1="${issudo}" arg2="true" />
      </not>
      <then>
        <fail message="You must run this build as sudo!" />
      </then>
    </if>
  </target>

  <target name="main" depends="sudotest">
    <echo>Running main target</echo>
  </target>
</project>

 

Add new comment

The content of this field is kept private and will not be shown publicly.
CAPTCHA
8 + 0 =
Solve this simple math problem and enter the result. E.g. for 1+3, enter 4.
This question is for testing whether or not you are a human visitor and to prevent automated spam submissions.