/**
 * An AppWorker is responsible for:
 *
 * - Launching a Shiny application with the proper user/group permissions
 * - Ensuring that stderr is written to the specified path
 * - Returning a promise that resolves when the worker process exits
 */

var child_process = require('child_process');
var fs = require('fs');
var path = require('path');
var util = require('util');
var Q = require('q');
var _ = require('underscore');

var rprog = process.env.R || 'R';
var scriptPath = __dirname + '/../../R/SockJSAdapter.R';

function exists_p(path) {
  var defer = Q.defer();
  fs.exists(path, function(exists) {
    defer.resolve(exists);
  });
  return defer.promise;
}

/**
 * Begins launching the worker; returns a promise that resolves when
 * the worker is constructed (doesn't necessarily mean the process has
 * actually begun running though).
 *
 * @param {AppSpec} appSpec - Contains the basic details about the app to
 *   launch
 * @param {Number} listenPort - The port number that the Shiny app should use.
 * @param {String} logFilePath - The file path to write stderr to.
 */
function launchWorker_p(appSpec, listenPort, logFilePath) {
  
  if (!appSpec.runAs)
    return Q.reject(new Error("No user specified"));

  if (!appSpec.appDir)
    return Q.reject(new Error("No app directory specified"));


  return exists_p(appSpec.appDir).then(function(exists) {
    if (!exists)
      throw new Error("App directory does not exist");
    
    // Open the log file asynchronously, then create the worker
    return Q.nfcall(fs.open, logFilePath, 'a', 0666).then(function(logStream) {

      // Create the worker; when it exits (or fails to start), close
      // the logStream.
      var worker = new AppWorker(appSpec, listenPort, logStream);
      worker.getExit_p().fin(function() {
        logStream.end();
      });

      return worker;
    });
  })
};
exports.launchWorker_p = launchWorker_p;

/**
 * Like launchWorker_p, but the promise it returns doesn't resolve until
 * the worker process exits.
 */
function runWorker_p(appSpec, listenPort, logFilePath) {
  return launchWorker_p(appSpec, listenPort, logFilePath).invoke('getExit_p');
};
exports.runWorker_p = runWorker_p;

/**
 * An AppWorker models a single R process that is running a Shiny app.
 *
 * @constructor
 * @param {AppSpec} appSpec - Contains the basic details about the app to
 *   launch
 * @param {Number} listenPort - The port number that the Shiny app should use.
 * @param {Stream} logStream - The stream to dump stderr to.
 */
var AppWorker = function(appSpec, listenPort, logStream) {
  this.$dfEnded = Q.defer();
  var self = this;

  try {
    // Run R
    var args = [
      path.join(__dirname, 'run-as.js'),
      appSpec.runAs,
      rprog,
      "--no-save",
      "-f",
      scriptPath
    ];

    var env = _.clone(process.env);
    env.SHINY_PORT = '' + listenPort;
    env.SHINY_APP = '.';
    if (appSpec.settings.gaTrackingId)
      env.SHINY_GAID = appSpec.settings.gaTrackingId;

    this.$proc = child_process.spawn(process.execPath, args, {
      env: env,
      cwd: appSpec.appDir,
      stdio: ['ignore', 'ignore', logStream]
    });
    this.$proc.on('exit', function(code, signal) {
      self.$dfEnded.resolve({code: code, signal: signal});
    });
  }
  catch (e) {
    this.$dfEnded.reject(e);
  }
};

(function() {

  /**
   * Returns a promise that is resolved when the process exits.
   * If the process terminated normally, code is the final exit
   * code of the process, otherwise null. If the process
   * terminated due to receipt of a signal, signal is the string
   * name of the signal, otherwise null.
   */
  this.getExit_p = function() {
    return this.$dfEnded.promise;
  };

  this.kill = function(signal) {
    this.$proc.kill(signal);
  };

}).call(AppWorker.prototype);

@@ -365,24 +365,52 @@
 re('util');%0A
+var bash = require('bash');%0A
 var Q = requ
@@ -2696,177 +2696,110 @@
     
-path.join(__dirname, 'run-as.js'),%0A      appSpec.runAs,%0A      rprog,%0A      %22--no-save%22,%0A      %22-f%22,%0A      scriptPath%0A    %5D;%0A%0A    var env = _.clone(process.env);
+'-',%0A      appSpec.runAs,%0A      '-c',%0A      'SHINY_APP=' + bash.escape(appSpec.appDir) + ' ' +
 %0A    
-env.
+  '
 SHIN
@@ -2808,59 +2808,50 @@
 PORT
- = '' + listenPort;%0A    env.SHINY_APP = '.';%0A    if
+=' + bash.escape(listenPort) + ' ' +%0A     
  (ap
@@ -2881,20 +2881,20 @@
 ngId
-)
+ ?
 %0A      
-env.
+  '
 SHIN
@@ -2899,19 +2899,33 @@
 INY_GAID
- = 
+=' + bash.escape(
 appSpec.
@@ -2949,116 +2949,165 @@
 ngId
-;%0A%0A    this.$proc = child_process.spawn(process.execPath, args, %7B%0A      env: env,%0A      cwd: appSpec.appDir,
+) + ' ' :%0A        '') +%0A      bash.escape(rprog) + %22 --no-save -f %22 + bash.escape(scriptPath)%0A    %5D;%0A%0A    this.$proc = child_process.spawn('/bin/su', args, %7B
 %0A   

Use su instead of run-as