Blog

the writings of

Forever, node.js, CentOS Service, Unprivileged User

June 23, 2014

How to setup and run a node.js script as an unprivilaged user on CentOS using forever

What I was trying to do

What I wanted to do was to run a simple node.js script, as a daemon/service, on CentOS using forever by nodejitsu — but I wanted to be able to start/stop it as a non-root user without having to use "sudo".

This would allow me to run the following commands, as an unprivileged, non-root user:

service project start
service project stop
service project restart

How I got it to work

Assuming your unprivileged user's name is "brant" and he is trying to run a project named "project" from /home/brant/project, name the following script something like "project", make it executable (chmod +x), and place it in your /etc/init.d directory.


#!/bin/sh
# chkconfig: 2345 85 15
# description: Startup script for project.

# exit on first error
set -e

# user running this script
_user="$(id -u -n)"

# points to the root for forever config
export "FOREVER_ROOT=/home/brant/.forever"

# commands to run on "start" (new line per command)
startup=(
    "/home/brant/project/node_modules/forever/bin/forever --sourceDir /home/brant/project start projectScript.js" 
)

# commands to run on "stop" (new line per command)
stopitems=(
    "/home/brant/project/node_modules/forever/bin/forever stop projectScript.js"
)

# start function
do_start(){
    if [ "$_user" == "brant" ]; then
        echo "USER: IT'S BRANT!"
        for i in "${startup[@]}"
            do
                $i
            done
    else
        echo "USER: NOT BRANT!"
        for i in "${startup[@]}"
            do
                su - brant -c "$i"  
            done
    fi
}

# stop function
do_stop(){
    if [ "$_user" == "brant" ]; then
        echo "USER: IT'S BRANT!"
        for i in "${stopitems[@]}"
            do
                $i
            done
    else
        echo "USER: NOT BRANT!"
        for i in "${stopitems[@]}"
            do
                su - brant -c "$i"              
            done
    fi
}

# Decide what command is being called
case "$1" in
    start)
        echo "Starting Project..."
        do_start
        echo "done."
    ;;
    stop)
        echo "Stoping Project..."
        do_stop
        echo "done."
    ;;
    restart)
        echo "Restarting Project..."
        do_stop
        do_start
        echo "done."
    ;;
    *)
        echo "Usage: project {start|stop|restart}" >&2
        exit 3
    ;;
esac

exit 0

This assumes you've installed forever as a local node module as part of the project (i.e. not in a centralized /usr/local/bin location).

I've made a gist too

Where I was caught up

Essentially, it all came down to this line in the script: export "FOREVER_ROOT=/home/brant/.forever"

As far as I could tell, this was a basically undocumented environment variable which tells forever where your ".forever" directory is located.

This ".forever" directory is automatically created in the home directory whatever user runs the forever script. However, if you run the above script as a service with the unprivileged user without the environment variable set, it seems to think you are running as root and it generates the ".forever" directory in /root/.forever. Thus, when you go to stop the service as said unprivileged user, that unprivileged user doesn't have access to the necessary files/directories to stop the service (such as the pid file).

Setting the environment variable seems to take care of it though.

It also runs as a chkconfig startup script, just chkconfig --add project

blog comments powered by Disqus