Started working on making the daemonize functionality a little more robust.
This commit is contained in:
		@@ -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
 | 
					from posix import SIGTERM, SIGHUP, signal, kill
 | 
				
			||||||
import strutils except toUpper
 | 
					import strutils except toUpper
 | 
				
			||||||
from unicode import toUpper
 | 
					from unicode import toUpper
 | 
				
			||||||
@@ -9,30 +11,20 @@ type
 | 
				
			|||||||
  DPConfig = tuple[planDir, dateFmt, pidfile, logfile, errfile: string,
 | 
					  DPConfig = tuple[planDir, dateFmt, pidfile, logfile, errfile: string,
 | 
				
			||||||
    notificationSecs: int]
 | 
					    notificationSecs: int]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  CombinedConfig = object
 | 
					const VERSION = "0.3.0"
 | 
				
			||||||
    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 timeFmt = "HH:mm"
 | 
					const timeFmt = "HH:mm"
 | 
				
			||||||
 | 
					
 | 
				
			||||||
var args: Table[string, Value]
 | 
					var args: Table[string, Value]
 | 
				
			||||||
var cfg: DPConfig
 | 
					var cfg: DPConfig
 | 
				
			||||||
let appName = getAppFilename()
 | 
					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] =
 | 
					proc parseDailyPlan(filename: string): seq[PlanItem] =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  debug "Parsing daily plan file: " & filename
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  result = @[]
 | 
					  result = @[]
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var parseState = BeforeHeading
 | 
					  var parseState = BeforeHeading
 | 
				
			||||||
@@ -47,27 +39,42 @@ proc parseDailyPlan(filename: string): seq[PlanItem] =
 | 
				
			|||||||
      if line.strip.startsWith("## Timeline"): parseState = ReadingPlans
 | 
					      if line.strip.startsWith("## Timeline"): parseState = ReadingPlans
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    of ReadingPlans:
 | 
					    of ReadingPlans:
 | 
				
			||||||
      let match = line.find(planItemRe)
 | 
					      # TODO: This is the better code using the nre module: 
 | 
				
			||||||
      if match.isSome(): result.add((
 | 
					      # let match = line.find(planItemRe)
 | 
				
			||||||
        time: parse(match.get().captures[0], timeFmt),
 | 
					      # if match.isSome(): result.add((
 | 
				
			||||||
        note: match.get().captures[1]))
 | 
					      #   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
 | 
					      else: parseState = AfterPlans
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    of AfterPlans: break
 | 
					    of AfterPlans: break
 | 
				
			||||||
    else: break
 | 
					    else: break
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  debug "Found " & $result.len & " items."
 | 
				
			||||||
 | 
					
 | 
				
			||||||
proc notifyDailyPlanItem(item: PlanItem): void =
 | 
					proc notifyDailyPlanItem(item: PlanItem): void =
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  let soundFile = appName[0..(appName.len-15)] & "sounds/navi/" & soundFiles[random(soundFiles.len)]
 | 
				
			||||||
  case hostOS
 | 
					  case hostOS
 | 
				
			||||||
  of "macosx":
 | 
					  of "macosx":
 | 
				
			||||||
    discard execShellCmd("osascript -e 'display notification \"" &
 | 
					    discard execShellCmd("osascript -e 'display notification \"" &
 | 
				
			||||||
      item.time.format(timeFmt) & " - " & item.note &
 | 
					      item.time.format(timeFmt) & " - " & item.note &
 | 
				
			||||||
      "\" with title \"Daily Notifier v" & VERSION &
 | 
					      "\" with title \"Daily Notifier v" & VERSION & "\"")
 | 
				
			||||||
      "\" sound name \"default\"'")
 | 
					    discard execShellCmd("ogg123 \"" & soundFile & "\"")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  of "linux":
 | 
					  of "linux":
 | 
				
			||||||
    discard execShellCmd("notify-send 'Daily Notifier v" & VERSION & "' '" & item.note & "'")
 | 
					    discard execShellCmd("notify-send 'Daily Notifier v" & VERSION & "' '" & item.note & "'")
 | 
				
			||||||
    discard execShellCmd("paplay \"" & soundFile & "\"")
 | 
					    discard execShellCmd("paplay \"" & soundFile & "\"")
 | 
				
			||||||
    echo "paplay \"" & soundFile & "\""
 | 
					 | 
				
			||||||
 | 
					
 | 
				
			||||||
  else: quit("Unsupported host OS: '" & hostOS & "'.")
 | 
					  else: quit("Unsupported host OS: '" & hostOS & "'.")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -82,6 +89,8 @@ proc loadConfig(s: cint): void {. exportc, noconv .} =
 | 
				
			|||||||
  var rcFilename: string =
 | 
					  var rcFilename: string =
 | 
				
			||||||
    foldl(rcLocations, if len(a) > 0: a elif existsFile(b): b else: "")
 | 
					    foldl(rcLocations, if len(a) > 0: a elif existsFile(b): b else: "")
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  debug "Reloading config file from " & rcFilename
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  var jsonCfg: JsonNode
 | 
					  var jsonCfg: JsonNode
 | 
				
			||||||
  try: jsonCfg = parseFile(rcFilename)
 | 
					  try: jsonCfg = parseFile(rcFilename)
 | 
				
			||||||
  except: jsonCfg = newJObject()
 | 
					  except: jsonCfg = newJObject()
 | 
				
			||||||
