SmartOS as a Home Router

Posted by Dave Eddy on 31 May 2015 - tags: tech

For the last couple of years I've used PFSense as my home router. It's been great - it's easy to manage with the web interface, and really easy to add features like an OpenVPN server, bandwidth monitoring, etc.

But, I like to manage all of my servers and zones at home using Chef, and my router has always been left out as it required being configured manually through the web interface. So now, I've replaced PFSense with 2 SmartOS zones: 1 for NAT and the other for DHCP - both managed by Chef, and both monitored with Nagios. This also had the added effect of reducing two physical servers at home down to one, for a cheaper power bill.

There are 3 steps required to configure a SmartOS server as a home router replacement.

  1. Add the External Interface
  2. Create the NAT Zone
  3. Create the DHCP Zone

1. Add the External Interface

The first thing is to ensure that you have 2 (or more) NICs on the SmartOS server - one for the external network (The Internet) and one for the internal network that will be behind NAT. To list the interfaces run

root - datadyne sunos ~ # dladm show-phys -m
LINK         SLOT     ADDRESS            INUSE CLIENT
rge0         primary  f4:6d:4:X:XX:XX    yes  rge0
e1000g0      primary  0:4:23:XX:XX:XX    yes  e1000g0

Cross-referencing this information with output from ifconfig(1M) reveals that the e1000g0 interface is not currently in use, and can be used as the external interface.

root - datadyne sunos ~ # ifconfig e1000g0
ifconfig: status: SIOCGLIFFLAGS: e1000g0: no such interface


Sonos and SmartOS - Samba Server

Posted by Dave Eddy on 05 May 2015 - tags: tech

Sonos is a fantastic product that makes it easy to set up whole-house audio in a piecemeal fashion. I started with one speaker, then a couple weeks later was up to 4 speakers, and a couple months after that the whole house is equipped with enough speakers for music to be heard anywhere.

In order to stream your own music collection to Sonos, it must be available over the network using a supported protocol - sadly at the time of this writing NFS is unsupported by Sonos. In order to get Sonos up and running on my network, I setup a cifs/samba read-only share with my music collection inside a SmartOS zone.

This post is basically a modified version of Jonathan Perkin's and Thomas Merkel's posts covering mounting in a shared directory with music using lofs.

create the zone

To create the zone you have to first create a JSON manifest to be used by the vmadm(1M) command. This is the JSON manifest for the cifs zones called cifs.json I used. The filesystems array mounts in /goliath/entertainment from the Global Zone to /entertainment inside the zone. I do this because my entertainment directory is used by multiple zones - for example my plex zone uses it to share my plex library.


djbdns on SmartOS

Posted by Dave Eddy on 27 Apr 2015 - tags: tech

djbdns is a software package for running a secure, fast, and simple DNS server.

djbdns is not actually a program itself, but instead is a collection of programs that can be used together to create a full caching, forwarding, and authoritative DNS system - this post will show how to setup all the necessary programs on SmartOS to mimic my home setup.

I use djbdns at home to give me:

  1. DNS lookups for my internal network: and 10.X.X.X
  2. DNS caching daemon for quick look ups: it forwards to OpenDNS and caches the results


To install the suite of tools, run

pkgin in djbdns gmake

Note: the GNU version of make isn't needed specifically, any implementation will suffice.

This will install a lot of programs, but the most important are:

  • tinydns: a DNS server daemon
  • dnscache: a recursive DNS caching daemon

The next step is to create the configuration directory which will be used later

mkdir -p /opt/local/etc/djbdns


Case Study for Bash and Node - __dirname

Posted by Dave Eddy on 13 Apr 2015 - tags: tech

__dirname is a variable that is available to all scripts that are run by Node.JS - it contains a string that refers to the directory name where the currently executing script is contained. The variable is set on a per-file basis, so any script that is sourced using require will have its own __dirname variable that points to the directory where the script itself is contained.

Example In Node

Take the following node script located at /tmp/node-one


When executed, we can see

$ node /tmp/node-one
$ cp /tmp/node-one /var/tmp/node-one
$ node /var/tmp/node-one
$ cp /tmp/node-one ~/node-one
$ node ~/node-one

And, when using require, given the following script in ~/two.js


When executed, we can see

$ node ~/two.js

Even though ~/two.js resides in /home/dave, it prints /tmp becasue the script being sourced resides in /tmp.

Because of this behavior, it is very easy and elegant for node scripts to require one another without needing to know an absolute path ahead of time. By using only relative require statements, all paths will be made relative to __dirname implicitly.


This behavior however, relies on very specific situations for the script to be executed. Imagine the case where node does not know where the JavaScript bytes are coming from. For example, using the original node-one script above:


WeeChat perl plugin on SmartOS

Posted by Dave Eddy on 30 Mar 2015 - tags: tech

Here's a quick hack to get the perl plugin working for WeeChat on the latest (2014Q4) SmartOS

weechat is now in pkgsrc, but it appears to have been built without perl support, which renders certain plugins not working with errors like:

/script install
script: script "" can not be installed because plugin "perl" is not loaded
/plugin load perl
Error: unable to load plugin "perl": weechat: fatal: perl: open failed: No such file or directory
If you're trying to load a script and not a C plugin, try command to load scripts (/perl, /python, ...)


