Description

Author: Goodevilgenius

With this script, you can run remote commands on your computer from any machine using Dropbox. I use this with my Ubuntu box, but it should work on any POSIX compatible machine (any Linux, or UNIX variant, probably Cygwin) with cron (or similar).

Setup

Create a set of directories in your Dropbox like this: Dropbox/remote/ Dropbox/remote/commands/ Dropbox/remote/output/ Dropbox/remote/old/ Then save the following to a file somewhere on your computer:

#!/bin/bash

REMOTEFOLD="${HOME}/Dropbox/remote"
[ -n "$1" ] && REMOTEFOLD="${HOME}/Dropbox/remote_$1"
COMFOLD="${REMOTEFOLD}/commands"
OUTFOLD="${REMOTEFOLD}/output"
OLDFOLD="${REMOTEFOLD}/old"
PRINTFOLD="${REMOTEFOLD}/toprint"
PRINTEDFOLD="${REMOTEFOLD}/printed"

pushd "$COMFOLD" >/dev/null
for i in *
do
    if [ -f "$i" -a ! -e "$i.lock" -a "${i%%.lock}" == "$i" ]
    then
        chmod +x "$i"
        touch "$i.lock"
        echo "==== Start: `date` ====" >> "${OUTFOLD}/${i}.log"
		if [ "${i#at-}" != "$i" ]
		then
			at -f "$i" "${i#at-}" >> "${OUTFOLD}/${i}.log.txt"
		else
			"./${i}" 2>&1 >> "${OUTFOLD}/${i}.log"
		fi
        echo "===== End: `date` =====" >> "${OUTFOLD}/${i}.log"
        echo >> "${OUTFOLD}/${i}.log"
		mv -t "${OLDFOLD}" "$i"
        rm "$i.lock"
    fi
done
popd >/dev/null

pushd "$PRINTFOLD" >/dev/null
for i in *.pdf
do
    if [ -f "$i" -a ! -e "$i.lock" -a "${i%%.lock}" == "$i" ]
    then
        touch "$i.lock"
        echo "=== Printing: `date` ==" >> "${OUTFOLD}/${i}.log"
        ps2pdf "$i" - | lp >> "${OUTFOLD}/${i}.log" 2>&1
        echo "= Print done: `date` ==" >> "${OUTFOLD}/${i}.log"
        mv -t "${PRINTEDFOLD}" "$i"
        rm "$i.lock"
    fi
done
for i in *.ps
do
    if [ -f "$i" -a ! -e "$i.lock" -a "${i%%.lock}" == "$i" ]
    then
        touch "$i.lock"
        echo "=== Printing: `date` ==" >> "${OUTFOLD}/${i}.log"
        lp "$i" >> "${OUTFOLD}/${i}.log" 2>&1
        echo "= Print done: `date` ==" >> "${OUTFOLD}/${i}.log"
        mv -t "${PRINTEDFOLD}" "$i"
        rm "$i.lock"
    fi
done
for i in *.jpg *.gif *.png *.bmp *.tif *.tiff
do
    if [ -f "$i" -a ! -e "$i.lock" -a "${i%%.lock}" == "$i" ]
    then
        touch "$i.lock"
        echo "=== Printing: `date` ==" >> "${OUTFOLD}/${i}.log"
        convert "$i" pdf:- | pdf2ps - - | lp >> "${OUTFOLD}/${i}.log" 2>&1
        echo "= Print done: `date` ==" >> "${OUTFOLD}/${i}.log"
        mv -t "${PRINTEDFOLD}" "$i"
        rm "$i.lock"
    fi
done
popd >/dev/null

This script doesn’t have to go in your Dropbox. It can be anywhere. If you want to avoid any hassle, put it in /usr/bin/, but I keep it elsewhere. Now, set up cron to run this command regularly. I won’t get into details on how to do this. There are plenty of cron tutorials online. I run it every three minutes, but if you’re impatient, you could do it every minute.

Usage

In the Dropbox/remote/commands/ folder, put scripts (or compiled programs) that you want to run on your remote machine. When cron runs the script you saved to your computer, any files saved to the Dropbox/remote/commands/ folder will be run. Their output will be redirected to a file in the Dropbox/remote/output/ folder. If the script being run is called helloWorld.py, the output would go to Dropbox/remote/output/helloWorld.py.log. When each script in the Dropbox/remote/commands/ folder is run, it will be moved to the Dropbox/remote/old/ folder. If you want to rerun the script, simply move it from old, back into commands.

Security Concerns

ANYTHING that can be run (scripts in bash, python, perl, etc., or compiled C/C++ programs, e.g.) on your computer can be run with this method. For this reason, don’t run the script as root. Also, for this reason, don’t share your Dropbox/remote/ folder.

Update

I’ve updated the script since originally writing this tutorial. The current version can be found on Github.