@@ -169,6 +178,7 @@ Options:
 | 
				
			|||||||
  -h --help                   Print this usage information.
 | 
					  -h --help                   Print this usage information.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  -s --notification-seconds <sec>
 | 
					  -s --notification-seconds <sec>
 | 
				
			||||||
 | 
					  -v --verbose                Enable verbose output.
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    Notification period for plan items.
 | 
					    Notification period for plan items.
 | 
				
			||||||
    
 | 
					    
 | 
				
			||||||
@@ -182,10 +192,16 @@ Options:
 | 
				
			|||||||
 | 
					
 | 
				
			||||||
  loadConfig(0)
 | 
					  loadConfig(0)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					  addHandler(newConsoleLogger(
 | 
				
			||||||
 | 
					    if args["--verbose"]: lvlAll
 | 
				
			||||||
 | 
					    else: lvlInfo))
 | 
				
			||||||
 | 
					
 | 
				
			||||||
  if args["start"]:
 | 
					  if args["start"]:
 | 
				
			||||||
    # Start our daemon process (if needed)
 | 
					    # Start our daemon process (if needed)
 | 
				
			||||||
    daemonize(cfg.pidfile, "/dev/null", cfg.logfile, cfg.errfile):
 | 
					    info "Starting daemon."
 | 
				
			||||||
      mainLoop(args)
 | 
					    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"]:
 | 
					  elif args["stop"] or args["reconfigure"]:
 | 
				
			||||||
 | 
					
 | 
				
			||||||
@@ -194,6 +210,7 @@ Options:
 | 
				
			|||||||
      quit(QuitSuccess)
 | 
					      quit(QuitSuccess)
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    let pid = parseInt(readFile(cfg.pidfile).strip)
 | 
					    let pid = parseInt(readFile(cfg.pidfile).strip)
 | 
				
			||||||
 | 
					    debug "Killing process " & $pid
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    if args["stop"]: discard kill(pid, SIGTERM)
 | 
					    if args["stop"]: discard kill(pid, SIGTERM)
 | 
				
			||||||
    else: discard kill(pid, SIGHUP)
 | 
					    else: discard kill(pid, SIGHUP)
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -1,6 +1,6 @@
 | 
				
			|||||||
# Package
 | 
					# Package
 | 
				
			||||||
 | 
					
 | 
				
			||||||
version       = "0.2.2"
 | 
					version       = "0.3.0"
 | 
				
			||||||
author        = "Jonathan Bernard"
 | 
					author        = "Jonathan Bernard"
 | 
				
			||||||
description   = "Little programs that reads my daily plan and notifies me of upcoming events."
 | 
					description   = "Little programs that reads my daily plan and notifies me of upcoming events."
 | 
				
			||||||
license       = "MIT"
 | 
					license       = "MIT"
 | 
				
			||||||
 
 | 
				
			|||||||
		Reference in New Issue
	
	Block a user