To quickly work around this:

1 install weechat from pkgsrc

pkgin in weechat

This will install weechat without the perl plugin

2 compile weechat at the same version from source

tar xzf weechat-1.0.tar.gz
cd weechat-1.0
mkdir build
cd build
cmake ../ -DPREFIX=/opt/local

THIS WILL FAIL, version 1.0 does not compile cleanly on SmartOS. However, has been built, which is all we need

3 move the plugin into place

sudo cp ./src/plugins/perl/ /opt/local/lib/weechat/plugins/

4 it works

/plugin load perl will now work as expected


  1. have weechat in pkgsrc built with perl support -
  2. fix weechat build problems on SmartOS -

Human Readable Duration in Bash

Posted by Dave Eddy on 29 Jun 2014 - tags: tech

show seconds in a human-readable form using pure bash

This function provides a simple way to turn a number of seconds into a human readable form using minutes, hours, days, etc. For example.

$ human 50
50 seconds
$ human 600
10 minutes
$ human 75890
21 hours
$ human 475890
5 days
$ echo "bash has been running for $(human "$SECONDS")"
bash has been running for 2 minutes

The Code

human() {
    local seconds=$1
    if ((seconds < 0)); then
        ((seconds *= -1))

    local times=(
    $((seconds / 60 / 60 / 24 / 365)) # years
    $((seconds / 60 / 60 / 24 / 30))  # months
    $((seconds / 60 / 60 / 24 / 7))   # weeks
    $((seconds / 60 / 60 / 24))       # days
    $((seconds / 60 / 60))            # hours
    $((seconds / 60))                 # minutes
    $((seconds))                      # seconds
    local names=(year month week day hour minute second)

    local i
    for ((i = 0; i < ${#names[@]}; i++)); do
        if ((${times[$i]} > 1)); then
            echo "${times[$i]} ${names[$i]}s"
        elif ((${times[$i]} == 1)); then
            echo "${times[$i]} ${names[$i]}"
    echo '0 seconds'

MIT License

Cross-Platform Implementation of which

Posted by Dave Eddy on 22 May 2014 - tags: tech


find the path of a binary in your PATH using only bash

The tool which(1) has many different implementations on many different operating systems. Because of this, its output and return codes are not well-defined, and should not be trusted in the context of a script. However, it is often desirable to determine if an executable exists on a filesystem, without having to fork the executable itself to test.

The algorithm which(1) uses is fairly simple: loop over all paths found in the environmental variable PATH, and test for the existence, and executable bit, of the binary in question.

The function below will search your PATH for the binary name given as the first argument, and print the full path of the first binary found and return 0 if it is successful. Otherwise, it won't print anything, and will return 1.

binfind() {
    local paths path
    IFS=: read -a paths <<< "$PATH"
    for path in "${paths[@]}"; do
        if [[ -x $path ]]; then
            echo "$path"
            return 0
    return 1

Example usage

$ binfind echo
$ binfind clang
$ binfind foobar
$ echo $?

Directory Management with cd

Posted by Dave Eddy on 14 Sep 2013 - tags: tech

You cd around like you normally would, and the directories are pushed into a stack. Use the function s to view the stack of directories, and run s "$num" to cd into the directory listed. Use b to jump back 1 directory.

I was inspired by this article written by Derek Wyatt about directory management in BASH. The code I've written for this accomplishes most of the same tasks, but does so with about 1/3 of the code, as this was written specifically for BASH (no legacy KSH bits) and doesn't implement any of the fancier features for cd.


In the above example I cd around a bit, and then run s to see what the current stack looks like. The current stacks shows all of my previous directories in reverse order (limited to $CD_STACK_MAX entries, which defaults to 15).

The code is on GitHub here

What's Open Now?

Posted by Dave Eddy on 18 Jun 2013 - tags: tech

You sit down to watch the LMN premier of Drew Peterson: Untouchable, when next thing you know, you just watched LMN for the last 6 hours, it is now 4am, and you're hungry. The obvious question is "What's open?", but where do you find the answer?

What's Open has the answer.

What's Open is a free service that uses Google Maps to show what places are around you are currently open.


Skye came up with the idea, and I wrote the code.

You can view the site here

As well as view the source code here

Synchronous File IO in Node.js

Posted by Dave Eddy on 26 Mar 2013 - tags: tech

Does calling fs.writeFileSync trigger a synchronous write to the file system?

If you are familiar with Node.js, or have at least heard of it, you've most likely heard that it uses non-blocking IO, and lets you do work asynchronously. One of the most basic APIs that Node provides is for the file system; With this API, you can read, write, remove, etc. files and do other file system related tasks and modifications.

This API follows a standard pattern of exposing 2 functions for each operation: one for asynchronous work, and the other for synchronous work. For example, if you want to read a file in Node you can do so asynchronously:

var fs = require('fs');
fs.readFile('/etc/passwd', function(err, buf) {

Node will continue executing any javascript code it encounters while reading the file. Once all javascript is done being executed and the file is ready, it will run the anonymous function and print the file contents.