Skip to content

Commit

Permalink
Merge pull request #3 from cybertk/on-login-as-root
Browse files Browse the repository at this point in the history
Support run job as root on user login
  • Loading branch information
cybertk committed Jan 20, 2016
2 parents 49420b1 + 8c47e7a commit b8297b2
Show file tree
Hide file tree
Showing 4 changed files with 126 additions and 4 deletions.
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,13 @@ To add a oneshot job `script.sh` to run at next user login time
sudo launchd-oneshot script.sh --on-login
```

To add a oneshot job `script.sh` to run at next user login time with root, and **launchd-oneshot** will pass current login user as `$1` to `script.sh`

```bash
sudo launchd-oneshot script.sh --on-login-as-root
```


## Troubleshooting

logs is written to `/tmp/launchd-oneshot.log`, you can view it with
Expand Down
19 changes: 15 additions & 4 deletions launchd-oneshot
Original file line number Diff line number Diff line change
Expand Up @@ -49,16 +49,17 @@ cleanup_trigger_for_job()
run_job()
{
declare job=$1
declare run_as=$2

local job_id=$(id_for_job "$job")
local launchd_agent="/Library/LaunchDaemons/$job_id.plist"
local trigger_options="/tmp/$job_id.trigger.options"

# Source job options if exists
local run_as=root
local login_user=root
[[ -f "$trigger_options" ]] && . "$trigger_options"

su "$run_as" -c "eval '$JOBS_DIR/$job'"
su "${run_as:-$login_user}" -c "eval '$JOBS_DIR/$job $login_user'"
rc=$?

if [[ "$rc" -eq 0 ]]; then
Expand All @@ -75,6 +76,7 @@ install_agent_for_job()
{
declare job_path=$1
declare should_watch_trigger=$2
declare run_as_root=$3

local job=$(job_for_path "$job_path")
local job_id=$(id_for_job "$job")
Expand Down Expand Up @@ -103,6 +105,9 @@ install_agent_for_job()
$PlistBuddy -c 'Add :RunAtLoad bool true' "$agent_plist"
fi

if [[ -n "$run_as_root" ]]; then
$PlistBuddy -c 'Add :EnvironmentVariables:LAUNCHD_ONESHOT_RUN_JOB_AS_ROOT string 1' "$agent_plist"
fi
chmod 644 "$agent_plist"

# Install jobs to JOBS_DIR
Expand Down Expand Up @@ -164,6 +169,9 @@ install_job()
elif [[ "$job_type" = "--on-login" ]]; then
install_trigger_for_job "$job_path"
install_agent_for_job "$job_path" --watch-trigger
elif [[ "$job_type" = "--on-login-as-root" ]]; then
install_trigger_for_job "$job_path"
install_agent_for_job "$job_path" --watch-trigger --run-as-root
else
echo "unkownn job type: $job_type"
exit 1
Expand All @@ -179,9 +187,12 @@ main()

if [[ -n "$LAUNCHD_ONESHOT_RUN_TRIGGER" ]]; then
local job_options_file=/tmp/$(id_for_job $arg1).options
echo "run_as=`id -un`" > "$job_options_file"
echo "login_user=`id -un`" > "$job_options_file"
elif [[ -n "$LAUNCHD_ONESHOT_RUN_JOB" ]]; then
run_job "$arg1"
local run_as

[[ -n "$LAUNCHD_ONESHOT_RUN_JOB_AS_ROOT" ]] && run_as=root
run_job "$arg1" "$run_as"
else
install_job "$arg1" "$arg2"
fi
Expand Down
101 changes: 101 additions & 0 deletions tests/cli-test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -78,6 +78,8 @@ fixtures
[ -f "$expected_job_signature" ]
# it should run job by root
[ -f "$expected_job_signature.by.root" ]
# it should run job with parameters (root)
[ -f "$expected_job_signature.params.login_user.root" ]
# it should generating log of job
[ -f /usr/local/var/log/launchd-oneshot/$expected_job.log ]
# it should removed agent
Expand Down Expand Up @@ -168,6 +170,105 @@ fixtures
[ -f "$expected_job_signature" ]
# it should run job by current login user
[ -f "$expected_job_signature.by.`id -un`" ]
# it should run job with parameters ($current_login_user)
[ -f "$expected_job_signature.params.login_user.`id -un`" ]
# it should generating log of job
[ -f /usr/local/var/log/launchd-oneshot/$expected_job.log ]
# it should removed agent
[ ! -f /Library/LaunchDaemons/com.cybertk.launchd-oneshot.$expected_job.plist ]
# it should removed trigger agent
[ ! -f /Library/LaunchAgents/com.cybertk.launchd-oneshot.$expected_job.trigger.plist ]
# it should removed installed job
[ ! -f "$JOBS_DIR/$expected_job" ]
# it should removed trigger signal
[ ! -f "/tmp/com.cybertk.launchd-oneshot.$expected_job.trigger.options" ]
}

@test "installing a valid job with --on-login-as-root" {
expected_job=valid.job
expected_job_id=com.cybertk.launchd-oneshot.valid.job
expected_agent=/Library/LaunchDaemons/com.cybertk.launchd-oneshot.$expected_job.plist
expected_trigger_agent=/Library/LaunchAgents/com.cybertk.launchd-oneshot.$expected_job.trigger.plist

run sudo launchd-oneshot "$FIXTURE_DIR/$expected_job" --on-login-as-root
# it should exit 0
[ $status -eq 0 ]
# it should installed job under $JOBS_DIR
[ -f "$JOBS_DIR/$expected_job" ]
# it should installed agent under /Library/LaunchDaemons/
[ -f "$expected_agent" ]
plutil -lint "$expected_agent"
# it should have correct key/values in agent
$PlistBuddy -c 'Print :Label' "$expected_agent" | grep "^com.cybertk.launchd-oneshot.$expected_job$"
$PlistBuddy -c 'Print :ProgramArguments' "$expected_agent" | wc -l | grep "4"
$PlistBuddy -c 'Print :ProgramArguments:0' "$expected_agent" | grep "^$SCRIPT_PATH$"
$PlistBuddy -c 'Print :ProgramArguments:1' "$expected_agent" | grep "^$expected_job$"
$PlistBuddy -c 'Print :EnvironmentVariables:LAUNCHD_ONESHOT_RUN_JOB' "$expected_agent" | grep "^1$"
$PlistBuddy -c 'Print :EnvironmentVariables:LAUNCHD_ONESHOT_RUN_JOB_AS_ROOT' "$expected_agent" | grep "^1$"
$PlistBuddy -c 'Print :KeepAlive:OtherJobEnabled:'"$expected_job_id.trigger" "$expected_agent" | grep "^true$"
$PlistBuddy -c 'Print :RunAtLoad' "$expected_agent" | grep "^false$"
$PlistBuddy -c 'Print :StandardOutPath' "$expected_agent" | grep "^$LOGS_DIR/$expected_job.log$"
$PlistBuddy -c 'Print :StandardErrorPath' "$expected_agent" | grep "^$LOGS_DIR/$expected_job.log$"
# it should installed corresponding trigger agent under /Library/LaunchAgents/
[ -f "$expected_trigger_agent" ]
plutil -lint "$expected_trigger_agent"
# it should have correct key/values in trigger agent
$PlistBuddy -c 'Print :Label' "$expected_trigger_agent" | grep "^com.cybertk.launchd-oneshot.$expected_job.trigger$"
$PlistBuddy -c 'Print :ProgramArguments' "$expected_trigger_agent" | wc -l | grep "4"
$PlistBuddy -c 'Print :ProgramArguments:0' "$expected_trigger_agent" | grep "$SCRIPT_PATH"
$PlistBuddy -c 'Print :ProgramArguments:1' "$expected_trigger_agent" | grep "^$expected_job.trigger$"
$PlistBuddy -c 'Print :EnvironmentVariables:LAUNCHD_ONESHOT_RUN_TRIGGER' "$expected_trigger_agent" | grep '^1$'
$PlistBuddy -c 'Print :RunAtLoad' "$expected_trigger_agent" | grep "^true$"
}

@test "load a valid --on-login-as-root job" {
expected_job=valid.job
expected_job_signature=/tmp/launchd-oneshot-fixtures.valid.job.signature
sudo launchd-oneshot "$FIXTURE_DIR/$expected_job" --on-login-as-root

# when job is loaded
sudo launchctl load /Library/LaunchDaemons/com.cybertk.launchd-oneshot.$expected_job.plist
sleep 1

# it should not run job
[ ! -f "$expected_job_signature" ]
# it should not generating log of job
[ ! -f /usr/local/var/log/launchd-oneshot/$expected_job.log ]
# it should not removed agent
[ -f /Library/LaunchDaemons/com.cybertk.launchd-oneshot.$expected_job.plist ]
# it should not removed installed job
[ -f "$JOBS_DIR/$expected_job" ]
}

@test "load the trigger of a valid --on-login-as-root job" {
expected_job=valid.job
expected_job_signature=/tmp/launchd-oneshot-fixtures.valid.job.signature
sudo launchd-oneshot "$FIXTURE_DIR/$expected_job" --on-login-as-root

# when job is loaded
launchctl load /Library/LaunchAgents/com.cybertk.launchd-oneshot.$expected_job.trigger.plist
sleep 1

# it should created signal
[ -f "/tmp/com.cybertk.launchd-oneshot.$expected_job.trigger.options" ]
}

@test "load the trigger of a valid --on-login-as-root job when job is loaded" {
expected_job=valid.job
expected_job_signature=/tmp/launchd-oneshot-fixtures.valid.job.signature
sudo launchd-oneshot "$FIXTURE_DIR/$expected_job" --on-login-as-root

# when job is loaded
sudo launchctl load /Library/LaunchDaemons/com.cybertk.launchd-oneshot.$expected_job.plist
launchctl load /Library/LaunchAgents/com.cybertk.launchd-oneshot.$expected_job.trigger.plist
sleep 1

# it should run job
[ -f "$expected_job_signature" ]
# it should run job by root
[ -f "$expected_job_signature.by.root" ]
# it should run job with parameters ($current_login_user)
[ -f "$expected_job_signature.params.login_user.`id -un`" ]
# it should generating log of job
[ -f /usr/local/var/log/launchd-oneshot/$expected_job.log ]
# it should removed agent
Expand Down
3 changes: 3 additions & 0 deletions tests/fixtures/valid.job
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
#!/bin/sh

login_user=$1

echo "hello from fixtures/jobs/valid_job"
touch /tmp/launchd-oneshot-fixtures.valid.job.signature
touch /tmp/launchd-oneshot-fixtures.valid.job.signature.by.`id -un`
touch /tmp/launchd-oneshot-fixtures.valid.job.signature.params.login_user.$login_user

0 comments on commit b8297b2

Please sign in to comment.