Why limit yourself to individual commands? Get a shell!

I expanded on the above idea to come up with this cool hack that avoids the need to write individual scripts to run. Instead you can get access to an interactive (almost) bash shell running on the remote computer. Just type in commands like you normally would and get a response back. Somewhat slow of course (due to having to wait for Dropbox to send the command and output files back and forth), but it works!

Setup

Create these directories in your Dropbox folder: Dropbox/remote/ Dropbox/remote/output/ Then save the following script to your computer. I call it clsh – for Cloud Shell :-)

#!/bin/bash
cmdDir=~/Dropbox/remote
cmdFile=$cmdDir/in
logFile=$cmdDir/output/out

server()
{
    > $logFile

    # Wait for command file to show up and send it to running bash
    inotifywait -m -q -e close_write,moved_to --format "%w%f" $cmdDir | while read file
    do
        [[ $file = $cmdFile ]] || continue
        cat $file
    done | /bin/bash -i >> $logFile 2>&1
}

client()
{
    local lastSize=$(stat -c %s $logFile)
    local size=$lastSize
    local wheel=("|" "/" "-" "\\\\")  # Spinning wheel
    # Force a prompt at start to confirm connection
    local line=": $RANDOM"

    while [[ -n $line ]] || read line
    do
        # Send command
        echo "$line" > $cmdFile

        # Wait for output
        echo -n "Waiting...."
        let i=0
        while [[ $size -eq $lastSize ]]
        do
            # Should really use inotifywait here but it doesn't seem to work
            # with the way Dropbox updates $logFile.
            sleep 0.5
            echo -n -e "${wheel[$i]}\b"
            let i=(i+1)%4
            size=$(stat -c %s $logFile)
        done

        # Display output
        if [[ $size -lt $lastSize ]]
        then
            echo "Error; please try again"
        else
            echo -n -e "\r            \r"
            tail -c $((size - lastSize)) $logFile
        fi

        lastSize=$size
        line=
    done
}

if [[ $1 = "-s" ]]
then
    server
else
    client
fi

The script doesn’t need to be run from cron, but it needs to be running on the remote machine as long as you need access to it. Running from cron every few minutes and checking for commands to run is pretty wasteful, so this script uses the more efficient inotify mechanism in Linux. Note that this will most likely not work on other POSIX systems. Basically the script keeps a bash shell running, to which it feeds the commands typed by the user. Output from the bash session is sent back to the user.

Usage

On the remote box, make sure the script is running with the “-s” (for server) option:

clsh -s

On the machine you are currently on, run clsh with no arguments. After a short wait you will get a shell prompt from the remote machine. From there on, it behaves almost like a shell, except for the slow response. It feels like remote login using dialup connections of the good ol’ days!

Limitations

  • Of course don’t even think of running any graphical programs. They simply won’t work.
  • You can’t run any interactive programs like those that ask for a password, or ask you “Are you sure?” before running. They probably could be made to work but I am not sure it makes sense for something so simple.
  • You can’t run any full screen interactive programs either (like vim, emacs or top). Forget it.
  • What you can run are simple commands or programs that don’t need any user input and have simple limited outputs. Anything more and you should really be using ssh, VNC or NX!

Security

The security concerns for the original script apply here as well. This is just a fun hack that I put together to show that it could be done. It certainly has its limited uses, but please don’t think of using it for anything serious. If you need security, use ssh or a VPN.

Share the Knowledge!

Helpful(0) Unhelpful(0)
  • colinsheppard

    Thanks for this!

    I altered your script for OS X and I modified it to allow multiple computers by using an ID.

    Get the script here:
    https://gist.github.com/colinsheppard/9301493

    You need to have fswatch installed:

    brew install fswatch

    If you don’t do homebrew:
    https://github.com/alandipert/fswatch

    Then, on the server:

    clsh -s ID

    where “ID” would be the code to identify the server computer, e.g. “home” or “work”.

    On the client:

    clsh ID command

    The directory structure then includes a subfolder for each ID you intend to use, e.g.:

    ~/Dropbox/remote/
    ~/Dropbox/remote/home/
    ~/Dropbox/remote/home/in/
    ~/Dropbox/remote/home/out/
    ~/Dropbox/remote/work/
    ~/Dropbox/remote/work/in/
    ~/Dropbox/remote/work/out/

  • Andrew

    I like this idea, and as for security concerns, what if you were to add in a signature method? Using a GPG PKI implementation where the public key is on the remote machine, and the private key creates a signature either at the bottom of the shell script or as a second file?

    You add the file(s) to the cloud storage, the remote computer sees them, and verifies the signature against your personal trusted public key, and if they match it runs the command. If the signature doesn’t match or there is none, it moves the file immediately to the old folder without running, and renames it to include something to tell you the file was invalid or maliciously created by a third party user.