1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
|
# tickwatch
# Author: Euxane TRAN-GIRARD
# Licence: EUPL-1.2
import std/sugar
import std/math
import std/times
import std/net
import std/posix
import std/paths
import std/strutils
import std/sequtils
import std/parseopt
import file
import ping
import logger
const
NAME = "tickwatch"
VERSION = staticExec("""
command -v git >/dev/null && git describe --tags --always || echo $VERSION
""").strip()
HELP_TEXT =
staticRead("readme.md")
.split("```helptext", 1)[1]
.split("```", 1)[0]
.strip()
proc registerTerminationHandlers() =
proc terminationHandler(sig: cint) {.noconv.} = flushAndQuit()
var action = SigAction(sa_handler: terminationHandler)
for signal in [SIGTERM, SIGHUP, SIGQUIT, SIGINT]:
discard sigaction(signal, action)
func getArg(
args: seq[tuple[kind: CmdLineKind, key, val: string]],
index: int,
label: string
): string =
try: args[index].key
except: raise newException(ValueError, "Missing " & label & " argument")
proc main() =
var
scale: Scale = log2
symbols = UNICODE_SYMBOLS
timestampHeader = formatTimestampDateTime
(min, max) = (0, 1000)
for optKind, key, val in getopt():
if optKind notin {cmdLongOption, cmdShortOption}:
continue
case key:
of "help", "h":
echo HELP_TEXT
quit(0)
of "version", "v":
echo NAME & " " & VERSION
quit(0)
of "scale":
case val:
of "logarithmic", "log", "log2": scale = log2
of "log10": scale = log10
of "ln": scale = ln
of "linear", "lin": scale = (val: float) => val
else: raise newException(ValueError, "Unrecognised scale choice")
of "symbols":
case val:
of "unicode", "utf8", "utf-8": symbols = UNICODE_SYMBOLS
of "numeric", "ascii": symbols = NUMERIC_SYMBOLS
else: raise newException(ValueError, "Unrecognised symbols choice")
of "timestamp":
case val:
of "datetime", "date-time": timestampHeader = formatTimestampDateTime
of "unix", "epoch": timestampHeader = formatTimestampUnix
of "none", "false": timestampHeader = formatTimestampNone
else: raise newException(ValueError, "Unrecognised timestamp choice")
of "range":
let parts = val.split(':', 1)
if parts.len != 2: raise newException(ValueError, "Invalid range")
(min, max) = (parseInt(parts[0]), parseInt(parts[1]))
else:
raise newException(ValueError, "Unrecognised option")
let
args = getopt().toSeq.filterIt(it.kind == cmdArgument)
monitor = args.getArg(0, "monitor")
target = args.getArg(1, "target")
if args.len > 2:
raise newException(ValueError, "Invalid number of arguments")
let probe = case monitor:
of "ping":
let targetIp = resolve(target)
let mon = initPingMonitor(targetIp, procIdent())
(timeout: Duration) => mon.ping(timeout).inMilliseconds.int
of "value":
let mon = initFileValueMonitor(Path target)
(timeout: Duration) => mon.readValue()
of "change":
let mon = initFileChangeMonitor(Path target)
(timeout: Duration) => mon.readValue()
else:
raise newException(ValueError, "Unrecognised monitor argument")
loop(
probe,
(val: int) => symbols.indicator(min, max, val, scale),
timestampHeader,
)
when not defined(test):
try:
registerTerminationHandlers()
main()
except CatchableError as err:
stderr.writeLine err.msg
quit(1)
|