3

tl;dr
Trying to run a service which needs ruby to run. But, Ruby is installed with RVM where the root user can't seem to access it, producting the error /usr/bin/env: ruby: No such file or directory. rvmsudo doesn't work.

Background
I have an init.d script which is supposed to start a unicorn server. I keep the script in the config directory of my rails application and symlink to it from /etc/init.d/busables_unicorn.

$ ls -l /etc/init.d/busables_unicorn
-> lrwxrwxrwx 1 root root   62 2012-01-12 15:02 busables_unicorn -> /home/dtuite/dev/rails/busables/current/config/unicorn_init.sh

This script (which is appended to the bottom) essentially just runs the following command:

$APP_ROOT/bin/unicorn -D -c $APP_ROOT/config/unicorn.rb -E production

where $APP_ROOT is the path to the root of my rails application. Every time that command is executed in that init.d script, it is supposed to do so as the dtuite (my deploy) user. To accomplish that, I call su -c "$CMD" - dtuite rather than just $CMD.

/bin/unicorn is a "binscript" which was generated by Bundler and config/unicorn.rb contains some configuration options which are passed to it.

The unicorn binscript looks like this:

#!/usr/bin/env ruby
#
# This file was generated by Bundler.
#
# The application 'unicorn' is installed as part of a gem, and
# this file is here to facilitate running it.
#

require 'pathname'
ENV['BUNDLE_GEMFILE'] ||= File.expand_path("../../Gemfile",
  Pathname.new(__FILE__).realpath)

require 'rubygems'
require 'bundler/setup'

load Gem.bin_path('unicorn', 'unicorn')

Now, I'm trying to start my unicorn service by running:

sudo service busables_unicorn start

That however produces the error:

/usr/bin/env: ruby: No such file or directory

I believe that this is happening because I'm running the service as the root user but RVM has installed ruby under the dtuite user's home directory and the root user has no access to it.

dtuite@localhost:$ which ruby
-> /home/dtuite/.rvm/rubies/ruby-1.9.3-p0/bin/ruby
dtuite@localhost:$ su
Password: 
root@localhost:$ which ruby
root@localhost:$

Question
What do I need to do to make this work?

My Setup
- ubuntu 11.10
- ruby 1.9.3p0 (2011-10-30 revision 33570) [i686-linux]
- nginx: nginx version: nginx/1.0.5

What I've tried

rvmsudo

$ rvmsudo service busables_unicorn start
/usr/bin/env: ruby: No such file or directory

rvm-auto-ruby

$ sudo service cakes_unicorn start
-> [sudo] password for dtuite: 
-> -su: /home/dtuite/dev/rails/cakes/current/bin/unicorn: rvm-auto-ruby: bad interpreter: No such file or directory

This other question may help but to be honest I don't really understand it.

Appendix
The busables_unicorn script in it's entirety.

# INFO: This file is based on the example found at
# https://github.com/defunkt/unicorn/blob/master/examples/init.sh
# Modifications are courtesy of Ryan Bate's Unicorn Railscast
# Install Instructions:
# sudo ln -s full-path-to-script /etc/init.d/APP_NAME_unicorn
# Once installed, an app's unicorn can be reloaded by running
# sudo service APP_NAME_unicorn restart

#!/bin/sh
set -e
# Example init script, this can be used with nginx, too,
# since nginx and unicorn accept the same signals

# Feel free to change any of the following variables for your app:
TIMEOUT=${TIMEOUT-60}

APP_ROOT=/home/dtuite/dev/rails/busables/current
PID=$APP_ROOT/tmp/pids/unicorn.pid
# in order to access this, we need to first run
# 'bundle install --binstubs'. THis will fill our
# app/bin directory with loads of stubs for executables
# this is the command that is run when we run this script
CMD="$APP_ROOT/bin/unicorn -D -c $APP_ROOT/config/unicorn.rb -E production"
# we don't need an init config because this file does it's job
action="$1"
set -u

old_pid="$PID.oldbin"

cd $APP_ROOT || exit 1

sig () {
    test -s "$PID" && kill -$1 `cat $PID`
}

oldsig () {
    test -s $old_pid && kill -$1 `cat $old_pid`
}

case $action in
start)
    sig 0 && echo >&2 "Already running" && exit 0
  # NOTE: We have to change all these lines.
  # Otherwise, the app will run as the root user
  su -c "$CMD" - dtuite
    ;;
stop)
    sig QUIT && exit 0
    echo >&2 "Not running"
    ;;
force-stop)
    sig TERM && exit 0
    echo >&2 "Not running"
    ;;
restart|reload)
    sig HUP && echo reloaded OK && exit 0
    echo >&2 "Couldn't reload, starting '$CMD' instead"
  su -c "$CMD" - dtuite
    ;;
upgrade)
    if sig USR2 && sleep 2 && sig 0 && oldsig QUIT
    then
        n=$TIMEOUT
        while test -s $old_pid && test $n -ge 0
        do
            printf '.' && sleep 1 && n=$(( $n - 1 ))
        done
        echo

        if test $n -lt 0 && test -s $old_pid
        then
            echo >&2 "$old_pid still exists after $TIMEOUT seconds"
            exit 1
        fi
        exit 0
    fi
    echo >&2 "Couldn't upgrade, starting '$CMD' instead"
  su -c "$CMD" - dtuite
    ;;
reopen-logs)
    sig USR1
    ;;
*)
    echo >&2 "Usage: $0 <start|stop|restart|upgrade|force-stop|reopen-logs>"
    exit 1
    ;;
esac
Community
  • 1
  • 1
David Tuite
  • 22,258
  • 25
  • 106
  • 176
  • If this is a production server, you should NOT be using Ruby based in a user's directory. What happens if that user makes a change? On a production server I'd install the one version of Ruby you need. And, users would not have accounts on it, only administrators. – the Tin Man Jan 12 '12 at 21:29
  • It's a private production server. `dtuite` is me. – David Tuite Jan 12 '12 at 21:49

2 Answers2

2

It sounds like su isn't spawning a shell that reads the profile files that normally setup the rvm environment.

I'd try changing the command you run to

 source "/home/dtuite/.rvm/scripts/rvm" && $APP_ROOT/bin/unicorn...
Frederick Cheung
  • 83,189
  • 8
  • 152
  • 174
1

Try adding your ruby path somewhere at the beginning of the start script, in an export statement like this:

export PATH=/home/dtuite/.rvm/rubies/ruby-1.9.3-p0/bin:$PATH

Andrei S
  • 6,486
  • 5
  • 37
  • 54