UNPKG

masq

Version:

A simple local dns server extracted from Pow

433 lines (319 loc) 20.7 kB
<!DOCTYPE html> <html> <head> <title>utils.coffee</title> <meta http-equiv="content-type" content="text/html; charset=UTF-8"> <meta name="viewport" content="width=device-width, target-densitydpi=160dpi, initial-scale=1.0; maximum-scale=1.0; user-scalable=0;"> <link rel="stylesheet" media="all" href="docco.css" /> </head> <body> <div id="container"> <div id="background"></div> <ul id="jump_to"> <li> <a class="large" href="javascript:void(0);">Jump To &hellip;</a> <a class="small" href="javascript:void(0);">+</a> <div id="jump_wrapper"> <div id="jump_page_wrapper"> <div id="jump_page"> <a class="source" href="command.html"> command.coffee </a> <a class="source" href="configuration.html"> configuration.coffee </a> <a class="source" href="daemon.html"> daemon.coffee </a> <a class="source" href="dns_server.html"> dns_server.coffee </a> <a class="source" href="index.html"> index.coffee </a> <a class="source" href="installer.html"> installer.coffee </a> <a class="source" href="logger.html"> logger.coffee </a> <a class="source" href="utils.html"> utils.coffee </a> </div> </div> </li> </ul> <ul class="sections"> <li id="title"> <div class="annotation"> <h1>utils.coffee</h1> </div> </li> <li id="section-1"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-1">&#182;</a> </div> <p>The <code>util</code> module houses a number of utility functions used throughout Pow.</p> </div> <div class="content"><div class='highlight'><pre> fs = <span class="hljs-built_in">require</span> <span class="hljs-string">"fs"</span> path = <span class="hljs-built_in">require</span> <span class="hljs-string">"path"</span> async = <span class="hljs-built_in">require</span> <span class="hljs-string">"async"</span> {execFile} = <span class="hljs-built_in">require</span> <span class="hljs-string">"child_process"</span> {Stream} = <span class="hljs-built_in">require</span> <span class="hljs-string">"stream"</span></pre></div></div> </li> <li id="section-2"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-2">&#182;</a> </div> <p>Asynchronously and recursively create a directory if it does not already exist. Then invoke the given callback.</p> </div> <div class="content"><div class='highlight'><pre>exports.mkdirp = <span class="hljs-function"><span class="hljs-params">(dirname, callback)</span> -&gt;</span> fs.lstat (p = path.normalize dirname), <span class="hljs-function"><span class="hljs-params">(err, stats)</span> -&gt;</span> <span class="hljs-keyword">if</span> err paths = [p].concat(p = path.dirname p <span class="hljs-keyword">until</span> p <span class="hljs-keyword">in</span> [<span class="hljs-string">"/"</span>, <span class="hljs-string">"."</span>]) async.forEachSeries paths.reverse(), <span class="hljs-function"><span class="hljs-params">(p, next)</span> -&gt;</span> fs.exists p, <span class="hljs-function"><span class="hljs-params">(exists)</span> -&gt;</span> <span class="hljs-keyword">if</span> exists <span class="hljs-keyword">then</span> next() <span class="hljs-keyword">else</span> fs.mkdir p, <span class="hljs-number">0</span>o755, <span class="hljs-function"><span class="hljs-params">(err)</span> -&gt;</span> <span class="hljs-keyword">if</span> err <span class="hljs-keyword">then</span> callback err <span class="hljs-keyword">else</span> next() , callback <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> stats.isDirectory() callback() <span class="hljs-keyword">else</span> callback <span class="hljs-string">"file exists"</span></pre></div></div> </li> <li id="section-3"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-3">&#182;</a> </div> <p>A wrapper around <code>chown(8)</code> for taking ownership of a given path with the specified owner string (such as <code>&quot;root:wheel&quot;</code>). Invokes <code>callback</code> with the error string, if any, and a boolean value indicating whether or not the operation succeeded.</p> </div> <div class="content"><div class='highlight'><pre>exports.chown = <span class="hljs-function"><span class="hljs-params">(path, owner, callback)</span> -&gt;</span> error = <span class="hljs-string">""</span> exec [<span class="hljs-string">"chown"</span>, owner, path], <span class="hljs-function"><span class="hljs-params">(err, stdout, stderr)</span> -&gt;</span> <span class="hljs-keyword">if</span> err <span class="hljs-keyword">then</span> callback err, stderr <span class="hljs-keyword">else</span> callback <span class="hljs-literal">null</span></pre></div></div> </li> <li id="section-4"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-4">&#182;</a> </div> <p>Spawn a Bash shell with the given <code>env</code> and source the named <code>script</code>. Then collect its resulting environment variables and pass them to <code>callback</code> as the second argument. If the script returns a non-zero exit code, call <code>callback</code> with the error as its first argument, and annotate the error with the captured <code>stdout</code> and <code>stderr</code>.</p> </div> <div class="content"><div class='highlight'><pre>exports.sourceScriptEnv = <span class="hljs-function"><span class="hljs-params">(script, env, options, callback)</span> -&gt;</span> <span class="hljs-keyword">if</span> options.call callback = options options = {} <span class="hljs-keyword">else</span> options ?= {}</pre></div></div> </li> <li id="section-5"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-5">&#182;</a> </div> <p>Build up the command to execute, starting with the <code>before</code> option, if any. Then source the given script, swallowing any output written to stderr. Finally, dump the current environment to a temporary file.</p> </div> <div class="content"><div class='highlight'><pre> cwd = path.dirname script filename = makeTemporaryFilename() command = <span class="hljs-string">""" <span class="hljs-subst">#{options.before ? <span class="hljs-string">"true"</span>}</span> &amp;&amp; source <span class="hljs-subst">#{quote script}</span> &gt; /dev/null &amp;&amp; env &gt; <span class="hljs-subst">#{quote filename}</span> """</span></pre></div></div> </li> <li id="section-6"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-6">&#182;</a> </div> <p>Run our command through Bash in the directory of the script. If an error occurs, rewrite the error to a more descriptive message. Otherwise, read and parse the environment from the temporary file and pass it along to the callback.</p> </div> <div class="content"><div class='highlight'><pre> exec [<span class="hljs-string">"bash"</span>, <span class="hljs-string">"-c"</span>, command], {cwd, env}, <span class="hljs-function"><span class="hljs-params">(err, stdout, stderr)</span> -&gt;</span> <span class="hljs-keyword">if</span> err err.message = <span class="hljs-string">"'<span class="hljs-subst">#{script}</span>' failed to load:\n<span class="hljs-subst">#{command}</span>"</span> err.stdout = stdout err.stderr = stderr callback err <span class="hljs-keyword">else</span> readAndUnlink filename, <span class="hljs-function"><span class="hljs-params">(err, result)</span> -&gt;</span> <span class="hljs-keyword">if</span> err <span class="hljs-keyword">then</span> callback err <span class="hljs-keyword">else</span> callback <span class="hljs-literal">null</span>, parseEnv result</pre></div></div> </li> <li id="section-7"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-7">&#182;</a> </div> <p>Get the user’s login environment by spawning a login shell and collecting its environment variables via the <code>env</code> command. (In case the user’s shell profile script prints output to stdout or stderr, we must redirect <code>env</code> output to a temporary file and read that.)</p> <p>The returned environment will include a default <code>LANG</code> variable if one is not set by the user’s shell. This default value of <code>LANG</code> is determined by joining the user’s current locale with the value of the <code>defaultEncoding</code> parameter, or <code>UTF-8</code> if it is not set.</p> </div> <div class="content"><div class='highlight'><pre>exports.getUserEnv = <span class="hljs-function"><span class="hljs-params">(callback, defaultEncoding = <span class="hljs-string">"UTF-8"</span>)</span> -&gt;</span> filename = makeTemporaryFilename() loginExec <span class="hljs-string">"exec env &gt; <span class="hljs-subst">#{quote filename}</span>"</span>, <span class="hljs-function"><span class="hljs-params">(err)</span> -&gt;</span> <span class="hljs-keyword">if</span> err <span class="hljs-keyword">then</span> callback err <span class="hljs-keyword">else</span> readAndUnlink filename, <span class="hljs-function"><span class="hljs-params">(err, result)</span> -&gt;</span> <span class="hljs-keyword">if</span> err <span class="hljs-keyword">then</span> callback err <span class="hljs-keyword">else</span> getUserLocale (locale) -&gt; env = parseEnv result env.LANG ?= <span class="hljs-string">"<span class="hljs-subst">#{locale}</span>.<span class="hljs-subst">#{defaultEncoding}</span>"</span> callback <span class="hljs-literal">null</span>, env</pre></div></div> </li> <li id="section-8"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-8">&#182;</a> </div> <p>Execute a command without spawning a subshell. The command argument is an array of program name and arguments.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">exec</span> = <span class="hljs-params">(command, options, callback)</span> -&gt;</span> <span class="hljs-keyword">unless</span> callback? callback = options options = {} execFile <span class="hljs-string">"/usr/bin/env"</span>, command, options, callback</pre></div></div> </li> <li id="section-9"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-9">&#182;</a> </div> <p>Single-quote a string for command line execution.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">quote</span> = <span class="hljs-params">(string)</span> -&gt;</span> <span class="hljs-string">"'"</span> + string.replace(<span class="hljs-regexp">/\'/g</span>, <span class="hljs-string">"'\\''"</span>) + <span class="hljs-string">"'"</span></pre></div></div> </li> <li id="section-10"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-10">&#182;</a> </div> <p>Generate and return a unique temporary filename based on the current process’s PID, the number of milliseconds elapsed since the UNIX epoch, and a random integer.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">makeTemporaryFilename</span> = -&gt;</span> tmpdir = process.env.TMPDIR ? <span class="hljs-string">"/tmp"</span> timestamp = <span class="hljs-keyword">new</span> Date().getTime() random = parseInt Math.random() * Math.pow(<span class="hljs-number">2</span>, <span class="hljs-number">16</span>) filename = <span class="hljs-string">"pow.<span class="hljs-subst">#{process.pid}</span>.<span class="hljs-subst">#{timestamp}</span>.<span class="hljs-subst">#{random}</span>"</span> path.join tmpdir, filename</pre></div></div> </li> <li id="section-11"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-11">&#182;</a> </div> <p>Read the contents of a file, unlink the file, then invoke the callback with the contents of the file.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">readAndUnlink</span> = <span class="hljs-params">(filename, callback)</span> -&gt;</span> fs.readFile filename, <span class="hljs-string">"utf8"</span>, <span class="hljs-function"><span class="hljs-params">(err, contents)</span> -&gt;</span> <span class="hljs-keyword">if</span> err <span class="hljs-keyword">then</span> callback err <span class="hljs-keyword">else</span> fs.unlink filename, <span class="hljs-function"><span class="hljs-params">(err)</span> -&gt;</span> <span class="hljs-keyword">if</span> err <span class="hljs-keyword">then</span> callback err <span class="hljs-keyword">else</span> callback <span class="hljs-literal">null</span>, contents</pre></div></div> </li> <li id="section-12"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-12">&#182;</a> </div> <p>Execute the given command through a login shell and pass the contents of its stdout and stderr streams to the callback. In order to spawn a login shell, first spawn the user’s shell with the <code>-l</code> option. If that fails, retry without <code>-l</code>; some shells, like tcsh, cannot be started as non-interactive login shells. If that fails, bubble the error up to the callback.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">loginExec</span> = <span class="hljs-params">(command, callback)</span> -&gt;</span> getUserShell (shell) -&gt; login = [<span class="hljs-string">"login"</span>, <span class="hljs-string">"-qf"</span>, process.env.LOGNAME, shell] exec [login..., <span class="hljs-string">"-i"</span>, <span class="hljs-string">"-c"</span>, command], <span class="hljs-function"><span class="hljs-params">(err, stdout, stderr)</span> -&gt;</span> <span class="hljs-keyword">if</span> err exec [login..., <span class="hljs-string">"-c"</span>, command], callback <span class="hljs-keyword">else</span> callback <span class="hljs-literal">null</span>, stdout, stderr</pre></div></div> </li> <li id="section-13"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-13">&#182;</a> </div> <p>Invoke <code>dscl(1)</code> to find out what shell the user prefers. We cannot rely on <code>process.env.SHELL</code> because it always seems to be <code>/bin/bash</code> when spawned from <code>launchctl</code>, regardless of what the user has set.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">getUserShell</span> = <span class="hljs-params">(callback)</span> -&gt;</span> command = [<span class="hljs-string">"dscl"</span>, <span class="hljs-string">"."</span>, <span class="hljs-string">"-read"</span>, <span class="hljs-string">"/Users/<span class="hljs-subst">#{process.env.LOGNAME}</span>"</span>, <span class="hljs-string">"UserShell"</span>] exec command, <span class="hljs-function"><span class="hljs-params">(err, stdout, stderr)</span> -&gt;</span> <span class="hljs-keyword">if</span> err callback process.env.SHELL <span class="hljs-keyword">else</span> <span class="hljs-keyword">if</span> matches = stdout.trim().match <span class="hljs-regexp">/^UserShell: (.+)$/</span> [match, shell] = matches callback shell <span class="hljs-keyword">else</span> callback process.env.SHELL</pre></div></div> </li> <li id="section-14"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-14">&#182;</a> </div> <p>Read the user’s current locale preference from the OS X defaults database. Fall back to <code>en_US</code> if it can’t be determined.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">getUserLocale</span> = <span class="hljs-params">(callback)</span> -&gt;</span> exec [<span class="hljs-string">"defaults"</span>, <span class="hljs-string">"read"</span>, <span class="hljs-string">"-g"</span>, <span class="hljs-string">"AppleLocale"</span>], <span class="hljs-function"><span class="hljs-params">(err, stdout, stderr)</span> -&gt;</span> locale = stdout?.trim() ? <span class="hljs-string">""</span> locale = <span class="hljs-string">"en_US"</span> <span class="hljs-keyword">unless</span> locale.match <span class="hljs-regexp">/^\w+$/</span> callback locale</pre></div></div> </li> <li id="section-15"> <div class="annotation"> <div class="pilwrap "> <a class="pilcrow" href="#section-15">&#182;</a> </div> <p>Parse the output of the <code>env</code> command into a JavaScript object.</p> </div> <div class="content"><div class='highlight'><pre><span class="hljs-function"><span class="hljs-title">parseEnv</span> = <span class="hljs-params">(stdout)</span> -&gt;</span> env = {} <span class="hljs-keyword">for</span> line <span class="hljs-keyword">in</span> stdout.split <span class="hljs-string">"\n"</span> <span class="hljs-keyword">if</span> matches = line.match <span class="hljs-regexp">/([^=]+)=(.+)/</span> [match, name, value] = matches env[name] = value env</pre></div></div> </li> </ul> </div> </body> </html>