Started working on making the daemonize functionality a little more robust.

This commit is contained in:
Jonathan Bernard 2016-11-04 16:29:32 -05:00
parent c717e7eeb4
commit 1861a2fcd7
2 changed files with 44 additions and 27 deletions

View File

@ -1,4 +1,6 @@
import daemonize, docopt, json, os, nre, sequtils, times, timeutils
import cliutils, docopt, json, logging, os, re, random, sequtils,
times, timeutils
from posix import SIGTERM, SIGHUP, signal, kill
import strutils except toUpper
from unicode import toUpper
@ -9,30 +11,20 @@ type
DPConfig = tuple[planDir, dateFmt, pidfile, logfile, errfile: string,
notificationSecs: int]
CombinedConfig = object
docopt: Table[string, Value]
json: JsonNode
proc getVal(cfg: CombinedConfig, key, default: string): string =
let argKey = "--" & key
let envKey = key.replace('-', '_').toUpper
let jsonKey = key.replace(re"(-\w)", proc (m: RegexMatch): string = ($m)[1..1].toUpper)
if cfg.docopt[argKey]: return $cfg.docopt[argKey]
elif existsEnv(envKey): return getEnv(envKey)
elif cfg.json.hasKey(jsonKey): return cfg.json[jsonKey].getStr
else: return default
const VERSION = "0.2.2"
const VERSION = "0.3.0"
const timeFmt = "HH:mm"
var args: Table[string, Value]
var cfg: DPConfig
let appName = getAppFilename()
let soundFile = appName[0..(appName.len-15)] & "sounds/notify1.ogg"
let soundsDir: string = appName[0..(appName.len-15)] & "sounds/navi"
let soundFiles = toSeq(walkFiles(soundsDir))
randomize()
proc parseDailyPlan(filename: string): seq[PlanItem] =
debug "Parsing daily plan file: " & filename
result = @[]
var parseState = BeforeHeading
@ -47,27 +39,42 @@ proc parseDailyPlan(filename: string): seq[PlanItem] =
if line.strip.startsWith("## Timeline"): parseState = ReadingPlans
of ReadingPlans:
let match = line.find(planItemRe)
if match.isSome(): result.add((
time: parse(match.get().captures[0], timeFmt),
note: match.get().captures[1]))
# TODO: This is the better code using the nre module:
# let match = line.find(planItemRe)
# if match.isSome(): result.add((
# time: parse(match.get().captures[0], timeFmt),
# note: match.get().captures[1]))
# else: parseState = AfterPlans
#
# Curently there is an incompatibility between the os and nre modules
# (see https://github.com/nim-lang/Nim/issues/4996) so the following code
# is used to avoid using nre until the bug is fixed.
if line.match(planItemRe):
let stripped = line.strip
result.add((
time: parse(stripped[0..4], timeFmt),
note: stripped.substr(5)))
else: parseState = AfterPlans
of AfterPlans: break
else: break
debug "Found " & $result.len & " items."
proc notifyDailyPlanItem(item: PlanItem): void =
let soundFile = appName[0..(appName.len-15)] & "sounds/navi/" & soundFiles[random(soundFiles.len)]
case hostOS
of "macosx":
discard execShellCmd("osascript -e 'display notification \"" &
item.time.format(timeFmt) & " - " & item.note &
"\" with title \"Daily Notifier v" & VERSION &
"\" sound name \"default\"'")
"\" with title \"Daily Notifier v" & VERSION & "\"")
discard execShellCmd("ogg123 \"" & soundFile & "\"")
of "linux":
discard execShellCmd("notify-send 'Daily Notifier v" & VERSION & "' '" & item.note & "'")
discard execShellCmd("paplay \"" & soundFile & "\"")
echo "paplay \"" & soundFile & "\""
else: quit("Unsupported host OS: '" & hostOS & "'.")
@ -82,6 +89,8 @@ proc loadConfig(s: cint): void {. exportc, noconv .} =
var rcFilename: string =
foldl(rcLocations, if len(a) > 0: a elif existsFile(b): b else: "")
debug "Reloading config file from " & rcFilename
var jsonCfg: JsonNode
try: jsonCfg = parseFile(rcFilename)
except: jsonCfg = newJObject()
@ -169,6 +178,7 @@ Options:
-h --help Print this usage information.
-s --notification-seconds <sec>
-v --verbose Enable verbose output.
Notification period for plan items.
@ -182,10 +192,16 @@ Options:
loadConfig(0)
addHandler(newConsoleLogger(
if args["--verbose"]: lvlAll
else: lvlInfo))
if args["start"]:
# Start our daemon process (if needed)
daemonize(cfg.pidfile, "/dev/null", cfg.logfile, cfg.errfile):
mainLoop(args)
info "Starting daemon."
let childPid = daemonize(cfg.pidfile, "/dev/null", cfg.logfile, cfg.errfile, proc(): void = mainLoop(args))
if childPid == 0: quit(QuitSuccess) # We are the child... don't need to do anything else.
info "Started, pid: " & $childPid
elif args["stop"] or args["reconfigure"]:
@ -194,6 +210,7 @@ Options:
quit(QuitSuccess)
let pid = parseInt(readFile(cfg.pidfile).strip)
debug "Killing process " & $pid
if args["stop"]: discard kill(pid, SIGTERM)
else: discard kill(pid, SIGHUP)

View File

@ -1,6 +1,6 @@
# Package
version = "0.2.2"
version = "0.3.0"
author = "Jonathan Bernard"
description = "Little programs that reads my daily plan and notifies me of upcoming events."
license = "MIT"