Skip to content

Commit

Permalink
Spawn the user's preferred shell and collect its environment variables
Browse files Browse the repository at this point in the history
  • Loading branch information
sstephenson committed Nov 14, 2011
1 parent f1dc0a8 commit e2247d7
Show file tree
Hide file tree
Showing 4 changed files with 122 additions and 21 deletions.
21 changes: 15 additions & 6 deletions lib/configuration.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

58 changes: 52 additions & 6 deletions lib/util.js

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

13 changes: 9 additions & 4 deletions src/configuration.coffee
Expand Up @@ -9,6 +9,7 @@ async = require "async"
Logger = require "./logger"
{mkdirp} = require "./util"
{sourceScriptEnv} = require "./util"
{getUserEnv} = require "./util"

{env} = process

Expand All @@ -29,11 +30,15 @@ module.exports = class Configuration
# script errors are passed along in the first argument. (No error
# occurs if the file does not exist.)
@loadUserConfigurationEnvironment: (callback) ->
path.exists p = @userConfigurationPath, (exists) ->
if exists
sourceScriptEnv p, {}, callback
getUserEnv (err, env) =>
if err
callback err
else
callback null, {}
path.exists p = @userConfigurationPath, (exists) ->
if exists
sourceScriptEnv p, env, callback
else
callback null, env

# Creates a Configuration object after evaluating the user
# configuration file. Any environment variables in `~/.powconfig`
Expand Down
51 changes: 46 additions & 5 deletions src/util.coffee
Expand Up @@ -118,7 +118,7 @@ exports.sourceScriptEnv = (script, env, options, callback) ->
command = """
#{options.before};
source '#{script}' > /dev/null;
'#{process.execPath}' -e 'console.log(JSON.stringify(process.env)); ""'
env
"""

exec command, cwd: path.dirname(script), env: env, (err, stdout, stderr) ->
Expand All @@ -128,7 +128,48 @@ exports.sourceScriptEnv = (script, env, options, callback) ->
err.stderr = stderr
callback err

try
callback null, JSON.parse stdout
catch exception
callback exception
callback null, parseEnv stdout

# Get the user's login environment by spawning a login shell and
# collecting its environment variables via the `env` command. First
# spawn the user's shell with the `-l` option. If that fails, retry
# without `-l`; some shells, like tcsh, cannot be started as
# non-interactive login shells. If that fails, bubble the error up to
# the callback. Otherwise, parse the output of `env` into a JavaScript
# object and pass it to the callback.
exports.getUserEnv = (callback) ->
getUserShell (shell) ->
exec "#{shell} -l -c env", (err, stdout, stderr) ->
if err
exec "#{shell} -c env", (err, stdout, stderr) ->
if err
callback err
else
callback null, parseEnv stdout
else
callback null, parseEnv stdout

# Invoke `dscl(1)` to find out what shell the user prefers. We cannot
# rely on `process.env.SHELL` because it always seems to be
# `/bin/bash` when spawned from `launchctl`, regardless of what the
# user has set.
getUserShell = (callback) ->
command = "dscl . -read '/Users/#{process.env.LOGNAME}' UserShell"
exec command, (err, stdout, stderr) ->
if err
callback process.env.SHELL
else
if matches = stdout.trim().match /^UserShell: (.+)$/
[match, shell] = matches
callback shell
else
callback process.env.SHELL

# Parse the output of the `env` command into a JavaScript object.
parseEnv = (stdout) ->
env = {}
for line in stdout.split "\n"
if matches = line.match /([^=]+)=(.+)/
[match, name, value] = matches
env[name] = value
env

0 comments on commit e2247d7

Please sign in to comment.