async = require "async"
fs = require "fs"
path = require "path"
{mkdirp} = require "./utils"
{chown} = require "./utils"
util = require "util"
The Installer
class, in conjunction with the private
InstallerFile
class, creates and installs local and system
configuration files if they’re missing or out of date. It’s used by
the Pow install script to set up the system for local development.
async = require "async"
fs = require "fs"
path = require "path"
{mkdirp} = require "./utils"
{chown} = require "./utils"
util = require "util"
Import the Eco templates for the /etc/resolver
and launchd
configuration files.
resolverSource = require "./templates/resolver"
firewallSource = require “./templates/cx.masq.firewall.plist”
daemonSource = require "./templates/cx.masq.masqd.plist"
InstallerFile
represents a single file candidate for installation:
a pathname, a string of the file’s source, and optional flags
indicating whether the file needs to be installed as root and what
permission bits it should have.
class InstallerFile
constructor: (@path, source, @root = false, @mode = 0o644) ->
@source = source.trim()
Check to see whether the file actually needs to be installed. If
the file exists on the filesystem with the specified path and
contents, callback
is invoked with false. Otherwise, callback
is invoked with true.
isStale: (callback) ->
fs.exists @path, (exists) =>
if exists
fs.readFile @path, "utf8", (err, contents) =>
if err
callback true
else
callback @source isnt contents.trim()
else
callback true
Create all the parent directories of the file’s path, if
necessary, and then invoke callback
.
vivifyPath: (callback) ->
mkdirp path.dirname(@path), callback
Write the file’s source to disk and invoke callback
.
writeFile: (callback) ->
fs.writeFile @path, @source, "utf8", callback
If the root flag is set for this file, change its ownership to the
root
user and wheel
group. Then invoke callback
.
setOwnership: (callback) ->
if @root
chown @path, "root:wheel", callback
else
callback false
Set permissions on the installed file with chmod
.
setPermissions: (callback) ->
fs.chmod @path, @mode, callback
Install a file asynchronously, first by making its parent directory, then writing it to disk, and finally setting its ownership and permission bits.
install: (callback) ->
async.series [
@vivifyPath.bind(@),
@writeFile.bind(@),
@setOwnership.bind(@),
@setPermissions.bind(@)
], callback
The Installer
class operates on a set of InstallerFile
instances.
It can check to see if any files are stale and whether or not root
access is necessary for installation. It can also install any stale
files asynchronously.
module.exports = class Installer
Factory method that takes a Configuration
instance and returns
an Installer
for system firewall and DNS configuration files.
@getSystemInstaller: (configuration) ->
files = [
new InstallerFile “/Library/LaunchDaemons/cx.masq.firewall.plist”, firewallSource(configuration), true
]
for domain in configuration.domains
files.push new InstallerFile "/etc/resolver/#{domain}",
resolverSource(configuration),
true
new Installer files
Factory method that takes a Configuration
instance and returns
an Installer
for the Pow launchctl
daemon configuration file.
@getLocalInstaller: (configuration) ->
new Installer [
new InstallerFile "#{process.env.HOME}/Library/LaunchAgents/cx.masq.masqd.plist",
daemonSource(configuration)
]
Create an installer for a set of files.
constructor: (@files = []) ->
Invoke callback
with an array of any files that need to be
installed.
getStaleFiles: (callback) ->
async.select @files, (file, proceed) ->
file.isStale proceed
, callback @files
Invoke callback
with a boolean argument indicating whether or
not any files need to be installed as root.
needsRootPrivileges: (callback) ->
@getStaleFiles (files) ->
async.detect files, (file, proceed) ->
proceed file.root
, (result) ->
callback result?
Installs any stale files asynchronously and then invokes
callback
.
install: (callback) ->
@getStaleFiles (files) ->
async.forEach files, (file, proceed) ->
file.install (err) ->
util.puts file.path unless err
proceed err
, callback