Just one geek's opinions and epiphanies

Reading Goal 2017

Reading Goal 2017

I've decided to make a public reading goal this year. I'm committing myself to reading 12 books that are unrelated to my work. You can follow my progress here and at Goodreads.

Currently I'm reading The Ghost Brigades by John Scalzi.

Syncthing & Systemd: For Fun

So a while ago I heard about Syncthing on the GoTime.FM Podcast, and I decided to finally get Syncthing running on my local systems.


Syncthing replaces proprietary sync and cloud services with something open, trustworthy and decentralized. Your data is your data alone and you deserve to choose where it is stored, if it is shared with some third party and how it's transmitted over the Internet.

The above quote is from Syncthing's site. Syncthing is an Open Source software that syncs things across the internet. No matter where your machines are, as long as they are connectable. They even have a version for Android available at Google Play, or on F-Droid.

How I'm using Syncthing

So my new job has me working as a road warrior. This means I have a laptop that I use a lot for work while I am remote. However, my machine at home (nicknamed: Beast) has better processing power, more memory, and as a result performs much better than the laptop.

The first thought I had was, how can I make the process of working on code less painful by getting code from my laptop to Beast and back? Syncthing to the rescue!

Setting up Syncthing

First you have to get Syncthing installed. This is easy as almost all OSes and distros are supported. I found that Fedora isn't supported directly, so I just grabbed the tarball and extracted it in /usr/local/syncthing

# Untar tarball to ~/.local
$ sudo tar xvzf ~/Downloads/syncthing.tar.gz -C /usr/local/

To make executing Syncthing easy, you can symlink the executable into your path

$ sudo symlink /usr/local/syncthing/syncthing /usr/local/bin/syncthing

When you successfully run Syncthing a browser will open with your new running instance of Syncthing!

Introduce your Devices

So once you have it running you have to establish Devices. What do you want to sync between, basically. I am syncing between Beast, Lenovo, and OPO (my Android based phone).

To do this you start Syncthing on each of the devices, and then share the device ID from each node. A hint here, if you use your Android as the Introducer you save yourself some work.

Sync your Things!

Finally you are ready to sync things. From the machine where the things exist now, you add a Folder and tell it what to share (the actual path) and then which devices to share it to. There are advanced options for timing, and master/normal sharing. I won't cover those but they are available.


Now, having Syncthing running when you fire up the executable is great, but wouldn't it be nice if Syncthing just started and did it's work each time you boot your machine?

No problem, just create a systemd .service and then enable it. Here is how I did mine.

# Create a group and user for Syncthing
$ sudo groupadd syncthing
$ sudo useradd -g syncthing syncthing

# Create .service file
vim /etc/systemd/system/syncthing@syncthing.service  
# systemd service file for Syncthing
Description=Syncthing - Open Source Continuous File Synchronization for %I  

ExecStart=/usr/local/bin/syncthing -no-browser -no-restart -logflags=0  
SuccessExitStatus=3 4  
RestartForceExitStatus=3 4


Finally enable Syncthing at startup

# Enable and Start Syncthing
$ sudo systemctl enable syncthing@syncthing.service
$ sudo systemctl start syncthing@syncthing.service

You now have Syncthing running at boot, and in the background.


If you happen to run into problems with starting Syncthing via systemd, you might want to test if SELinux is the problem.

# Check audit log for syncthing
$ sudo cat /etc/log/audit/audit.log | grep syncthing
# Temporarily disable SELinux
$ sudo setenforce 0

If you find an error or disabling SELinux allows Syncthing to run then you need to adjust the labels of the executable (most likely). However, that is out of scope for this article.

Late but worth the wait...

I have finally updated my blog to Ghost 0.9.0, but I have to be honest, I think this might be the last version of my blog on Ghost. Now that I am working with Go much more, I am giving some serious consideration to using Hugo to generate a static build site. Not that my server is over burdened by the traffic I am getting, but the static site, for obvious reasons seems like a good way to go. Who knows?

