Skip to content

Instantly share code, notes, and snippets.

@ghthor
Created May 23, 2012 13:00
Show Gist options
  • Save ghthor/2775131 to your computer and use it in GitHub Desktop.
Save ghthor/2775131 to your computer and use it in GitHub Desktop.
Go Routine bug
throw: all goroutines are asleep - deadlock!
goroutine 1 [chan receive]:
main.main()
/home/ghthor/proj/go/ddnsd/main.go:121 +0x5e
goroutine 2 [syscall]:
created by runtime.main
/build/src/go/src/pkg/runtime/proc.c:221
//
// This app will monitor a file that will be updated with an ip address
// 1. Monitor file for ip changes
// 2. Ip changes update rackspace dns record
package main
import (
"code.google.com/p/go/src/pkg/exp/inotify"
"flag"
"io/ioutil"
"log"
"path"
"strings"
)
// Hostnames filesystem database that is updated by the clients
// databaseDir/[hostname]
// File contents will be the ip addr
var databaseDir string
func init() {
// CLI Flags
flag.StringVar(&databaseDir, "database", "hostnames", "Path to the directory of the Hostnames database that will be monitored by ddnsd")
flag.Parse()
}
func readFromDatabase(databaseHostname string) (ip string) {
ipRaw, err := ioutil.ReadFile(databaseHostname)
if err != nil {
log.Fatalf("Error reading from database: %s", err)
}
ip = strings.TrimRight(string(ipRaw), "\n")
return
}
type Record struct {
Hostname, IP string
}
func WatchForUpdates(filesystemDB string) <-chan Record {
// The in memory database of
// [hostname] ==> ip
hostnamesDB := make(map[string]string)
hostnames, err := ioutil.ReadDir(filesystemDB)
if err != nil {
log.Fatalf("Error opening database: %s", err)
}
// Generate the in memeory database of [hostname:ip]
for _, info := range hostnames {
hostnamesDB[info.Name()] = readFromDatabase(filesystemDB + "/" + info.Name())
}
log.Printf("Loaded Database: %s", hostnamesDB)
watcher, err := inotify.NewWatcher()
if err != nil {
log.Fatal(err)
}
defer watcher.Close()
err = watcher.Watch(filesystemDB)
if err != nil {
log.Fatalf("Watch failed: %s", err)
}
log.Printf("Monitoring database: %s", filesystemDB)
// Receive errors on the error channel on a separate goroutine
go func() {
for err := range watcher.Error {
log.Fatalf("error received: %s", err)
}
}()
// We want to mark when a file is modified so we can check it on the next IN_CLOSE event
modified := make(map[string]bool)
for hostname, _ := range hostnamesDB {
modified[hostname] = false
}
updates := make(chan Record, 100)
// Receive inotify events
eventstream := watcher.Event
go func(updates chan<- Record) {
for event := range eventstream {
hostname := path.Base(event.Name)
if (event.Mask&inotify.IN_CLOSE != 0) && (modified[hostname] == true) {
log.Printf("Close Event: \t%s", event)
// Unset the modified flag
modified[hostname] = false
// Read in the IP
ip := readFromDatabase(event.Name)
// Check if it is changed from what it was
if ip != hostnamesDB[hostname] {
hostnamesDB[hostname] = ip
// TODO Update dns record with rackspace API
updates<- Record{hostname, ip}
}
} else if event.Mask&inotify.IN_MODIFY != 0 {
log.Printf("Modify Event: \t%s", event)
// Set the modified flag
modified[hostname] = true
}
}
}(updates)
return updates
}
func main() {
updates := WatchForUpdates(databaseDir)
for update := range updates {
log.Printf("Database Updated: \t%s:%s", update.Hostname, update.IP)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment