-
Notifications
You must be signed in to change notification settings - Fork 0
/
Copy pathstage0.lua
512 lines (444 loc) · 13.3 KB
/
stage0.lua
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
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
--
-- Copyright (c) 2009, Cloudkick, Inc.
-- All right reserved.
--
module(..., package.seeall);
local util = require 'util'
local socket = require 'socket'
local log = util.log
local lanes = require "lanes"
local alien = require "alien"
local helen = require 'helen'
function string:rfind(char)
local i = self:len()
while i >= 1 do
if self:sub(i, i) == char then
return i
end
i = i - 1
end
return -1
end
local function appname()
local basename = equus.equus_get_argv(0)
if equus.p_is_windows() == 1 then
basename = basename:sub(basename:rfind('\\')+1)
else
basename = basename:sub(basename:rfind('/')+1)
end
return basename
end
function version_str()
return "".. equus.EQUUS_VERSION_MAJOR ..".".. equus.EQUUS_VERSION_MINOR ..".".. equus.EQUUS_VERSION_PATCH
end
function log_version()
log.msg(appname() .." version %s", version_str())
end
function log_original_command()
local s = {}
for i=0,equus.equus_get_argc()-1 do
table.insert(s, equus.equus_get_argv(i))
end
log.msg('Launch Command: %s', table.concat(s, ' '))
end
local function detach()
local rv=nil
local err=nil
local merr=nil
local EXIT_FAILURE=1
-- rough port of apr_proc_detach
local libc = alien.default
-- The chdir() is expected to conform to IEEE Std 1003.1-1988 (``POSIX.1'').
libc.chdir:types("int", "string")
-- fork() function call appeared in Version 6 AT&T UNIX.
libc.fork:types("int")
-- The setsid function is expected to be compliant with the IEEE Std 1003.1-1988 (``POSIX.1'') specification.
libc.setsid:types("int")
-- The setpgid() function conforms to IEEE Std 1003.1-1988 (``POSIX.1'').
libc.setpgid:types("int", "int", "int")
libc.exit:types("void", "int")
libc.strerror:types("string", "int")
rv = libc.chdir("/")
if (rv <= -1) then
err = alien.errno()
merr = libc.strerror(err)
log.crit("chdir('/') returned -1: errno=(%d) %s", err, merr)
libc.exit(EXIT_FAILURE)
end
rv = libc.fork()
if (rv <= -1) then
err = alien.errno()
merr = libc.strerror(err)
log.crit("fork() returned -1: errno=(%d) %s", err, merr)
libc.exit(EXIT_FAILURE)
elseif (rv > 0) then
-- this is the parent process
libc.exit(0)
else
-- child
rv = libc.setsid()
if (rv <= -1) then
err = alien.errno()
merr = libc.strerror(err)
log.crit("libc.setsid() returned -1: errno=(%d) %s", err, merr)
libc.exit(EXIT_FAILURE)
end
equus.equus_shutdown_stdio()
end
end
function write_pidfile(path)
local libc = alien.default
libc.getpid:types("int")
local pid = libc.getpid()
local fd = assert(io.open(path, "w"))
fd:write(tostring(pid))
fd:close()
end
function print_help()
print(appname() ..' - Monitoring Agent by Cloudkick')
print('')
print('Usage: '.. appname() ..' <options>')
print('')
print('Options:')
if equus.p_is_windows() == 1 then
print(' --config|-c <path> Path to Config File [default: '.. os.getenv("ProgramFiles") ..'\\Cloudkick Agent\\etc\\cloudkick.cfg]')
-- these are pre-processed in equus.c:main()
print(' --noservice Do not start as Windows Service (default: start as service)')
print(' --install Install Windows Service')
print(' --delete Delete Windows Service')
print(' --start Start Windows Service')
print(' --stop Stop Windows Service')
print(' --status Status of Windows Service')
-- these are pre-processed in equus.c:main()
print(' --log|-l <path> Path to Log File [default: Windows Event Logger, "-" for testing]')
else
print(' --config|-c <path> Path to Config File [default: /etc/cloudkick.conf]')
print(' --daemon|-d Daemonize process')
print(' --log|-l <path> Path to Log File [default: /var/log/'.. appname() .. '.log]')
end
print(' --loglevel <ll> Verbosity of logging. [default: info]')
if equus.p_is_windows() == 0 then
print(' --pid|-p <path> Path to write file with process ID')
end
print(' --help|-h Show this help page')
print('')
end
function process_argv(conf)
local skiparg = false
for i=1,equus.equus_get_argc()-1 do
local v = equus.equus_get_argv(i)
if skiparg then
-- noop
skiparg = false
elseif (v == "--help" or v == "-h") then
print_help()
return false
-- TODO: check in win32
elseif (v == "--daemon" or v == "-d") then
conf.deamonize = true
-- TODO: check in win32
elseif (v == "--pid" or v == "-p") then
conf.pid_file = equus.equus_get_argv(i+1)
skiparg = true
elseif (v == "--log" or v == "-l") then
conf.log_file = equus.equus_get_argv(i+1)
skiparg = true
elseif (v == "--loglevel") then
conf.log_level = equus.equus_get_argv(i+1)
skiparg = true
-- TODO: check in win32
elseif (v == "--config" or v == "-c") then
conf.config_file = equus.equus_get_argv(i+1)
skiparg = true
else
if (v == "--install" or
v == "--delete" or
v == "--start" or
v == "--status" or
v == "--stop" or
v == "--noservice") then
else
print('Unknown command: '.. v)
print('')
print_help()
return false
end
end
end
return true
end
function mergeConfTables(t1, t2)
for k,v in pairs(t2) do
if v ~= nil then
t1[k] = v
--log.crit("merge: %s = %s", k, v)
end
end
return t1
end
function loadconf(filename)
-- returns a table with a files contents
local function trim(s)
-- from PiL2 20.4
return (s:gsub("^%s*(.-)%s*$", "%1"))
end
local c = {}
for line in io.lines(filename) do
if string.sub(line, 1, 1) ~= "#" then
local a,b = string.find(line, "(%s+)")
if a ~= nil then
local key = trim(string.sub(line, 1, a))
local value = trim(string.sub(line, b))
if key ~= nil and string.len(key) > 0 and value ~= nil and string.len(value) > 0 then
c[key] = value
--log.crit("conf: %s = %s", key, value)
end
end
end
end
return c
end
local global_conf = {}
function pick_config()
if equus.p_is_windows() == 1 then
return os.getenv("ProgramFiles") .."\\Cloudkick Agent\\etc\\cloudkick.cfg"
else
return "/etc/cloudkick.conf"
end
end
function load_config_defaults()
-- set some defaults.
local conf = {}
conf.deamonize = nil
conf.config_file = pick_config()
conf.pid_file = nil
conf.log_file = nil
if process_argv(conf) == false then
return
end
if conf.config_file ~= nil then
local cf, err = pcall(loadconf, conf.config_file)
if not cf and err then
log.crit("Unable to load config: %s", err)
return
end
conf = mergeConfTables(err, conf)
end
if conf.log_level == nil then
conf.log_level = "info"
end
if conf.oauth_key == nil then
log.crit("oauth_key missing from configuration file: %s", conf.config_file)
log.crit("Did you run cloudkick-config?")
log.crit("For support please visit https://support.cloudkick.com/Agent")
return
end
if conf.oauth_secret == nil then
log.crit("oauth_secret missing from configuration file: %s", conf.config_file)
log.crit("Did you run cloudkick-config?")
log.crit("For support please visit https://support.cloudkick.com/Agent")
return
end
if conf.endpoint == nil then
conf.endpoint = "agent-endpoint.cloudkick.com"
end
if conf.endpoint_port == nil then
conf.endpoint_port = 4166 -- Joost Peer to Peer Protocol -- Thanks Colm!
end
if conf.node_id_file == nil then
conf.node_id_file = "/var/lib/cloudkick-agent/node_id"
end
if conf.log_file == nil then
if conf.deamonize == true then
conf.log_file = "/var/log/".. appname() ..".log"
end
end
-- ping_interval: 10 minute max, 30 sec min, 10 minute default
if conf.ping_interval == nil then
conf.ping_interval = 600
else
conf.ping_interval = math.max(math.min(tonumber(conf.ping_interval), 600), 30)
end
if util.log_levels[conf.log_level] == nil then
log.crit("Unknown log level '%s'", conf.log_level)
local ll = {}
for k,v in pairs(util.log_levels) do table.insert(ll, k) end
log.crit("Valid log levels: %s", table.concat(ll, ', '))
log.crit("For support please visit https://support.cloudkick.com/Agent")
return
end
equus.equus_log_level_set(util.log_levels[conf.log_level])
if conf.log_file ~= nil then
local rv = equus.equus_log_set_path(conf.log_file)
if rv ~= 0 then
log.crit("Failed to setup logfile. Exiting.")
log.crit("For support please visit https://support.cloudkick.com/Agent")
return
end
end
if equus.p_is_windows() == 1 then
platform = 'windows'
else
platform = 'unix'
end
if conf.local_plugins_path == nil then
if platform == 'windows' then
conf.local_plugins_path = os.getenv("ProgramFiles") .."\\Cloudkick Agent\\plugins\\"
else
conf.local_plugins_path = "/usr/lib/cloudkick-agent/plugins/"
end
else
conf.local_plugins_path = util.normalize_path(conf.local_plugins_path, platform,
true, true)
end
if conf.munin_plugins_path == nil then
conf.munin_plugins_path = conf.local_plugins_path
else
conf.munin_plugins_path = util.normalize_path(conf.munin_plugins_path, platform,
true, true)
end
global_conf = conf
return conf
end
function run()
local conf = nil
conf = load_config_defaults()
if not conf then
return
end
if equus.g_equus_run_count <= 1 then
if conf.deamonize == true then
log.dbg("Starting to detach")
detach()
end
if conf.pid_file ~= nil then
log.dbg("Writing pid file")
write_pidfile(conf.pid_file)
end
else
log.info("Restarting...")
end
global_conf = conf
log_version()
log_original_command()
while equus.equus_restart_get() == 0 do
log.dbg('calling runloop()')
helen.run_loop()
end
end
local checks = {}
local run_once = 1
function periodic()
log.dbg(""..#(checks).." checks active")
--collectgarbage("collect")
if run_once == 1 then
-- run_check('disk')
run_once = 0
end
local a = {}
-- run_check('cpu', a, a)
--run_check('io', a, a)
for i,v in ipairs(checks) do
local l = v.lane
if l.status == "pending" then
log.dbg ('task pending')
elseif l.status == "running" then
log.dbg ('task running')
elseif l.status == "waiting" then
log.dbg ('task waiting')
else
log.dbg ('task done')
-- r = l[1]
l:join()
table.remove(checks, i)
end
end
collectgarbage()
end
function run_check(name, token, payload)
local checkname = 'check_'.. name
local func = lanes.gen(function(wkfd, checkname, token, payload)
local util = require 'util'
local log = util.log
log.dbg("in the lane for %s", checkname)
local Check = util.Check
local alien = require 'alien'
local rv, err, check
local function prep_result(checkname, rv)
local Json = require 'Json'
local data = Json.Encode(rv)
local l = string.len(data)
local s = "result ".. checkname .. " ".. token .." ".. l .. "\r\n".. data .. "\r\n"
return s
end
local check_obj = Check.create()
rv, check = pcall(require, checkname)
if not rv then
log.err("check failed to load: ".. check)
check_obj:set_error(check)
else
check.conf = {}
check.conf.local_plugins_path = global_conf.local_plugins_path
check.conf.munin_plugins_path = global_conf.munin_plugins_path
rv, err = pcall(check.run, check_obj, payload)
if not rv then
log.err("check failed pcall: ".. err)
check_obj:set_error(err)
end
end
rv = prep_result(checkname, check_obj)
equus.equus_push_result(rv, string.len(rv));
if equus.p_is_windows() == 1 then
-- local wakeup_pipe,err = socket.connect(helen.wakeup_pipe_ip, helen.wakeup_pipe_port)
-- if err ~= nil then
-- log.err("wakeup_pipe connect: %s", err)
-- end
-- wakeup_pipe.close()
else
log.dbg("writing wakeup_pipe")
local libc = alien.default
local buf = alien.array("char", 1)
buf[1] = '1'
libc.write:types("int", "int", "pointer", "int")
local rv = libc.write(wkfd, buf.buffer, 1)
end
return nil
end)
local t
if equus.p_is_windows() == 1 then
t = {lane=func(nil, checkname, token, payload), name=checkname}
else
t = {lane=func(helen.wakeup_pipe:getfd(), checkname, token, payload), name=checkname}
end
table.insert(checks, t)
end
function helen_host()
if global_conf.redirect_endpoint ~= nil then
return global_conf.redirect_endpoint
end
return global_conf.endpoint
end
function redirect_helen_host(dest)
global_conf.redirect_endpoint = dest
end
function helen_port()
return global_conf.endpoint_port
end
function helen_key()
return global_conf.oauth_key
end
function helen_secret()
return global_conf.oauth_secret
end
function helen_config_file()
return global_conf.config_file
end
function helen_ping_interval()
return global_conf.ping_interval
end
function local_plugins_path()
return global_conf.local_plugins_path
end
function munin_plugins_paths()
return global_conf.munin_plugins_path
end