Onward to the future!

Broadcast Communication in Golang

Today I set out to understand a few different ways of running an infinite loop with controlled stopping. Perhaps better known as a Daemon. Ok, daemons have lots of other features, but the gist is this... it's a loop, that accepts signals, and reacts upon them accordingly.

To that end, I came up with the code below for running a Daemon in Golang, that responds to UNIX Signals, and has multiple workers listening to the same channel and reacting in sync.

Note: The main function also alerts the works when to stop.

First, take note I called what I use a channel, that isn't 100% true. Yes, I use some channels, but the workers are actually listening to a sync.Cond, and reacting to the sync.Cond.Broadcast() method. You'll also note that I extended the sync.Cond struct into MySyncCond and added Payload and Kill fields. These allow me to not only wake the workers, but also tell them important information.

Hopefully my inline comment and docblocs do this justice, if not please feel free to reach out to me.

package main

import (

// Extend the sync.Cond struct to add Kill, and Payload
type MySyncCond struct {
    Kill    bool
    Payload int

// Add an Init() and set Kill to false (declaritive)
func (m *MySyncCond) Init() {
    m.Kill = false

// Extend sync.Cond.Broadcast() to allow a payload
func (m *MySyncCond) Broadcast(payload int) {
    // set the new payload before broadcasting
    m.Payload = payload
    // call sync.Cond.Broadcast()

// first worker, generates message to be broadcast out
func pinger(c *MySyncCond, wg *sync.WaitGroup) {

    defer wg.Done() // alert the waiting group we are done
    defer c.L.Unlock() // make sure we unlock the condition
    defer log.Println("Terminated pinger") // log a message that we are done here

    // Loop, and send out pings
    count := 0
    for {
        count += 1
        log.Printf("Pinger Pings: %d\n", count)
        // here I use the extended MySyncCond.Broadcast() that allows a payload to be passed
        time.Sleep(5 * time.Second)

// our second worker, checks if the ping count is divisible by 1 (true)
// Note: The infinite loop only breaks when the sync.Cond has the field Kill == true
func divByOne(c *MySyncCond, wg *sync.WaitGroup) {
    defer wg.Done() 
    defer c.L.Unlock() // make sure we unlock
    defer log.Printf("Terminated divByOne")

    for {
        // here I evaluate the Kill field of the MySyncCond struct
        if c.Kill {
        // here I evaluate the Payload field of the MySyncCond struct
        if c.Payload%1 == 0 {

// basically the same as divByOne, but by 2
func divByTwo(c *MySyncCond, wg *sync.WaitGroup) {
    defer wg.Done()
    defer c.L.Unlock() // make sure we unlock
    defer log.Println("Terminated divByTwo")

    for {
        if c.Kill {
        if c.Payload%2 == 0 {

// yet another worker, this one divides by 3
func divByThree(c *MySyncCond, wg *sync.WaitGroup) {
    defer wg.Done()
    defer c.L.Unlock()
    defer log.Println("Terminated divByThree")
    for {
        if c.Kill {
        if c.Payload%3 == 0 {

// Where the magic starts
func main() {
    // write all the logs to a known file
    logfile, err := os.OpenFile("/tmp/condfun.log", os.O_CREATE|os.O_APPEND|os.O_RDWR, 0644)
    if err != nil {
    // make sure we close the file handler
    defer logfile.Close()

    // Create a new Lock (Mutext), pass it to a new sync.Cond, and the push the sync.Cond into MySyncCond (thus extending it)
    c := MySyncCond{sync.NewCond(new(sync.Mutex)),  false, 0}

    // our first channel, this is used to tell Main that the Waiter is done.
    done := make(chan bool)

    // Kill channel, used to catch Unix Signals
    kill := make(chan os.Signal)
    defer close(kill) // let's make sure to close the channel when we are done!
    signal.Notify(kill) // the magic here assigns Unix Signals to be passed to our channel

    var wg sync.WaitGroup // create a WaitGroup

    wg.Add(4) // tell our WaitGroup how many workers we have
    // fire off all our workers
    // each of these must call wg.Done() or else this whole thing falls apart, hence the defer wg.Done() they all call. To further enforce this we could have used and interface, but that seemed like real work
    go pinger(&c, &wg)
    go divByOne(&c, &wg)
    go divByTwo(&c, &wg)
    go divByThree(&c, &wg)

    // out anonymous worker, this waits for all things in the WaitGroup to complete, if that happens it will pass true to the done channel
    go func(wg *sync.WaitGroup, done chan bool) {
        defer close(done)
        done <- true
    }(&wg, done)

    select {
    case <-kill: // handle the kill signal
        log.Println("Kill Seen")
        c.Kill = true
    case <-done: // handle the Waiter finishing (signals all workers stopped)
        log.Println("All workers have reported done")

    log.Println("Terminated Main")

As things always are, as I am writing this I realize now that there is the potential for the workers to fail, and I don't handle the errors, but this is more about the synchronous communication between workers.

#talkpay: 13 years of my professional life

#talkpay has taken twitter by storm. I wanted to go a little longer form because I want to get the whole story of how I got to where I am at out there, and documented for everyone to see. I will not disclose my current salary, because I haven't been approved (I haven't asked) but it is considered confidential at my company.

I graduated from ITT Technical in September 11th, 2002. I had an associates degree in Computer Networking Systems Technology. I had given up on networking and decided to pursue a paycheck (read: things were tight, needed a job). I landed my first post-school job at Overstock.com as Customer Service Phone rep. I was making $10/hour. I soon helped the company start their first email response team. Manually typing out emails with links to best selling products. I had known HTML since middle school (6th grade) and I had been friendly with computers from earlier in life. My training in HTML and small scripting allowed me to knock out hundreds of emails a day, where most team members were completing 20 to 50.

This gave me opportunity to move into the Affiliate Marketing department, with a small raise to $11/hour. I spent a few years working in this department. I started as part of a 2 or 3 person team. Our 3rd basically showed the 2 of us how to do things, and then went on to better things. While there I learned PHP, JavaScript, some JSP, some Java, SQL, MySQL administration, Apache configuration, some Linux, and how to talk tech. When I finally left the company I was earning $15/hour and had a small chunk of Restricted Stock Units (RSUs).

I left that company for a whirl wind of stops at other small tech companies, each time increasing my salary. First at GrabTakeOut.com (no gone), WebStats (they were purchased by Adobe/Omniture), Lantis Fireworks (mostly non tech related work), SOS Staffing Inc (now Elwood Staffing), Code Greene, and Progrexion Marketing.

Each time I moved from one company to the next I would go to the internet, and find the salary range that is expected for the titles I would be taking. I would determine if I met the requirements of the job, if I knew the technologies, how well I knew the technologies, and how much I would have to learn quickly, to earn what I was asking. Never did I take a job with a salary request that I wasn't certain I was worth. I always asked for more than I felt I was worth, so that when the dust settled I would have what I thought I was worth. This is probably the most important part of all of this... I always got what I felt I was worth. My self worth was my target salary.

Once in my 13 years as a professional, I took a job offer, and was backed into a corner with my salary negotiation. It soured the entire experience at the company, and as a result I only stayed with the company a short time. Their expectation of my worth, was not held to my requirements. I walked. I got a MUCH better job, with a company that respects whole-heartedly their employees.

In 13 years I went from $20,800/yr in customer service to roughly 7x that, by simply continuing to learn, grow and expand my worth. Today I program in Python, to asist in Infrastructure Automation, with the title Senior Infrastructure Engineer. To get where I am today I never stopped learning. Today I am learning new things. Tomorrow I will learn more new things. I will never stop learning, becuase if I do, my worth will decline.

If you want to #talkpay that's fine, I have things to go learn so I can continue to #earnpay