1 /*
2 * Copyright (C) Tildeslash Ltd. All rights reserved.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU Affero General Public License version 3.
6 *
7 * This program is distributed in the hope that it will be useful,
8 * but WITHOUT ANY WARRANTY; without even the implied warranty of
9 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
10 * GNU General Public License for more details.
11 *
12 * You should have received a copy of the GNU Affero General Public License
13 * along with this program. If not, see <http://www.gnu.org/licenses/>.
14 *
15 * In addition, as a special exception, the copyright holders give
16 * permission to link the code of portions of this program with the
17 * OpenSSL library under certain conditions as described in each
18 * individual source file, and distribute linked combinations
19 * including the two.
20 *
21 * You must obey the GNU Affero General Public License in all respects
22 * for all of the code used other than OpenSSL.
23 */
24
25 #include "config.h"
26
27 #ifdef HAVE_STDIO_H
28 #include <stdio.h>
29 #endif
30
31 #ifdef HAVE_STDLIB_H
32 #include <stdlib.h>
33 #endif
34
35 #ifdef HAVE_ERRNO_H
36 #include <errno.h>
37 #endif
38
39 #ifdef HAVE_SYS_TYPES_H
40 #include <sys/types.h>
41 #endif
42
43 #ifdef HAVE_SYS_SOCKET_H
44 #include <sys/socket.h>
45 #endif
46
47 #ifdef HAVE_STRING_H
48 #include <string.h>
49 #endif
50
51 #ifdef HAVE_STRINGS_H
52 #include <strings.h>
53 #endif
54
55 #ifdef HAVE_UNISTD_H
56 #include <unistd.h>
57 #endif
58
59 #ifdef HAVE_SYS_STAT_H
60 #include <sys/stat.h>
61 #endif
62
63 #ifdef HAVE_SYS_TIME_H
64 #include <sys/time.h>
65 #endif
66
67 #ifdef HAVE_CTYPE_H
68 #include <ctype.h>
69 #endif
70
71 // libmonit
72 #include "system/Time.h"
73 #include "util/Convert.h"
74 #include "util/List.h"
75
76 #include "monit.h"
77 #include "cervlet.h"
78 #include "engine.h"
79 #include "processor.h"
80 #include "base64.h"
81 #include "event.h"
82 #include "alert.h"
83 #include "ProcessTree.h"
84 #include "device.h"
85 #include "protocol.h"
86 #include "TextColor.h"
87 #include "TextBox.h"
88
89
90 #define ACTION(c) ! strncasecmp(req->url, c, sizeof(c))
91
92
93 /* URL Commands supported */
94 #define HOME "/"
95 #define TEST "/_monit"
96 #define ABOUT "/_about"
97 #define PING "/_ping"
98 #define GETID "/_getid"
99 #define STATUS "/_status"
100 #define STATUS2 "/_status2"
101 #define SUMMARY "/_summary"
102 #define REPORT "/_report"
103 #define RUNTIME "/_runtime"
104 #define VIEWLOG "/_viewlog"
105 #define DOACTION "/_doaction"
106 #define FAVICON "/favicon.ico"
107
108 // Limit for the viewlog response
109 #define VIEWLOG_LIMIT 1048576
110
111
112 typedef enum {
113 TXT = 0,
114 HTML
115 } __attribute__((__packed__)) Output_Type;
116
117
118 typedef struct ServiceMap_T {
119 int found;
120 union {
121 struct {
122 const char *name;
123 const char *token;
124 Action_Type id;
125 } action;
126 struct {
127 HttpResponse res;
128 } status;
129 struct {
130 TextBox_T box;
131 } summary;
132 } data;
133 } *ServiceMap_T;
134
135
136 typedef struct ReportStatics_T {
137 float up;
138 float down;
139 float init;
140 float unmonitored;
141 float total;
142 } *ReportStatics_T;
143
144
145 /* Private prototypes */
146 static bool is_readonly(HttpRequest);
147 static void printFavicon(HttpResponse);
148 static void doGet(HttpRequest, HttpResponse);
149 static void doPost(HttpRequest, HttpResponse);
150 static void do_head(HttpResponse res, const char *path, const char *name, int refresh);
151 static void do_foot(HttpResponse res);
152 static void do_home(HttpResponse);
153 static void do_home_system(HttpResponse);
154 static void do_home_filesystem(HttpResponse);
155 static void do_home_directory(HttpResponse);
156 static void do_home_file(HttpResponse);
157 static void do_home_fifo(HttpResponse);
158 static void do_home_net(HttpResponse);
159 static void do_home_process(HttpResponse);
160 static void do_home_program(HttpResponse);
161 static void do_home_host(HttpResponse);
162 static void do_about(HttpResponse);
163 static void do_ping(HttpResponse);
164 static void do_getid(HttpResponse);
165 static void do_runtime(HttpRequest, HttpResponse);
166 static void do_viewlog(HttpRequest, HttpResponse);
167 static void handle_service(HttpRequest, HttpResponse);
168 static void handle_service_action(HttpRequest, HttpResponse);
169 static void handle_doaction(HttpRequest, HttpResponse);
170 static void handle_runtime(HttpRequest, HttpResponse);
171 static void handle_runtime_action(HttpRequest, HttpResponse);
172 static void is_monit_running(HttpResponse);
173 static void do_service(HttpRequest, HttpResponse, Service_T);
174 static void print_alerts(HttpResponse, Mail_T);
175 static void print_buttons(HttpRequest, HttpResponse, Service_T);
176 static void print_service_rules_timeout(HttpResponse, Service_T);
177 static void print_service_rules_nonexistence(HttpResponse, Service_T);
178 static void print_service_rules_existence(HttpResponse, Service_T);
179 static void print_service_rules_port(HttpResponse, Service_T);
180 static void print_service_rules_socket(HttpResponse, Service_T);
181 static void print_service_rules_icmp(HttpResponse, Service_T);
182 static void print_service_rules_perm(HttpResponse, Service_T);
183 static void print_service_rules_uid(HttpResponse, Service_T);
184 static void print_service_rules_euid(HttpResponse, Service_T);
185 static void print_service_rules_gid(HttpResponse, Service_T);
186 static void print_service_rules_timestamp(HttpResponse, Service_T);
187 static void print_service_rules_fsflags(HttpResponse, Service_T);
188 static void print_service_rules_filesystem(HttpResponse, Service_T);
189 static void print_service_rules_size(HttpResponse, Service_T);
190 static void print_service_rules_linkstatus(HttpResponse, Service_T);
191 static void print_service_rules_linkspeed(HttpResponse, Service_T);
192 static void print_service_rules_linksaturation(HttpResponse, Service_T);
193 static void print_service_rules_uploadbytes(HttpResponse, Service_T);
194 static void print_service_rules_uploadpackets(HttpResponse, Service_T);
195 static void print_service_rules_downloadbytes(HttpResponse, Service_T);
196 static void print_service_rules_downloadpackets(HttpResponse, Service_T);
197 static void print_service_rules_uptime(HttpResponse, Service_T);
198 static void print_service_rules_content(HttpResponse, Service_T);
199 static void print_service_rules_checksum(HttpResponse, Service_T);
200 static void print_service_rules_pid(HttpResponse, Service_T);
201 static void print_service_rules_ppid(HttpResponse, Service_T);
202 static void print_service_rules_program(HttpResponse, Service_T);
203 static void print_service_rules_resource(HttpResponse, Service_T);
204 static void print_service_rules_secattr(HttpResponse, Service_T);
205 static void print_service_rules_filedescriptors(HttpResponse, Service_T);
206 static void print_status(HttpRequest, HttpResponse, int);
207 static void print_summary(HttpRequest, HttpResponse);
208 static void _printReport(HttpRequest req, HttpResponse res);
209 static void status_service_txt(Service_T, HttpResponse);
210 static char *get_monitoring_status(Output_Type, Service_T s, char *, int);
211 static char *get_service_status(Output_Type, Service_T, char *, int);
212
213
214 /**
215 * Implementation of doGet and doPost routines used by the cervlet
216 * processor module. This particilary cervlet will provide
217 * information about the monit daemon and programs monitored by
218 * monit.
219 *
220 * @file
221 */
222
223
224 /* ------------------------------------------------------------------ Public */
225
226
227 /**
228 * Callback hook to the Processor module for registering this modules
229 * doGet and doPost methods.
230 */
init_service()231 void init_service() {
232 add_Impl(doGet, doPost);
233 }
234
235
236 /* ----------------------------------------------------------------- Private */
237
238
_printServiceSummary(TextBox_T t,Service_T s)239 static void _printServiceSummary(TextBox_T t, Service_T s) {
240 TextBox_setColumn(t, 1, "%s", s->name);
241 TextBox_setColumn(t, 2, "%s", get_service_status(TXT, s, (char[STRLEN]){}, STRLEN));
242 TextBox_setColumn(t, 3, "%s", servicetypes[s->type]);
243 TextBox_printRow(t);
244 }
245
246
_serviceMapByName(const char * pattern,void (* callback)(Service_T s,ServiceMap_T ap),ServiceMap_T ap)247 static void _serviceMapByName(const char *pattern, void (*callback)(Service_T s, ServiceMap_T ap), ServiceMap_T ap) {
248 // Check the service name using the following sequence:
249 // 1) if the pattern is NULL, any service will match
250 // 2) backard compatibility: before monit 5.28.0 there was no support for regular expresion => check verbatim match before trying regex (the service may contain special characters)
251 // 3) regex match
252 if (pattern) {
253 int rv;
254 regex_t r;
255 char patternEscaped[STRLEN];
256 const char *patternCursor;
257
258 // If the pattern doesn't contain "^" or "$" already, wrap it as "^<pattern>$" to prevent match with services that contain the given substring only
259 if (! Str_has("^$", pattern)) {
260 snprintf(patternEscaped, sizeof(patternEscaped), "^%s$", pattern);
261 patternCursor = patternEscaped;
262 } else {
263 patternCursor = pattern;
264 }
265
266 // The pattern is set, try to compile it as regex
267 if ((rv = regcomp(&r, patternCursor, REG_NOSUB | REG_EXTENDED | REG_ICASE))) {
268 // Pattern compilation failed, fallback to verbatim match (before monit 5.28.0 there was no support for regular expresion)
269 char error[STRLEN];
270
271 regerror(rv, &r, error, STRLEN);
272 regfree(&r);
273 DEBUG("Regex %s parsing error: %s\n", patternCursor, error);
274
275 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
276 if (IS(pattern, s->name)) { // Use the unescaped/original pattern
277 callback(s, ap);
278 ap->found++;
279 }
280 }
281 } else {
282 // Regular expression match
283 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
284 if (! regexec(&r, s->name, 0, NULL, 0)) {
285 callback(s, ap);
286 ap->found++;
287 }
288 }
289 regfree(&r);
290 }
291
292
293 } else {
294 // Pattern is not set, any service will match
295 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
296 callback(s, ap);
297 ap->found++;
298 }
299 }
300 }
301
302
_serviceMapByType(Service_Type type,void (* callback)(Service_T s,ServiceMap_T ap),ServiceMap_T ap)303 static void _serviceMapByType(Service_Type type, void (*callback)(Service_T s, ServiceMap_T ap), ServiceMap_T ap) {
304 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
305 if (s->type == type) {
306 callback(s, ap);
307 ap->found++;
308 }
309 }
310 }
311
312
_serviceMapSummary(Service_T s,ServiceMap_T ap)313 static void _serviceMapSummary(Service_T s, ServiceMap_T ap) {
314 _printServiceSummary(ap->data.summary.box, s);
315 }
316
317
_serviceMapStatus(Service_T s,ServiceMap_T ap)318 static void _serviceMapStatus(Service_T s, ServiceMap_T ap) {
319 status_service_txt(s, ap->data.status.res);
320 }
321
322
_serviceMapAction(Service_T s,ServiceMap_T ap)323 static void _serviceMapAction(Service_T s, ServiceMap_T ap) {
324 s->doaction = ap->data.action.id;
325 Log_info("'%s' %s on user request\n", s->name, ap->data.action.name);
326 }
327
328
_getUptime(time_t delta,char s[256])329 static char *_getUptime(time_t delta, char s[256]) {
330 static int min = 60;
331 static int hour = 3600;
332 static int day = 86400;
333 long rest_d;
334 long rest_h;
335 long rest_m;
336 char *p = s;
337
338 if (delta < 0) {
339 *s = 0;
340 } else {
341 if ((rest_d = delta / day) > 0) {
342 p += snprintf(p, 256 - (p - s), "%ldd ", rest_d);
343 delta -= rest_d * day;
344 }
345 if ((rest_h = delta / hour) > 0 || (rest_d > 0)) {
346 p += snprintf(p, 256 - (p - s), "%ldh ", rest_h);
347 delta -= rest_h * hour;
348 }
349 rest_m = delta / min;
350 snprintf(p, 256 - (p - s), "%ldm", rest_m);
351 }
352 return s;
353 }
354
355
_formatStatus(const char * name,Event_Type errorType,Output_Type type,HttpResponse res,Service_T s,bool validValue,const char * value,...)356 __attribute__((format (printf, 7, 8))) static void _formatStatus(const char *name, Event_Type errorType, Output_Type type, HttpResponse res, Service_T s, bool validValue, const char *value, ...) {
357 if (type == HTML) {
358 StringBuffer_append(res->outputbuffer, "<tr><td>%c%s</td>", toupper(name[0]), name + 1);
359 } else {
360 StringBuffer_append(res->outputbuffer, " %-28s ", name);
361 }
362 if (! validValue) {
363 StringBuffer_append(res->outputbuffer, type == HTML ? "<td class='gray-text'>-</td>" : COLOR_DARKGRAY "-" COLOR_RESET);
364 } else {
365 va_list ap;
366 va_start(ap, value);
367 char *_value = Str_vcat(value, ap);
368 va_end(ap);
369
370 if (errorType != Event_Null && s->error & errorType)
371 StringBuffer_append(res->outputbuffer, type == HTML ? "<td class='red-text'>" : COLOR_LIGHTRED);
372 else
373 StringBuffer_append(res->outputbuffer, type == HTML ? "<td>" : COLOR_DEFAULT);
374
375 if (type == HTML) {
376 // If the output contains multiple line, wrap use <pre>, otherwise keep as is
377 bool multiline = strrchr(_value, '\n') ? true : false;
378 if (multiline)
379 StringBuffer_append(res->outputbuffer, "<pre>");
380 escapeHTML(res->outputbuffer, _value);
381 StringBuffer_append(res->outputbuffer, "%s</td>", multiline ? "</pre>" : "");
382 } else {
383 int column = 0;
384 for (int i = 0; _value[i]; i++) {
385 if (_value[i] == '\r') {
386 // Discard CR
387 continue;
388 } else if (_value[i] == '\n') {
389 // Indent 2nd+ line
390 if (_value[i + 1])
391 StringBuffer_append(res->outputbuffer, "\n ");
392 column = 0;
393 continue;
394 } else if (column <= 200) {
395 StringBuffer_append(res->outputbuffer, "%c", _value[i]);
396 column++;
397 }
398 }
399 StringBuffer_append(res->outputbuffer, COLOR_RESET);
400 }
401 FREE(_value);
402 }
403 StringBuffer_append(res->outputbuffer, type == HTML ? "</tr>" : "\n");
404 }
405
406
_printIOStatistics(Output_Type type,HttpResponse res,Service_T s,IOStatistics_T io,const char * name)407 static void _printIOStatistics(Output_Type type, HttpResponse res, Service_T s, IOStatistics_T io, const char *name) {
408 char header[STRLEN] = {};
409 if (Statistics_initialized(&(io->bytes))) {
410 snprintf(header, sizeof(header), "%s bytes", name);
411 double deltaBytesPerSec = Statistics_deltaNormalize(&(io->bytes));
412 _formatStatus(header, Event_Resource, type, res, s, true, "%s/s [%s total]", Convert_bytes2str(deltaBytesPerSec, (char[10]){}), Convert_bytes2str(Statistics_raw(&(io->bytes)), (char[10]){}));
413 }
414 if (Statistics_initialized(&(io->bytesPhysical))) {
415 snprintf(header, sizeof(header), "disk %s bytes", name);
416 double deltaBytesPerSec = Statistics_deltaNormalize(&(io->bytesPhysical));
417 _formatStatus(header, Event_Resource, type, res, s, true, "%s/s [%s total]", Convert_bytes2str(deltaBytesPerSec, (char[10]){}), Convert_bytes2str(Statistics_raw(&(io->bytesPhysical)), (char[10]){}));
418 }
419 if (Statistics_initialized(&(io->operations))) {
420 snprintf(header, sizeof(header), "disk %s operations", name);
421 double deltaOpsPerSec = Statistics_deltaNormalize(&(io->operations));
422 _formatStatus(header, Event_Resource, type, res, s, true, "%.1f %ss/s [%llu %ss total]", deltaOpsPerSec, name, Statistics_raw(&(io->operations)), name);
423 }
424 }
425
426
_printStatus(Output_Type type,HttpResponse res,Service_T s)427 static void _printStatus(Output_Type type, HttpResponse res, Service_T s) {
428 if (Util_hasServiceStatus(s)) {
429 switch (s->type) {
430 case Service_System:
431 {
432 _formatStatus("load average", Event_Resource, type, res, s, true, "[%.2f] [%.2f] [%.2f]", systeminfo.loadavg[0], systeminfo.loadavg[1], systeminfo.loadavg[2]);
433 StringBuffer_T sb = StringBuffer_create(256);
434 if (systeminfo.statisticsAvailable & Statistics_CpuUser)
435 StringBuffer_append(sb, "%.1f%%usr ", systeminfo.cpu.usage.user > 0. ? systeminfo.cpu.usage.user : 0.);
436 if (systeminfo.statisticsAvailable & Statistics_CpuSystem)
437 StringBuffer_append(sb, "%.1f%%sys ", systeminfo.cpu.usage.system > 0. ? systeminfo.cpu.usage.system : 0.);
438 if (systeminfo.statisticsAvailable & Statistics_CpuNice)
439 StringBuffer_append(sb, "%.1f%%nice ", systeminfo.cpu.usage.nice > 0. ? systeminfo.cpu.usage.nice : 0.);
440 if (systeminfo.statisticsAvailable & Statistics_CpuIOWait)
441 StringBuffer_append(sb, "%.1f%%iowait ", systeminfo.cpu.usage.iowait > 0. ? systeminfo.cpu.usage.iowait : 0.);
442 if (systeminfo.statisticsAvailable & Statistics_CpuHardIRQ)
443 StringBuffer_append(sb, "%.1f%%hardirq ", systeminfo.cpu.usage.hardirq > 0. ? systeminfo.cpu.usage.hardirq : 0.);
444 if (systeminfo.statisticsAvailable & Statistics_CpuSoftIRQ)
445 StringBuffer_append(sb, "%.1f%%softirq ", systeminfo.cpu.usage.softirq > 0. ? systeminfo.cpu.usage.softirq : 0.);
446 if (systeminfo.statisticsAvailable & Statistics_CpuSteal)
447 StringBuffer_append(sb, "%.1f%%steal ", systeminfo.cpu.usage.steal > 0. ? systeminfo.cpu.usage.steal : 0.);
448 if (systeminfo.statisticsAvailable & Statistics_CpuGuest)
449 StringBuffer_append(sb, "%.1f%%guest ", systeminfo.cpu.usage.guest > 0. ? systeminfo.cpu.usage.guest : 0.);
450 if (systeminfo.statisticsAvailable & Statistics_CpuGuestNice)
451 StringBuffer_append(sb, "%.1f%%guestnice ", systeminfo.cpu.usage.guest_nice > 0. ? systeminfo.cpu.usage.guest_nice : 0.);
452 _formatStatus("cpu", Event_Resource, type, res, s, true, "%s", StringBuffer_toString(sb));
453 StringBuffer_free(&sb);
454 _formatStatus("memory usage", Event_Resource, type, res, s, true, "%s [%.1f%%]", Convert_bytes2str(systeminfo.memory.usage.bytes, (char[10]){}), systeminfo.memory.usage.percent);
455 _formatStatus("swap usage", Event_Resource, type, res, s, true, "%s [%.1f%%]", Convert_bytes2str(systeminfo.swap.usage.bytes, (char[10]){}), systeminfo.swap.usage.percent);
456 _formatStatus("uptime", Event_Uptime, type, res, s, systeminfo.booted > 0, "%s", _getUptime(Time_now() - systeminfo.booted, (char[256]){}));
457 _formatStatus("boot time", Event_Null, type, res, s, true, "%s", Time_string(systeminfo.booted, (char[32]){}));
458 if (systeminfo.statisticsAvailable & Statistics_FiledescriptorsPerSystem) {
459 if (systeminfo.filedescriptors.maximum > 0)
460 _formatStatus("filedescriptors", Event_Resource, type, res, s, true, "%lld [%.1f%% of %lld limit]", systeminfo.filedescriptors.allocated, (float)100 * (float)systeminfo.filedescriptors.allocated / (float)systeminfo.filedescriptors.maximum, systeminfo.filedescriptors.maximum);
461 else
462 _formatStatus("filedescriptors", Event_Resource, type, res, s, true, "N/A");
463 }
464 }
465 break;
466
467 case Service_File:
468 _formatStatus("permission", Event_Permission, type, res, s, s->inf.file->mode >= 0, "%o", s->inf.file->mode & 07777);
469 _formatStatus("uid", Event_Uid, type, res, s, s->inf.file->uid >= 0, "%d", s->inf.file->uid);
470 _formatStatus("gid", Event_Gid, type, res, s, s->inf.file->gid >= 0, "%d", s->inf.file->gid);
471 _formatStatus("size", Event_Size, type, res, s, s->inf.file->size >= 0, "%s", Convert_bytes2str(s->inf.file->size, (char[10]){}));
472 _formatStatus("access timestamp", Event_Timestamp, type, res, s, s->inf.file->timestamp.access > 0, "%s", Time_string(s->inf.file->timestamp.access, (char[32]){}));
473 _formatStatus("change timestamp", Event_Timestamp, type, res, s, s->inf.file->timestamp.change > 0, "%s", Time_string(s->inf.file->timestamp.change, (char[32]){}));
474 _formatStatus("modify timestamp", Event_Timestamp, type, res, s, s->inf.file->timestamp.modify > 0, "%s", Time_string(s->inf.file->timestamp.modify, (char[32]){}));
475 if (s->matchlist)
476 _formatStatus("content match", Event_Content, type, res, s, true, "%s", (s->error & Event_Content) ? "yes" : "no");
477 if (s->checksum)
478 _formatStatus("checksum", Event_Checksum, type, res, s, *s->inf.file->cs_sum, "%s (%s)", s->inf.file->cs_sum, checksumnames[s->checksum->type]);
479 break;
480
481 case Service_Directory:
482 _formatStatus("permission", Event_Permission, type, res, s, s->inf.directory->mode >= 0, "%o", s->inf.directory->mode & 07777);
483 _formatStatus("uid", Event_Uid, type, res, s, s->inf.directory->uid >= 0, "%d", s->inf.directory->uid);
484 _formatStatus("gid", Event_Gid, type, res, s, s->inf.directory->gid >= 0, "%d", s->inf.directory->gid);
485 _formatStatus("access timestamp", Event_Timestamp, type, res, s, s->inf.directory->timestamp.access > 0, "%s", Time_string(s->inf.directory->timestamp.access, (char[32]){}));
486 _formatStatus("change timestamp", Event_Timestamp, type, res, s, s->inf.directory->timestamp.change > 0, "%s", Time_string(s->inf.directory->timestamp.change, (char[32]){}));
487 _formatStatus("modify timestamp", Event_Timestamp, type, res, s, s->inf.directory->timestamp.modify > 0, "%s", Time_string(s->inf.directory->timestamp.modify, (char[32]){}));
488 break;
489
490 case Service_Fifo:
491 _formatStatus("permission", Event_Permission, type, res, s, s->inf.fifo->mode >= 0, "%o", s->inf.fifo->mode & 07777);
492 _formatStatus("uid", Event_Uid, type, res, s, s->inf.fifo->uid >= 0, "%d", s->inf.fifo->uid);
493 _formatStatus("gid", Event_Gid, type, res, s, s->inf.fifo->gid >= 0, "%d", s->inf.fifo->gid);
494 _formatStatus("access timestamp", Event_Timestamp, type, res, s, s->inf.fifo->timestamp.access > 0, "%s", Time_string(s->inf.fifo->timestamp.access, (char[32]){}));
495 _formatStatus("change timestamp", Event_Timestamp, type, res, s, s->inf.fifo->timestamp.change > 0, "%s", Time_string(s->inf.fifo->timestamp.change, (char[32]){}));
496 _formatStatus("modify timestamp", Event_Timestamp, type, res, s, s->inf.fifo->timestamp.modify > 0, "%s", Time_string(s->inf.fifo->timestamp.modify, (char[32]){}));
497 break;
498
499 case Service_Net:
500 {
501 long long speed = Link_getSpeed(s->inf.net->stats);
502 long long ibytes = Link_getBytesInPerSecond(s->inf.net->stats);
503 long long obytes = Link_getBytesOutPerSecond(s->inf.net->stats);
504 _formatStatus("link", Event_Link, type, res, s, Link_getState(s->inf.net->stats) == 1, "%lld errors", Link_getErrorsInPerSecond(s->inf.net->stats) + Link_getErrorsOutPerSecond(s->inf.net->stats));
505 if (speed > 0) {
506 _formatStatus("capacity", Event_Speed, type, res, s, Link_getState(s->inf.net->stats) == 1, "%.0lf Mb/s %s-duplex", (double)speed / 1000000., Link_getDuplex(s->inf.net->stats) == 1 ? "full" : "half");
507 _formatStatus("download bytes", Event_ByteIn, type, res, s, Link_getState(s->inf.net->stats) == 1, "%s/s (%.1f%% link saturation)", Convert_bytes2str(ibytes, (char[10]){}), 100. * ibytes * 8 / (double)speed);
508 _formatStatus("upload bytes", Event_ByteOut, type, res, s, Link_getState(s->inf.net->stats) == 1, "%s/s (%.1f%% link saturation)", Convert_bytes2str(obytes, (char[10]){}), 100. * obytes * 8 / (double)speed);
509 } else {
510 _formatStatus("download bytes", Event_ByteIn, type, res, s, Link_getState(s->inf.net->stats) == 1, "%s/s", Convert_bytes2str(ibytes, (char[10]){}));
511 _formatStatus("upload bytes", Event_ByteOut, type, res, s, Link_getState(s->inf.net->stats) == 1, "%s/s", Convert_bytes2str(obytes, (char[10]){}));
512 }
513 _formatStatus("download packets", Event_PacketIn, type, res, s, Link_getState(s->inf.net->stats) == 1, "%lld per second", Link_getPacketsInPerSecond(s->inf.net->stats));
514 _formatStatus("upload packets", Event_PacketOut, type, res, s, Link_getState(s->inf.net->stats) == 1, "%lld per second", Link_getPacketsOutPerSecond(s->inf.net->stats));
515 }
516 break;
517
518 case Service_Filesystem:
519 _formatStatus("filesystem type", Event_Null, type, res, s, *(s->inf.filesystem->object.type), "%s", s->inf.filesystem->object.type);
520 _formatStatus("filesystem flags", Event_FsFlag, type, res, s, *(s->inf.filesystem->flags.current), "%s", s->inf.filesystem->flags.current);
521 _formatStatus("permission", Event_Permission, type, res, s, s->inf.filesystem->mode >= 0, "%o", s->inf.filesystem->mode & 07777);
522 _formatStatus("uid", Event_Uid, type, res, s, s->inf.filesystem->uid >= 0, "%d", s->inf.filesystem->uid);
523 _formatStatus("gid", Event_Gid, type, res, s, s->inf.filesystem->gid >= 0, "%d", s->inf.filesystem->gid);
524 _formatStatus("block size", Event_Null, type, res, s, true, "%s", Convert_bytes2str(s->inf.filesystem->f_bsize, (char[10]){}));
525 _formatStatus("space total", Event_Null, type, res, s, true, "%s (of which %.1f%% is reserved for root user)",
526 s->inf.filesystem->f_bsize > 0 ? Convert_bytes2str(s->inf.filesystem->f_blocks * s->inf.filesystem->f_bsize, (char[10]){}) : "0 MB",
527 s->inf.filesystem->f_blocks > 0 ? ((float)100 * (float)(s->inf.filesystem->f_blocksfreetotal - s->inf.filesystem->f_blocksfree) / (float)s->inf.filesystem->f_blocks) : 0);
528 _formatStatus("space free for non superuser", Event_Null, type, res, s, true, "%s [%.1f%%]",
529 s->inf.filesystem->f_bsize > 0 ? Convert_bytes2str(s->inf.filesystem->f_blocksfree * s->inf.filesystem->f_bsize, (char[10]){}) : "0 MB",
530 s->inf.filesystem->f_blocks > 0 ? ((float)100 * (float)s->inf.filesystem->f_blocksfree / (float)s->inf.filesystem->f_blocks) : 0);
531 _formatStatus("space free total", Event_Resource, type, res, s, true, "%s [%.1f%%]",
532 s->inf.filesystem->f_bsize > 0 ? Convert_bytes2str(s->inf.filesystem->f_blocksfreetotal * s->inf.filesystem->f_bsize, (char[10]){}) : "0 MB",
533 s->inf.filesystem->f_blocks > 0 ? ((float)100 * (float)s->inf.filesystem->f_blocksfreetotal / (float)s->inf.filesystem->f_blocks) : 0);
534 if (s->inf.filesystem->f_files > 0) {
535 _formatStatus("inodes total", Event_Null, type, res, s, true, "%lld", s->inf.filesystem->f_files);
536 if (s->inf.filesystem->f_filesfree > 0)
537 _formatStatus("inodes free", Event_Resource, type, res, s, true, "%lld [%.1f%%]", s->inf.filesystem->f_filesfree, (float)100 * (float)s->inf.filesystem->f_filesfree / (float)s->inf.filesystem->f_files);
538 }
539 _printIOStatistics(type, res, s, &(s->inf.filesystem->read), "read");
540 _printIOStatistics(type, res, s, &(s->inf.filesystem->write), "write");
541 bool hasReadTime = Statistics_initialized(&(s->inf.filesystem->time.read));
542 bool hasWriteTime = Statistics_initialized(&(s->inf.filesystem->time.write));
543 bool hasWaitTime = Statistics_initialized(&(s->inf.filesystem->time.wait));
544 bool hasRunTime = Statistics_initialized(&(s->inf.filesystem->time.run));
545 double deltaOperations = Statistics_delta(&(s->inf.filesystem->read.operations)) + Statistics_delta(&(s->inf.filesystem->write.operations));
546 if (hasReadTime && hasWriteTime) {
547 double readTime = deltaOperations > 0. ? Statistics_deltaNormalize(&(s->inf.filesystem->time.read)) / deltaOperations : 0.;
548 double writeTime = deltaOperations > 0. ? Statistics_deltaNormalize(&(s->inf.filesystem->time.write)) / deltaOperations : 0.;
549 _formatStatus("service time", Event_Null, type, res, s, true, "%.3fms/operation (of which read %.3fms, write %.3fms)", readTime + writeTime, readTime, writeTime);
550 } else if (hasWaitTime && hasRunTime) {
551 double waitTime = deltaOperations > 0. ? Statistics_deltaNormalize(&(s->inf.filesystem->time.wait)) / deltaOperations : 0.;
552 double runTime = deltaOperations > 0. ? Statistics_deltaNormalize(&(s->inf.filesystem->time.run)) / deltaOperations : 0.;
553 _formatStatus("service time", Event_Null, type, res, s, true, "%.3fms/operation (of which queue %.3fms, active %.3fms)", waitTime + runTime, waitTime, runTime);
554 } else if (hasWaitTime) {
555 double waitTime = deltaOperations > 0. ? Statistics_deltaNormalize(&(s->inf.filesystem->time.wait)) / deltaOperations : 0.;
556 _formatStatus("service time", Event_Null, type, res, s, true, "%.3fms/operation", waitTime);
557 } else if (hasRunTime) {
558 double runTime = deltaOperations > 0. ? Statistics_deltaNormalize(&(s->inf.filesystem->time.run)) / deltaOperations : 0.;
559 _formatStatus("service time", Event_Null, type, res, s, true, "%.3fms/operation", runTime);
560 }
561 break;
562
563 case Service_Process:
564 _formatStatus("pid", Event_Pid, type, res, s, s->inf.process->pid >= 0, "%d", s->inf.process->pid);
565 _formatStatus("parent pid", Event_PPid, type, res, s, s->inf.process->ppid >= 0, "%d", s->inf.process->ppid);
566 _formatStatus("uid", Event_Uid, type, res, s, s->inf.process->uid >= 0, "%d", s->inf.process->uid);
567 _formatStatus("effective uid", Event_Uid, type, res, s, s->inf.process->euid >= 0, "%d", s->inf.process->euid);
568 _formatStatus("gid", Event_Gid, type, res, s, s->inf.process->gid >= 0, "%d", s->inf.process->gid);
569 _formatStatus("uptime", Event_Uptime, type, res, s, s->inf.process->uptime >= 0, "%s", _getUptime(s->inf.process->uptime, (char[256]){}));
570 if (Run.flags & Run_ProcessEngineEnabled) {
571 _formatStatus("threads", Event_Resource, type, res, s, s->inf.process->threads >= 0, "%d", s->inf.process->threads);
572 _formatStatus("children", Event_Resource, type, res, s, s->inf.process->children >= 0, "%d", s->inf.process->children);
573 _formatStatus("cpu", Event_Resource, type, res, s, s->inf.process->cpu_percent >= 0, "%.1f%%", s->inf.process->cpu_percent);
574 _formatStatus("cpu total", Event_Resource, type, res, s, s->inf.process->total_cpu_percent >= 0, "%.1f%%", s->inf.process->total_cpu_percent);
575 _formatStatus("memory", Event_Resource, type, res, s, s->inf.process->mem_percent >= 0, "%.1f%% [%s]", s->inf.process->mem_percent, Convert_bytes2str(s->inf.process->mem, (char[10]){}));
576 _formatStatus("memory total", Event_Resource, type, res, s, s->inf.process->total_mem_percent >= 0, "%.1f%% [%s]", s->inf.process->total_mem_percent, Convert_bytes2str(s->inf.process->total_mem, (char[10]){}));
577 #ifdef LINUX
578 _formatStatus("security attribute", Event_Invalid, type, res, s, *(s->inf.process->secattr), "%s", s->inf.process->secattr);
579 long long limit = s->inf.process->filedescriptors.limit.soft < s->inf.process->filedescriptors.limit.hard ? s->inf.process->filedescriptors.limit.soft : s->inf.process->filedescriptors.limit.hard;
580 if (limit > 0)
581 _formatStatus("filedescriptors", Event_Resource, type, res, s, s->inf.process->filedescriptors.open != -1LL, "%lld [%.1f%% of %lld limit]", s->inf.process->filedescriptors.open, (float)100 * (float)s->inf.process->filedescriptors.open / (float)limit, limit);
582 else
583 _formatStatus("filedescriptors", Event_Resource, type, res, s, s->inf.process->filedescriptors.open != -1LL, "N/A");
584 _formatStatus("total filedescriptors", Event_Resource, type, res, s, s->inf.process->filedescriptors.openTotal != -1LL, "%lld", s->inf.process->filedescriptors.openTotal);
585 #endif
586 }
587 _printIOStatistics(type, res, s, &(s->inf.process->read), "read");
588 _printIOStatistics(type, res, s, &(s->inf.process->write), "write");
589 break;
590
591 case Service_Program:
592 if (s->program->started) {
593 _formatStatus("last exit value", Event_Status, type, res, s, true, "%d", s->program->exitStatus);
594 _formatStatus("last output", Event_Status, type, res, s, StringBuffer_length(s->program->lastOutput), "%s", StringBuffer_toString(s->program->lastOutput));
595 }
596 break;
597
598 default:
599 break;
600 }
601 for (Icmp_T i = s->icmplist; i; i = i->next) {
602 if (i->is_available == Connection_Failed)
603 _formatStatus("ping response time", i->check_invers ? Event_Null : Event_Icmp, type, res, s, true, "connection failed");
604 else
605 _formatStatus("ping response time", i->check_invers ? Event_Icmp : Event_Null, type, res, s, i->is_available != Connection_Init && i->responsetime.current >= 0., "%s", Convert_time2str(i->responsetime.current, (char[11]){}));
606 }
607 for (Port_T p = s->portlist; p; p = p->next) {
608 if (p->is_available == Connection_Failed) {
609 Event_Type highlight = p->check_invers ? Event_Null : Event_Connection;
610 _formatStatus("port response time", highlight, type, res, s, true, "FAILED to [%s]:%d%s type %s/%s %sprotocol %s", p->hostname, p->target.net.port, Util_portRequestDescription(p), Util_portTypeDescription(p), Util_portIpDescription(p), p->target.net.ssl.options.flags ? "using TLS " : "", p->protocol->name);
611 } else {
612 char buf[STRLEN] = {};
613 if (p->target.net.ssl.options.flags)
614 snprintf(buf, sizeof(buf), "using TLS (certificate valid for %d days) ", p->target.net.ssl.certificate.validDays);
615 Event_Type highlight = p->check_invers ? Event_Connection : Event_Null;
616 if (p->target.net.ssl.certificate.validDays < p->target.net.ssl.certificate.minimumDays)
617 highlight |= Event_Timestamp;
618 _formatStatus("port response time", highlight, type, res, s, p->is_available != Connection_Init, "%s to %s:%d%s type %s/%s %sprotocol %s", Convert_time2str(p->responsetime.current, (char[11]){}), p->hostname, p->target.net.port, Util_portRequestDescription(p), Util_portTypeDescription(p), Util_portIpDescription(p), buf, p->protocol->name);
619 }
620 }
621 for (Port_T p = s->socketlist; p; p = p->next) {
622 if (p->is_available == Connection_Failed) {
623 _formatStatus("unix socket response time", p->check_invers ? Event_Null : Event_Connection, type, res, s, true, "FAILED to %s type %s protocol %s", p->target.unix.pathname, Util_portTypeDescription(p), p->protocol->name);
624 } else {
625 _formatStatus("unix socket response time", p->check_invers ? Event_Connection : Event_Null, type, res, s, p->is_available != Connection_Init, "%s to %s type %s protocol %s", Convert_time2str(p->responsetime.current, (char[11]){}), p->target.unix.pathname, Util_portTypeDescription(p), p->protocol->name);
626 }
627 }
628 }
629 _formatStatus("data collected", Event_Null, type, res, s, true, "%s", Time_string(s->collected.tv_sec, (char[32]){}));
630 }
631
632
_displayTableRow(HttpResponse res,bool escape,const char * class,const char * key,const char * value,...)633 __attribute__((format (printf, 5, 6))) static void _displayTableRow(HttpResponse res, bool escape, const char *class, const char *key, const char *value, ...) {
634 va_list ap;
635 va_start(ap, value);
636 char *_value = Str_vcat(value, ap);
637 va_end(ap);
638
639 if (STR_DEF(class))
640 StringBuffer_append(res->outputbuffer, "<tr class='%s'><td>%s</td><td>", class, key);
641 else
642 StringBuffer_append(res->outputbuffer, "<tr><td>%s</td><td>", key);
643 if (escape)
644 escapeHTML(res->outputbuffer, _value);
645 else
646 StringBuffer_append(res->outputbuffer, "%s", _value);
647 StringBuffer_append(res->outputbuffer, "</td></tr>");
648 FREE(_value);
649 }
650
651
_formatAction(HttpResponse res,const char * type,command_t cmd)652 static void _formatAction(HttpResponse res, const char *type, command_t cmd) {
653 char key[STRLEN] = {};
654 snprintf(key, sizeof(key), "%s program", type);
655 StringBuffer_T sb = StringBuffer_create(256);
656 StringBuffer_append(sb, "'%s'", Util_commandDescription(cmd, (char[STRLEN]){}));
657 if (cmd->has_uid)
658 StringBuffer_append(sb, " as uid %d", cmd->uid);
659 if (cmd->has_gid)
660 StringBuffer_append(sb, " as gid %d", cmd->gid);
661 StringBuffer_append(sb, " timeout %s", Convert_time2str(cmd->timeout, (char[11]){}));
662 _displayTableRow(res, true, NULL, key, "%s", StringBuffer_toString(sb));
663 StringBuffer_free(&sb);
664 }
665
666
_formatAddress(HttpResponse res,const char * type,Address_T addr)667 static void _formatAddress(HttpResponse res, const char *type, Address_T addr) {
668 char key[STRLEN] = {};
669 snprintf(key, sizeof(key), "Default mail %s", type);
670 if (addr->name)
671 _displayTableRow(res, true, NULL, key, "%s <%s>", addr->name, addr->address);
672 else
673 _displayTableRow(res, true, NULL, key, "%s", addr->address);
674 }
675
676
677 /**
678 * Called by the Processor (via the service method)
679 * to handle a POST request.
680 */
doPost(HttpRequest req,HttpResponse res)681 static void doPost(HttpRequest req, HttpResponse res) {
682 set_content_type(res, "text/html");
683 if (ACTION(RUNTIME))
684 handle_runtime_action(req, res);
685 else if (ACTION(VIEWLOG))
686 do_viewlog(req, res);
687 else if (ACTION(STATUS))
688 print_status(req, res, 1);
689 else if (ACTION(STATUS2))
690 print_status(req, res, 2);
691 else if (ACTION(SUMMARY))
692 print_summary(req, res);
693 else if (ACTION(REPORT))
694 _printReport(req, res);
695 else if (ACTION(DOACTION))
696 handle_doaction(req, res);
697 else
698 handle_service_action(req, res);
699 }
700
701
702 /**
703 * Called by the Processor (via the service method)
704 * to handle a GET request.
705 */
doGet(HttpRequest req,HttpResponse res)706 static void doGet(HttpRequest req, HttpResponse res) {
707 set_content_type(res, "text/html");
708 if (ACTION(HOME)) {
709 LOCK(Run.mutex)
710 do_home(res);
711 END_LOCK;
712 } else if (ACTION(RUNTIME)) {
713 handle_runtime(req, res);
714 } else if (ACTION(TEST)) {
715 is_monit_running(res);
716 } else if (ACTION(ABOUT)) {
717 do_about(res);
718 } else if (ACTION(FAVICON)) {
719 printFavicon(res);
720 } else if (ACTION(PING)) {
721 do_ping(res);
722 } else if (ACTION(GETID)) {
723 do_getid(res);
724 } else if (ACTION(STATUS)) {
725 print_status(req, res, 1);
726 } else if (ACTION(STATUS2)) {
727 print_status(req, res, 2);
728 } else if (ACTION(SUMMARY)) {
729 print_summary(req, res);
730 } else if (ACTION(REPORT)) {
731 _printReport(req, res);
732 } else if (ACTION(VIEWLOG)) {
733 do_viewlog(req, res);
734 } else {
735 handle_service(req, res);
736 }
737 }
738
739
740 /* ----------------------------------------------------------------- Helpers */
741
742
is_monit_running(HttpResponse res)743 static void is_monit_running(HttpResponse res) {
744 set_status(res, exist_daemon() ? SC_OK : SC_GONE);
745 }
746
747
printFavicon(HttpResponse res)748 static void printFavicon(HttpResponse res) {
749 static size_t l;
750 Socket_T S = res->S;
751 static unsigned char *favicon = NULL;
752
753 if (! favicon) {
754 favicon = CALLOC(sizeof(unsigned char), strlen(FAVICON_ICO));
755 l = decode_base64(favicon, FAVICON_ICO);
756 }
757 if (l) {
758 res->is_committed = true;
759 Socket_print(S, "HTTP/1.0 200 OK\r\n");
760 Socket_print(S, "Content-length: %lu\r\n", (unsigned long)l);
761 Socket_print(S, "Content-Type: image/x-icon\r\n");
762 Socket_print(S, "Connection: close\r\n\r\n");
763 if (Socket_write(S, favicon, l) < 0) {
764 Log_error("Error sending favicon data -- %s\n", STRERROR);
765 }
766 }
767 }
768
769
do_head(HttpResponse res,const char * path,const char * name,int refresh)770 static void do_head(HttpResponse res, const char *path, const char *name, int refresh) {
771 StringBuffer_T system_htmlescaped = escapeHTML(StringBuffer_create(16), Run.system->name);
772 StringBuffer_append(res->outputbuffer,
773 "<!DOCTYPE html>"\
774 "<html>"\
775 "<head>"\
776 "<title>Monit: %s</title> "\
777 "<style type=\"text/css\"> "\
778 " html, body {height: 100%%;margin: 0;} "\
779 " body {background-color: white;font: normal normal normal 16px/20px 'HelveticaNeue', Helvetica, Arial, sans-serif; color:#222;} "\
780 " h1 {padding:30px 0 10px 0; text-align:center;color:#222;font-size:28px;} "\
781 " h2 {padding:20px 0 10px 0; text-align:center;color:#555;font-size:22px;} "\
782 " a:hover {text-decoration: none;} "\
783 " a {text-decoration: underline;color:#222} "\
784 " table {border-collapse:collapse; border:0;} "\
785 " .stripe {background:#EDF5FF} "\
786 " .rule {background:#ddd} "\
787 " .red-text {color:#ff0000;} "\
788 " .green-text {color:#00ff00;} "\
789 " .gray-text {color:#999999;} "\
790 " .blue-text {color:#0000ff;} "\
791 " .yellow-text {color:#ffff00;} "\
792 " .orange-text {color:#ff8800;} "\
793 " .short {overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 350px;}"\
794 " .column {min-width: 80px;} "\
795 " .left {text-align:left} "\
796 " .right {text-align:right} "\
797 " .center {text-align:center} "\
798 " #wrap {min-height: 100%%;} "\
799 " #main {overflow:auto; padding-bottom:50px;} "\
800 " /*Opera Fix*/body:before {content:\"\";height:100%%;float:left;width:0;margin-top:-32767px;} "\
801 " #footer {position: relative;margin-top: -50px; height: 50px; clear:both; font-size:11px;color:#777;text-align:center;} "\
802 " #footer a {color:#333;} #footer a:hover {text-decoration: none;} "\
803 " #nav {background:#ddd;font:normal normal normal 14px/0px 'HelveticaNeue', Helvetica;} "\
804 " #nav td {padding:5px 10px;} "\
805 " #header {margin-bottom:30px;background:#EFF7FF} "\
806 " #nav, #header {border-bottom:1px solid #ccc;} "\
807 " #header-row {width:95%%;} "\
808 " #header-row th {padding:30px 10px 10px 10px;font-size:120%%;} "\
809 " #header-row td {padding:3px 10px;} "\
810 " #header-row .first {min-width:200px;width:200px;white-space:nowrap;overflow:hidden;text-overflow:ellipsis;} "\
811 " #status-table {width:95%%;} "\
812 " #status-table th {text-align:left;background:#edf5ff;font-weight:normal;} "\
813 " #status-table th, #status-table td, #status-table tr {border:1px solid #ccc;padding:5px;} "\
814 " #buttons {font-size:20px; margin:40px 0 20px 0;} "\
815 " #buttons td {padding-right:50px;} "\
816 " #buttons input {font-size:18px;padding:5px;} "\
817 "</style>"\
818 "<meta HTTP-EQUIV='REFRESH' CONTENT=%d> "\
819 "<meta HTTP-EQUIV='Expires' Content=0> "\
820 "<meta HTTP-EQUIV='Pragma' CONTENT='no-cache'> "\
821 "<meta charset='UTF-8'>" \
822 "<link rel='shortcut icon' href='favicon.ico'>"\
823 "</head>"\
824 "<body><div id='wrap'><div id='main'>" \
825 "<table id='nav' width='100%%'>"\
826 " <tr>"\
827 " <td width='20%%'><a href='.'>Home</a> > <a href='%s'>%s</a></td>"\
828 " <td width='60%%' style='text-align:center;'>Use <a href='https://mmonit.com/'>M/Monit</a> to manage all your Monit instances</td>"\
829 " <td width='20%%'><p class='right'><a href='_about'>Monit %s</a></td>"\
830 " </tr>"\
831 "</table>"\
832 "<center>",
833 StringBuffer_toString(system_htmlescaped), refresh, path, name, VERSION);
834 StringBuffer_free(&system_htmlescaped);
835 }
836
837
do_foot(HttpResponse res)838 static void do_foot(HttpResponse res) {
839 StringBuffer_append(res->outputbuffer,
840 "</center></div></div>"
841 "<div id='footer'>"
842 "Copyright © 2001-2021 <a href=\"http://tildeslash.com/\">Tildeslash</a>. All rights reserved. "
843 "<span style='margin-left:5px;'></span>"
844 "<a href=\"http://mmonit.com/monit/\">Monit web site</a> | "
845 "<a href=\"http://mmonit.com/wiki/\">Monit Wiki</a> | "
846 "<a href=\"http://mmonit.com/\">M/Monit</a>"
847 "</div></body></html>");
848 }
849
850
do_home(HttpResponse res)851 static void do_home(HttpResponse res) {
852 do_head(res, "", "", Run.polltime);
853 StringBuffer_T system_htmlescaped = escapeHTML(StringBuffer_create(16), Run.system->name);
854 StringBuffer_append(res->outputbuffer,
855 "<table id='header' width='100%%'>"
856 " <tr>"
857 " <td colspan=2 valign='top' class='left' width='100%%'>"
858 " <h1>Monit Service Manager</h1>"
859 " <p class='center'>Monit is <a href='_runtime'>running</a> on %s and monitoring:</p><br>"
860 " </td>"
861 " </tr>"
862 "</table>",
863 StringBuffer_toString(system_htmlescaped));
864 StringBuffer_free(&system_htmlescaped);
865
866 do_home_system(res);
867 do_home_process(res);
868 do_home_program(res);
869 do_home_filesystem(res);
870 do_home_file(res);
871 do_home_fifo(res);
872 do_home_directory(res);
873 do_home_net(res);
874 do_home_host(res);
875
876 do_foot(res);
877 }
878
879
do_about(HttpResponse res)880 static void do_about(HttpResponse res) {
881 StringBuffer_append(res->outputbuffer,
882 "<html><head><title>about monit</title></head><body bgcolor=white>"
883 "<br><h1><center><a href='http://mmonit.com/monit/'>"
884 "monit " VERSION "</a></center></h1>");
885 StringBuffer_append(res->outputbuffer,
886 "<ul>"
887 "<li style='padding-bottom:10px;'>Copyright © 2001-2021 <a "
888 "href='http://tildeslash.com/'>Tildeslash Ltd"
889 "</a>. All Rights Reserved.</li></ul>");
890 StringBuffer_append(res->outputbuffer, "<hr size='1'>");
891 StringBuffer_append(res->outputbuffer,
892 "<p>This program is free software; you can redistribute it and/or "
893 "modify it under the terms of the GNU Affero General Public License version 3</p>"
894 "<p>This program is distributed in the hope that it will be useful, but "
895 "WITHOUT ANY WARRANTY; without even the implied warranty of "
896 "MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the "
897 "<a href='http://www.gnu.org/licenses/agpl.html'>"
898 "GNU AFFERO GENERAL PUBLIC LICENSE</a> for more details.</p>");
899 StringBuffer_append(res->outputbuffer,
900 "<center><p style='padding-top:20px;'>[<a href='.'>Back to Monit</a>]</p></body></html>");
901 }
902
903
do_ping(HttpResponse res)904 static void do_ping(HttpResponse res) {
905 StringBuffer_append(res->outputbuffer, "pong");
906 }
907
908
do_getid(HttpResponse res)909 static void do_getid(HttpResponse res) {
910 escapeHTML(res->outputbuffer, Run.id);
911 }
912
913
do_runtime(HttpRequest req,HttpResponse res)914 static void do_runtime(HttpRequest req, HttpResponse res) {
915 int pid = exist_daemon();
916 char buf[STRLEN];
917
918 do_head(res, "_runtime", "Runtime", 1000);
919 StringBuffer_append(res->outputbuffer,
920 "<h2>Monit runtime status</h2>");
921 StringBuffer_append(res->outputbuffer, "<table id='status-table'><tr>"
922 "<th width='40%%'>Parameter</th>"
923 "<th width='60%%'>Value</th></tr>");
924 _displayTableRow(res, true, NULL, "Monit ID", "%s", Run.id);
925 _displayTableRow(res, true, NULL, "Host", "%s", Run.system->name);
926 _displayTableRow(res, false, NULL, "Process id", "%d", pid);
927 _displayTableRow(res, true, NULL, "Effective user running Monit", "%s", Run.Env.user);
928 _displayTableRow(res, true, NULL, "Controlfile", "%s", Run.files.control);
929 if (Run.files.log)
930 _displayTableRow(res, true, NULL, "Logfile", "%s", Run.files.log);
931 _displayTableRow(res, true, NULL, "Pidfile", "%s", Run.files.pid);
932 _displayTableRow(res, true, NULL, "State file", "%s", Run.files.state);
933 _displayTableRow(res, true, NULL, "Debug", "%s", Run.debug ? "True" : "False");
934 _displayTableRow(res, true, NULL, "Log", "%s", (Run.flags & Run_Log) ? "True" : "False");
935 _displayTableRow(res, true, NULL, "Use syslog", "%s", (Run.flags & Run_UseSyslog) ? "True" : "False");
936
937 if (Run.eventlist_dir) {
938 if (Run.eventlist_slots < 0)
939 _displayTableRow(res, true, NULL, "Event queue", "base directory %s with unlimited slots", Run.eventlist_dir);
940 else
941 _displayTableRow(res, true, NULL, "Event queue", "base directory %s with %d slots", Run.eventlist_dir, Run.eventlist_slots);
942 }
943 #ifdef HAVE_OPENSSL
944 {
945 char opt[STRLEN] = {};
946 const char *options = Ssl_printOptions(&(Run.ssl), opt, STRLEN);
947 if (options && *options)
948 _displayTableRow(res, true, NULL, "SSL options", "%s", options);
949 }
950 #endif
951 if (Run.mmonits) {
952 StringBuffer_append(res->outputbuffer, "<tr><td>M/Monit server(s)</td><td>");
953 for (Mmonit_T c = Run.mmonits; c; c = c->next)
954 {
955 escapeHTML(res->outputbuffer, c->url->url);
956 StringBuffer_append(res->outputbuffer, " with timeout %s", Convert_time2str(c->timeout, (char[11]){}));
957 #ifdef HAVE_OPENSSL
958 if (c->ssl.flags) {
959 StringBuffer_append(res->outputbuffer, " using TLS");
960 const char *options = Ssl_printOptions(&c->ssl, (char[STRLEN]){}, STRLEN);
961 if (options && *options)
962 StringBuffer_append(res->outputbuffer, " with options {%s}", options);
963 if (c->ssl.checksum) {
964 StringBuffer_append(res->outputbuffer, " and certificate checksum %s equal to '", checksumnames[c->ssl.checksumType]);
965 escapeHTML(res->outputbuffer, c->ssl.checksum);
966 StringBuffer_append(res->outputbuffer, "'");
967 }
968 }
969 #endif
970 if (Run.flags & Run_MmonitCredentials && c->url->user)
971 StringBuffer_append(res->outputbuffer, " with credentials");
972 if (c->next)
973 StringBuffer_append(res->outputbuffer, "</td></tr><tr><td> </td><td>");
974 }
975 StringBuffer_append(res->outputbuffer, "</td></tr>");
976 }
977 if (Run.mailservers) {
978 StringBuffer_append(res->outputbuffer, "<tr><td>Mail server(s)</td><td>");
979 for (MailServer_T mta = Run.mailservers; mta; mta = mta->next) {
980 escapeHTML(res->outputbuffer, mta->host);
981 StringBuffer_append(res->outputbuffer, ":%d", mta->port);
982 #ifdef HAVE_OPENSSL
983 if (mta->ssl.flags) {
984 StringBuffer_append(res->outputbuffer, " using TLS");
985 char opt[STRLEN] = {};
986 const char *options = Ssl_printOptions(&mta->ssl, opt, STRLEN);
987 if (options && *options)
988 StringBuffer_append(res->outputbuffer, " with options {%s}", options);
989 if (mta->ssl.checksum) {
990 StringBuffer_append(res->outputbuffer, " and certificate checksum %s equal to '", checksumnames[mta->ssl.checksumType]);
991 escapeHTML(res->outputbuffer, mta->ssl.checksum);
992 StringBuffer_append(res->outputbuffer, "'");
993 }
994 }
995 #endif
996 if (mta->next)
997 StringBuffer_append(res->outputbuffer, "</td></tr><tr><td> </td><td>");
998 }
999 StringBuffer_append(res->outputbuffer, "</td></tr>");
1000 }
1001 if (Run.MailFormat.from)
1002 _formatAddress(res, "from", Run.MailFormat.from);
1003 if (Run.MailFormat.replyto)
1004 _formatAddress(res, "reply to", Run.MailFormat.replyto);
1005 if (Run.MailFormat.subject)
1006 _displayTableRow(res, true, NULL, "Default mail subject", "%s", Run.MailFormat.subject);
1007 if (Run.MailFormat.message)
1008 _displayTableRow(res, true, NULL, "Default mail message", "%s", Run.MailFormat.message);
1009 _displayTableRow(res, false, NULL, "Limit for Send/Expect buffer", "%s", Convert_bytes2str(Run.limits.sendExpectBuffer, buf));
1010 _displayTableRow(res, false, NULL, "Limit for file content buffer", "%s", Convert_bytes2str(Run.limits.fileContentBuffer, buf));
1011 _displayTableRow(res, false, NULL, "Limit for HTTP content buffer", "%s", Convert_bytes2str(Run.limits.httpContentBuffer, buf));
1012 _displayTableRow(res, false, NULL, "Limit for program output", "%s", Convert_bytes2str(Run.limits.programOutput, buf));
1013 _displayTableRow(res, false, NULL, "Limit for network timeout", "%s", Convert_time2str(Run.limits.networkTimeout, (char[11]){}));
1014 _displayTableRow(res, false, NULL, "Limit for check program timeout", "%s", Convert_time2str(Run.limits.programTimeout, (char[11]){}));
1015 _displayTableRow(res, false, NULL, "Limit for service stop timeout", "%s", Convert_time2str(Run.limits.stopTimeout, (char[11]){}));
1016 _displayTableRow(res, false, NULL, "Limit for service start timeout", "%s", Convert_time2str(Run.limits.startTimeout, (char[11]){}));
1017 _displayTableRow(res, false, NULL, "Limit for service restart timeout", "%s", Convert_time2str(Run.limits.restartTimeout, (char[11]){}));
1018 _displayTableRow(res, false, NULL, "On reboot", "%s", onrebootnames[Run.onreboot]);
1019 _displayTableRow(res, false, NULL, "Poll time", "%d seconds with start delay %d seconds", Run.polltime, Run.startdelay);
1020 if (Run.httpd.flags & Httpd_Net) {
1021 _displayTableRow(res, true, NULL, "httpd bind address", "%s", Run.httpd.socket.net.address ? Run.httpd.socket.net.address : "Any/All");
1022 _displayTableRow(res, false, NULL, "httpd portnumber", "%d", Run.httpd.socket.net.port);
1023 #ifdef HAVE_OPENSSL
1024 const char *options = Ssl_printOptions(&(Run.httpd.socket.net.ssl), (char[STRLEN]){}, STRLEN);
1025 if (options && *options)
1026 _displayTableRow(res, false, NULL, "httpd encryption", "%s", options);
1027 #endif
1028 }
1029 if (Run.httpd.flags & Httpd_Unix)
1030 _displayTableRow(res, true, NULL, "httpd unix socket", "%s", Run.httpd.socket.unix.path);
1031 _displayTableRow(res, false, NULL, "httpd signature", "%s", Run.httpd.flags & Httpd_Signature ? "True" : "False");
1032 _displayTableRow(res, false, NULL, "httpd auth. style", "%s", Run.httpd.credentials && Engine_hasAllow() ?
1033 "Basic Authentication and Host/Net allow list" : Run.httpd.credentials ? "Basic Authentication" : Engine_hasAllow() ? "Host/Net allow list" : "No authentication");
1034 print_alerts(res, Run.maillist);
1035 StringBuffer_append(res->outputbuffer, "</table>");
1036 if (! is_readonly(req)) {
1037 StringBuffer_append(res->outputbuffer,
1038 "<table id='buttons'><tr>");
1039 StringBuffer_append(res->outputbuffer,
1040 "<td style='color:red;'>"
1041 "<form method=POST action='_runtime'>Stop Monit http server? "
1042 "<input type=hidden name='securitytoken' value='%s'>"
1043 "<input type=hidden name='action' value='stop'>"
1044 "<input type=submit value='Go'>"
1045 "</form>"
1046 "</td>",
1047 res->token);
1048 StringBuffer_append(res->outputbuffer,
1049 "<td>"
1050 "<form method=POST action='_runtime'>Force validate now? "
1051 "<input type=hidden name='securitytoken' value='%s'>"
1052 "<input type=hidden name='action' value='validate'>"
1053 "<input type=submit value='Go'>"
1054 "</form>"
1055 "</td>",
1056 res->token);
1057
1058 if ((Run.flags & Run_Log) && ! (Run.flags & Run_UseSyslog)) {
1059 StringBuffer_append(res->outputbuffer,
1060 "<td>"
1061 "<form method=POST action='_viewlog'>View Monit logfile? "
1062 "<input type=hidden name='securitytoken' value='%s'>"
1063 "<input type=submit value='Go'>"
1064 "</form>"
1065 "</td>",
1066 res->token);
1067 }
1068 StringBuffer_append(res->outputbuffer,
1069 "</tr></table>");
1070 }
1071 do_foot(res);
1072 }
1073
1074
do_viewlog(HttpRequest req,HttpResponse res)1075 static void do_viewlog(HttpRequest req, HttpResponse res) {
1076 if (is_readonly(req)) {
1077 send_error(req, res, SC_FORBIDDEN, "You do not have sufficient privileges to access this page");
1078 return;
1079 }
1080 do_head(res, "_viewlog", "View log", 100);
1081 if ((Run.flags & Run_Log) && ! (Run.flags & Run_UseSyslog)) {
1082 FILE *f = fopen(Run.files.log, "r");
1083 if (f) {
1084 size_t n;
1085 size_t total = 0;
1086 char buf[512];
1087 StringBuffer_append(res->outputbuffer, "<br><p><form><textarea cols=120 rows=30 readonly>");
1088 while (total < VIEWLOG_LIMIT && (n = fread(buf, sizeof(char), sizeof(buf) - 1, f)) > 0) {
1089 total += n;
1090 buf[n] = 0;
1091 escapeHTML(res->outputbuffer, buf);
1092 }
1093 fclose(f);
1094 StringBuffer_append(res->outputbuffer, "</textarea></form>");
1095 } else {
1096 StringBuffer_append(res->outputbuffer, "Error opening logfile: %s", STRERROR);
1097 }
1098 } else {
1099 StringBuffer_append(res->outputbuffer,
1100 "<b>Cannot view logfile:</b><br>");
1101 if (! (Run.flags & Run_Log))
1102 StringBuffer_append(res->outputbuffer, "Monit was started without logging");
1103 else
1104 StringBuffer_append(res->outputbuffer, "Monit uses syslog");
1105 }
1106 do_foot(res);
1107 }
1108
1109
handle_service(HttpRequest req,HttpResponse res)1110 static void handle_service(HttpRequest req, HttpResponse res) {
1111 char *name = req->url;
1112 if (! name) {
1113 send_error(req, res, SC_NOT_FOUND, "Service name required");
1114 return;
1115 }
1116 Service_T s = Util_getService(++name);
1117 if (! s) {
1118 send_error(req, res, SC_NOT_FOUND, "There is no service named \"%s\"", name);
1119 return;
1120 }
1121 do_service(req, res, s);
1122 }
1123
1124
1125 // Do action for the service (the service name is the last component of the URL path)
handle_service_action(HttpRequest req,HttpResponse res)1126 static void handle_service_action(HttpRequest req, HttpResponse res) {
1127 char *name = req->url;
1128 if (! name) {
1129 send_error(req, res, SC_NOT_FOUND, "Service name required");
1130 return;
1131 }
1132 struct ServiceMap_T ap = {.found = 0, .data.action.name = get_parameter(req, "action")};
1133 if (ap.data.action.name) {
1134 if (is_readonly(req)) {
1135 send_error(req, res, SC_FORBIDDEN, "You do not have sufficient privileges to access this page");
1136 } else {
1137 ap.data.action.id = Util_getAction(ap.data.action.name);
1138 if (ap.data.action.id == Action_Ignored) {
1139 send_error(req, res, SC_BAD_REQUEST, "Invalid action \"%s\"", ap.data.action.name);
1140 } else {
1141 Service_T s = Util_getService(++name);
1142 if (! s) {
1143 send_error(req, res, SC_NOT_FOUND, "There is no service named \"%s\"", name);
1144 return;
1145 }
1146 _serviceMapAction(s, &ap);
1147 Run.flags |= Run_ActionPending; /* set the global flag */
1148 do_wakeupcall();
1149 do_service(req, res, s);
1150 }
1151 }
1152 }
1153 }
1154
1155
1156 // Do action for all services listed in "service" HTTP parameter (may have multiple value)
handle_doaction(HttpRequest req,HttpResponse res)1157 static void handle_doaction(HttpRequest req, HttpResponse res) {
1158 struct ServiceMap_T ap = {.found = 0, .data.action.name = get_parameter(req, "action")};
1159 if (ap.data.action.name) {
1160 if (is_readonly(req)) {
1161 send_error(req, res, SC_FORBIDDEN, "You do not have sufficient privileges to access this page");
1162 return;
1163 } else {
1164 if ((ap.data.action.id = Util_getAction(ap.data.action.name)) == Action_Ignored) {
1165 send_error(req, res, SC_BAD_REQUEST, "Invalid action \"%s\"", ap.data.action.name);
1166 return;
1167 }
1168 for (HttpParameter p = req->params; p; p = p->next) {
1169 if (IS(p->name, "service")) {
1170 _serviceMapByName(p->value, _serviceMapAction, &ap);
1171 if (ap.found == 0) {
1172 send_error(req, res, SC_BAD_REQUEST, "There is no service named \"%s\"", p->value ? p->value : "");
1173 return;
1174 }
1175 }
1176 }
1177 if (ap.found > 0) {
1178 Run.flags |= Run_ActionPending;
1179 do_wakeupcall();
1180 }
1181 }
1182 }
1183 }
1184
1185
handle_runtime(HttpRequest req,HttpResponse res)1186 static void handle_runtime(HttpRequest req, HttpResponse res) {
1187 LOCK(Run.mutex)
1188 do_runtime(req, res);
1189 END_LOCK;
1190 }
1191
1192
handle_runtime_action(HttpRequest req,HttpResponse res)1193 static void handle_runtime_action(HttpRequest req, HttpResponse res) {
1194 const char *action = get_parameter(req, "action");
1195 if (action) {
1196 if (is_readonly(req)) {
1197 send_error(req, res, SC_FORBIDDEN, "You do not have sufficient privileges to access this page");
1198 return;
1199 }
1200 if (IS(action, "validate")) {
1201 Log_info("The Monit http server woke up on user request\n");
1202 do_wakeupcall();
1203 } else if (IS(action, "stop")) {
1204 Log_info("The Monit http server stopped on user request\n");
1205 send_error(req, res, SC_SERVICE_UNAVAILABLE, "The Monit http server is stopped");
1206 Engine_stop();
1207 return;
1208 }
1209 }
1210 handle_runtime(req, res);
1211 }
1212
1213
do_service(HttpRequest req,HttpResponse res,Service_T s)1214 static void do_service(HttpRequest req, HttpResponse res, Service_T s) {
1215 ASSERT(s);
1216 char buf[STRLEN] = {};
1217
1218 do_head(res, s->name_urlescaped, StringBuffer_toString(s->name_htmlescaped), Run.polltime);
1219 StringBuffer_append(res->outputbuffer,
1220 "<h2>%s status</h2>"
1221 "<table id='status-table'>"
1222 "<tr>"
1223 "<th width='30%%'>Parameter</th>"
1224 "<th width='70%%'>Value</th>"
1225 "</tr>",
1226 servicetypes[s->type]);
1227 _displayTableRow(res, true, NULL, "Name", "%s", s->name);
1228 if (s->type == Service_Process)
1229 _displayTableRow(res, true, NULL, s->matchlist ? "Match" : "Pid file", "%s", s->path);
1230 else if (s->type == Service_Host)
1231 _displayTableRow(res, true, NULL, "Address", "%s", s->path);
1232 else if (s->type == Service_Net)
1233 _displayTableRow(res, true, NULL, "Interface", "%s", s->path);
1234 else if (s->type != Service_System)
1235 _displayTableRow(res, true, NULL, "Path", "%s", s->path);
1236 _displayTableRow(res, false, NULL, "Status", "%s", get_service_status(HTML, s, buf, sizeof(buf)));
1237 for (ServiceGroup_T sg = servicegrouplist; sg; sg = sg->next) {
1238 for (list_t m = sg->members->head; m; m = m->next)
1239 if (m->e == s)
1240 _displayTableRow(res, false, NULL, "Group", "%s", sg->name);
1241 }
1242 _displayTableRow(res, false, NULL, "Monitoring status", "%s", get_monitoring_status(HTML, s, buf, sizeof(buf)));
1243 _displayTableRow(res, false, NULL, "Monitoring mode", "%s", modenames[s->mode]);
1244 _displayTableRow(res, false, NULL, "On reboot", "%s", onrebootnames[s->onreboot]);
1245 for (Dependant_T d = s->dependantlist; d; d = d->next) {
1246 if (d->dependant != NULL)
1247 _displayTableRow(res, false, NULL, "Depends on service", "<a href='%s'>%s</a>", d->dependant_urlescaped, StringBuffer_toString(d->dependant_htmlescaped));
1248 }
1249 if (s->start)
1250 _formatAction(res, "Start", s->start);
1251 if (s->stop)
1252 _formatAction(res, "Stop", s->stop);
1253 if (s->restart)
1254 _formatAction(res, "Restart", s->restart);
1255 if (s->every.type != Every_Cycle) {
1256 if (s->every.type == Every_SkipCycles)
1257 _displayTableRow(res, false, NULL, "Check service", "every %d cycle", s->every.spec.cycle.number);
1258 else if (s->every.type == Every_Cron)
1259 _displayTableRow(res, false, NULL, "Check service", "every <code>\"%s\"</code>", s->every.spec.cron);
1260 else if (s->every.type == Every_NotInCron)
1261 _displayTableRow(res, false, NULL, "Check service", "not every <code>\"%s\"</code>", s->every.spec.cron);
1262 }
1263 _printStatus(HTML, res, s);
1264 // Rules
1265 print_service_rules_timeout(res, s);
1266 print_service_rules_nonexistence(res, s);
1267 print_service_rules_existence(res, s);
1268 print_service_rules_icmp(res, s);
1269 print_service_rules_port(res, s);
1270 print_service_rules_socket(res, s);
1271 print_service_rules_perm(res, s);
1272 print_service_rules_uid(res, s);
1273 print_service_rules_euid(res, s);
1274 print_service_rules_secattr(res, s);
1275 print_service_rules_filedescriptors(res, s);
1276 print_service_rules_gid(res, s);
1277 print_service_rules_timestamp(res, s);
1278 print_service_rules_fsflags(res, s);
1279 print_service_rules_filesystem(res, s);
1280 print_service_rules_size(res, s);
1281 print_service_rules_linkstatus(res, s);
1282 print_service_rules_linkspeed(res, s);
1283 print_service_rules_linksaturation(res, s);
1284 print_service_rules_uploadbytes(res, s);
1285 print_service_rules_uploadpackets(res, s);
1286 print_service_rules_downloadbytes(res, s);
1287 print_service_rules_downloadpackets(res, s);
1288 print_service_rules_uptime(res, s);
1289 print_service_rules_content(res, s);
1290 print_service_rules_checksum(res, s);
1291 print_service_rules_pid(res, s);
1292 print_service_rules_ppid(res, s);
1293 print_service_rules_program(res, s);
1294 print_service_rules_resource(res, s);
1295 print_alerts(res, s->maillist);
1296 StringBuffer_append(res->outputbuffer, "</table>");
1297 print_buttons(req, res, s);
1298 do_foot(res);
1299 }
1300
1301
do_home_system(HttpResponse res)1302 static void do_home_system(HttpResponse res) {
1303 Service_T s = Run.system;
1304 char buf[STRLEN];
1305
1306 StringBuffer_append(res->outputbuffer,
1307 "<table id='header-row'>"
1308 "<tr>"
1309 "<th class='left first'>System</th>"
1310 "<th class='left'>Status</th>"
1311 "<th class='right column'>Load</th>"
1312 "<th class='right column'>CPU</th>"
1313 "<th class='right column'>Memory</th>"
1314 "<th class='right column'>Swap</th>"
1315 "</tr>"
1316 "<tr class='stripe'>"
1317 "<td class='left'><a href='%s'>%s</a></td>"
1318 "<td class='left'>%s</td>"
1319 "<td class='right column'>[%.2f] [%.2f] [%.2f]</td>"
1320 "<td class='right column'>",
1321 s->name_urlescaped, StringBuffer_toString(s->name_htmlescaped),
1322 get_service_status(HTML, s, buf, sizeof(buf)),
1323 systeminfo.loadavg[0], systeminfo.loadavg[1], systeminfo.loadavg[2]);
1324 if (systeminfo.statisticsAvailable & Statistics_CpuUser)
1325 StringBuffer_append(res->outputbuffer, "%.1f%%us ", systeminfo.cpu.usage.user > 0. ? systeminfo.cpu.usage.user : 0.);
1326 if (systeminfo.statisticsAvailable & Statistics_CpuSystem)
1327 StringBuffer_append(res->outputbuffer, "%.1f%%sy ", systeminfo.cpu.usage.system > 0. ? systeminfo.cpu.usage.system : 0.);
1328 if (systeminfo.statisticsAvailable & Statistics_CpuNice)
1329 StringBuffer_append(res->outputbuffer, "%.1f%%ni ", systeminfo.cpu.usage.nice > 0. ? systeminfo.cpu.usage.nice : 0.);
1330 if (systeminfo.statisticsAvailable & Statistics_CpuIOWait)
1331 StringBuffer_append(res->outputbuffer, "%.1f%%wa ", systeminfo.cpu.usage.iowait > 0. ? systeminfo.cpu.usage.iowait : 0.);
1332 StringBuffer_append(res->outputbuffer,
1333 "</td>");
1334 StringBuffer_append(res->outputbuffer,
1335 "<td class='right column'>%.1f%% [%s]</td>",
1336 systeminfo.memory.usage.percent, Convert_bytes2str(systeminfo.memory.usage.bytes, buf));
1337 StringBuffer_append(res->outputbuffer,
1338 "<td class='right column'>%.1f%% [%s]</td>",
1339 systeminfo.swap.usage.percent, Convert_bytes2str(systeminfo.swap.usage.bytes, buf));
1340 StringBuffer_append(res->outputbuffer,
1341 "</tr>"
1342 "</table>");
1343 }
1344
1345
do_home_process(HttpResponse res)1346 static void do_home_process(HttpResponse res) {
1347 char buf[STRLEN];
1348 bool on = true;
1349 bool header = true;
1350
1351 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
1352 if (s->type != Service_Process)
1353 continue;
1354 if (header) {
1355 StringBuffer_append(res->outputbuffer,
1356 "<table id='header-row'>"
1357 "<tr>"
1358 "<th class='left' class='first'>Process</th>"
1359 "<th class='left'>Status</th>"
1360 "<th class='right'>Uptime</th>"
1361 "<th class='right'>CPU Total</b></th>"
1362 "<th class='right'>Memory Total</th>"
1363 "<th class='right column'>Read</th>"
1364 "<th class='right column'>Write</th>"
1365 "</tr>");
1366 header = false;
1367 }
1368 StringBuffer_append(res->outputbuffer,
1369 "<tr%s>"
1370 "<td class='left'><a href='%s'>%s</a></td>"
1371 "<td class='left'>%s</td>",
1372 on ? " class='stripe'" : "",
1373 s->name_urlescaped, StringBuffer_toString(s->name_htmlescaped),
1374 get_service_status(HTML, s, buf, sizeof(buf)));
1375 if (! (Run.flags & Run_ProcessEngineEnabled) || ! Util_hasServiceStatus(s) || s->inf.process->uptime < 0) {
1376 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1377 } else {
1378 StringBuffer_append(res->outputbuffer, "<td class='right'>%s</td>", _getUptime(s->inf.process->uptime, (char[256]){}));
1379 }
1380 if (! (Run.flags & Run_ProcessEngineEnabled) || ! Util_hasServiceStatus(s) || s->inf.process->total_cpu_percent < 0) {
1381 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1382 } else {
1383 StringBuffer_append(res->outputbuffer, "<td class='right%s'>%.1f%%</td>", (s->error & Event_Resource) ? " red-text" : "", s->inf.process->total_cpu_percent);
1384 }
1385 if (! (Run.flags & Run_ProcessEngineEnabled) || ! Util_hasServiceStatus(s) || s->inf.process->total_mem_percent < 0) {
1386 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1387 } else {
1388 StringBuffer_append(res->outputbuffer, "<td class='right%s'>%.1f%% [%s]</td>", (s->error & Event_Resource) ? " red-text" : "", s->inf.process->total_mem_percent, Convert_bytes2str(s->inf.process->total_mem, buf));
1389 }
1390 bool hasReadBytes = Statistics_initialized(&(s->inf.process->read.bytes));
1391 bool hasReadOperations = Statistics_initialized(&(s->inf.process->read.operations));
1392 if (! (Run.flags & Run_ProcessEngineEnabled) || ! Util_hasServiceStatus(s) || (! hasReadBytes && ! hasReadOperations)) {
1393 StringBuffer_append(res->outputbuffer, "<td class='right column'>-</td>");
1394 } else if (hasReadBytes) {
1395 StringBuffer_append(res->outputbuffer, "<td class='right column%s'>%s/s</td>", (s->error & Event_Resource) ? " red-text" : "", Convert_bytes2str(Statistics_deltaNormalize(&(s->inf.process->read.bytes)), (char[10]){}));
1396 } else if (hasReadOperations) {
1397 StringBuffer_append(res->outputbuffer, "<td class='right column%s'>%.1f/s</td>", (s->error & Event_Resource) ? " red-text" : "", Statistics_deltaNormalize(&(s->inf.process->read.operations)));
1398 }
1399 bool hasWriteBytes = Statistics_initialized(&(s->inf.process->write.bytes));
1400 bool hasWriteOperations = Statistics_initialized(&(s->inf.process->write.operations));
1401 if (! (Run.flags & Run_ProcessEngineEnabled) || ! Util_hasServiceStatus(s) || (! hasWriteBytes && ! hasWriteOperations)) {
1402 StringBuffer_append(res->outputbuffer, "<td class='right column'>-</td>");
1403 } else if (hasWriteBytes) {
1404 StringBuffer_append(res->outputbuffer, "<td class='right column%s'>%s/s</td>", (s->error & Event_Resource) ? " red-text" : "", Convert_bytes2str(Statistics_deltaNormalize(&(s->inf.process->write.bytes)), (char[10]){}));
1405 } else if (hasWriteOperations) {
1406 StringBuffer_append(res->outputbuffer, "<td class='right column%s'>%.1f/s</td>", (s->error & Event_Resource) ? " red-text" : "", Statistics_deltaNormalize(&(s->inf.process->write.operations)));
1407 }
1408 StringBuffer_append(res->outputbuffer, "</tr>");
1409 on = ! on;
1410 }
1411 if (! header)
1412 StringBuffer_append(res->outputbuffer, "</table>");
1413 }
1414
1415
do_home_program(HttpResponse res)1416 static void do_home_program(HttpResponse res) {
1417 char buf[STRLEN];
1418 bool on = true;
1419 bool header = true;
1420
1421 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
1422 if (s->type != Service_Program)
1423 continue;
1424 if (header) {
1425 StringBuffer_append(res->outputbuffer,
1426 "<table id='header-row'>"
1427 "<tr>"
1428 "<th class='left' class='first'>Program</th>"
1429 "<th class='left'>Status</th>"
1430 "<th class='left'>Output</th>"
1431 "<th class='right'>Last started</th>"
1432 "<th class='right'>Exit value</th>"
1433 "</tr>");
1434 header = false;
1435 }
1436 StringBuffer_append(res->outputbuffer,
1437 "<tr %s>"
1438 "<td class='left'><a href='%s'>%s</a></td>"
1439 "<td class='left'>%s</td>",
1440 on ? "class='stripe'" : "",
1441 s->name_urlescaped, StringBuffer_toString(s->name_htmlescaped),
1442 get_service_status(HTML, s, buf, sizeof(buf)));
1443 if (! Util_hasServiceStatus(s)) {
1444 StringBuffer_append(res->outputbuffer, "<td class='left'>-</td>");
1445 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1446 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1447 } else {
1448 if (s->program->started) {
1449 StringBuffer_append(res->outputbuffer, "<td class='left short'>");
1450 if (StringBuffer_length(s->program->lastOutput)) {
1451 // Print first line only (escape HTML characters if any)
1452 const char *output = StringBuffer_toString(s->program->lastOutput);
1453 for (int i = 0; output[i]; i++) {
1454 if (output[i] == '<')
1455 StringBuffer_append(res->outputbuffer, "<");
1456 else if (output[i] == '>')
1457 StringBuffer_append(res->outputbuffer, ">");
1458 else if (output[i] == '&')
1459 StringBuffer_append(res->outputbuffer, "&");
1460 else if (output[i] == '\r' || output[i] == '\n')
1461 break;
1462 else
1463 StringBuffer_append(res->outputbuffer, "%c", output[i]);
1464 }
1465 } else {
1466 StringBuffer_append(res->outputbuffer, "no output");
1467 }
1468 StringBuffer_append(res->outputbuffer, "</td>");
1469 StringBuffer_append(res->outputbuffer, "<td class='right'>%s</td>", Time_fmt((char[32]){}, 32, "%d %b %Y %H:%M:%S", s->program->started));
1470 StringBuffer_append(res->outputbuffer, "<td class='right'>%d</td>", s->program->exitStatus);
1471 } else {
1472 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1473 StringBuffer_append(res->outputbuffer, "<td class='right'>Not yet started</td>");
1474 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1475 }
1476 }
1477 StringBuffer_append(res->outputbuffer, "</tr>");
1478 on = ! on;
1479 }
1480 if (! header)
1481 StringBuffer_append(res->outputbuffer, "</table>");
1482
1483 }
1484
1485
do_home_net(HttpResponse res)1486 static void do_home_net(HttpResponse res) {
1487 char buf[STRLEN];
1488 bool on = true;
1489 bool header = true;
1490
1491 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
1492 if (s->type != Service_Net)
1493 continue;
1494 if (header) {
1495 StringBuffer_append(res->outputbuffer,
1496 "<table id='header-row'>"
1497 "<tr>"
1498 "<th class='left first'>Net</th>"
1499 "<th class='left'>Status</th>"
1500 "<th class='right'>Upload</th>"
1501 "<th class='right'>Download</th>"
1502 "</tr>");
1503 header = false;
1504 }
1505 StringBuffer_append(res->outputbuffer,
1506 "<tr %s>"
1507 "<td class='left'><a href='%s'>%s</a></td>"
1508 "<td class='left'>%s</td>",
1509 on ? "class='stripe'" : "",
1510 s->name_urlescaped, StringBuffer_toString(s->name_htmlescaped),
1511 get_service_status(HTML, s, buf, sizeof(buf)));
1512 if (! Util_hasServiceStatus(s) || Link_getState(s->inf.net->stats) != 1) {
1513 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1514 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1515 } else {
1516 StringBuffer_append(res->outputbuffer, "<td class='right'>%s/s</td>", Convert_bytes2str(Link_getBytesOutPerSecond(s->inf.net->stats), buf));
1517 StringBuffer_append(res->outputbuffer, "<td class='right'>%s/s</td>", Convert_bytes2str(Link_getBytesInPerSecond(s->inf.net->stats), buf));
1518 }
1519 StringBuffer_append(res->outputbuffer, "</tr>");
1520 on = ! on;
1521 }
1522 if (! header)
1523 StringBuffer_append(res->outputbuffer, "</table>");
1524 }
1525
1526
do_home_filesystem(HttpResponse res)1527 static void do_home_filesystem(HttpResponse res) {
1528 char buf[STRLEN];
1529 bool on = true;
1530 bool header = true;
1531
1532 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
1533 if (s->type != Service_Filesystem)
1534 continue;
1535 if (header) {
1536 StringBuffer_append(res->outputbuffer,
1537 "<table id='header-row'>"
1538 "<tr>"
1539 "<th class='left first'>Filesystem</th>"
1540 "<th class='left'>Status</th>"
1541 "<th class='right'>Space usage</th>"
1542 "<th class='right'>Inodes usage</th>"
1543 "<th class='right column'>Read</th>"
1544 "<th class='right column'>Write</th>"
1545 "</tr>");
1546 header = false;
1547 }
1548 StringBuffer_append(res->outputbuffer,
1549 "<tr %s>"
1550 "<td class='left'><a href='%s'>%s</a></td>"
1551 "<td class='left'>%s</td>",
1552 on ? "class='stripe'" : "",
1553 s->name_urlescaped, StringBuffer_toString(s->name_htmlescaped),
1554 get_service_status(HTML, s, buf, sizeof(buf)));
1555 if (! Util_hasServiceStatus(s)) {
1556 StringBuffer_append(res->outputbuffer,
1557 "<td class='right'>- [-]</td>"
1558 "<td class='right'>- [-]</td>"
1559 "<td class='right column'>- [-]</td>"
1560 "<td class='right column'>- [-]</td>");
1561 } else {
1562 StringBuffer_append(res->outputbuffer,
1563 "<td class='right column%s'>%.1f%% [%s]</td>",
1564 (s->error & Event_Resource) ? " red-text" : "",
1565 s->inf.filesystem->space_percent,
1566 s->inf.filesystem->f_bsize > 0 ? Convert_bytes2str(s->inf.filesystem->f_blocksused * s->inf.filesystem->f_bsize, buf) : "0 MB");
1567 if (s->inf.filesystem->f_files > 0) {
1568 StringBuffer_append(res->outputbuffer,
1569 "<td class='right column%s'>%.1f%% [%lld objects]</td>",
1570 (s->error & Event_Resource) ? " red-text" : "",
1571 s->inf.filesystem->inode_percent,
1572 s->inf.filesystem->f_filesused);
1573 } else {
1574 StringBuffer_append(res->outputbuffer,
1575 "<td class='right column'>not supported by filesystem</td>");
1576 }
1577 StringBuffer_append(res->outputbuffer,
1578 "<td class='right column%s'>%s/s</td>"
1579 "<td class='right column%s'>%s/s</td>",
1580 (s->error & Event_Resource) ? " red-text" : "",
1581 Convert_bytes2str(Statistics_deltaNormalize(&(s->inf.filesystem->read.bytes)), (char[10]){}),
1582 (s->error & Event_Resource) ? " red-text" : "",
1583 Convert_bytes2str(Statistics_deltaNormalize(&(s->inf.filesystem->write.bytes)), (char[10]){}));
1584 }
1585 StringBuffer_append(res->outputbuffer, "</tr>");
1586 on = ! on;
1587 }
1588 if (! header)
1589 StringBuffer_append(res->outputbuffer, "</table>");
1590 }
1591
1592
do_home_file(HttpResponse res)1593 static void do_home_file(HttpResponse res) {
1594 char buf[STRLEN];
1595 bool on = true;
1596 bool header = true;
1597
1598 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
1599 if (s->type != Service_File)
1600 continue;
1601 if (header) {
1602 StringBuffer_append(res->outputbuffer,
1603 "<table id='header-row'>"
1604 "<tr>"
1605 "<th class='left first'>File</th>"
1606 "<th class='left'>Status</th>"
1607 "<th class='right'>Size</th>"
1608 "<th class='right'>Permission</th>"
1609 "<th class='right'>UID</th>"
1610 "<th class='right'>GID</th>"
1611 "</tr>");
1612
1613 header = false;
1614 }
1615 StringBuffer_append(res->outputbuffer,
1616 "<tr %s>"
1617 "<td class='left'><a href='%s'>%s</a></td>"
1618 "<td class='left'>%s</td>",
1619 on ? "class='stripe'" : "",
1620 s->name_urlescaped, StringBuffer_toString(s->name_htmlescaped),
1621 get_service_status(HTML, s, buf, sizeof(buf)));
1622 if (! Util_hasServiceStatus(s) || s->inf.file->size < 0)
1623 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1624 else
1625 StringBuffer_append(res->outputbuffer, "<td class='right'>%s</td>", Convert_bytes2str(s->inf.file->size, (char[10]){}));
1626 if (! Util_hasServiceStatus(s) || s->inf.file->mode < 0)
1627 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1628 else
1629 StringBuffer_append(res->outputbuffer, "<td class='right'>%04o</td>", s->inf.file->mode & 07777);
1630 if (! Util_hasServiceStatus(s) || s->inf.file->uid < 0)
1631 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1632 else
1633 StringBuffer_append(res->outputbuffer, "<td class='right'>%d</td>", s->inf.file->uid);
1634 if (! Util_hasServiceStatus(s) || s->inf.file->gid < 0)
1635 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1636 else
1637 StringBuffer_append(res->outputbuffer, "<td class='right'>%d</td>", s->inf.file->gid);
1638 StringBuffer_append(res->outputbuffer, "</tr>");
1639 on = ! on;
1640 }
1641 if (! header)
1642 StringBuffer_append(res->outputbuffer, "</table>");
1643 }
1644
1645
do_home_fifo(HttpResponse res)1646 static void do_home_fifo(HttpResponse res) {
1647 char buf[STRLEN];
1648 bool on = true;
1649 bool header = true;
1650
1651 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
1652 if (s->type != Service_Fifo)
1653 continue;
1654 if (header) {
1655 StringBuffer_append(res->outputbuffer,
1656 "<table id='header-row'>"
1657 "<tr>"
1658 "<th class='left first'>Fifo</th>"
1659 "<th class='left'>Status</th>"
1660 "<th class='right'>Permission</th>"
1661 "<th class='right'>UID</th>"
1662 "<th class='right'>GID</th>"
1663 "</tr>");
1664 header = false;
1665 }
1666 StringBuffer_append(res->outputbuffer,
1667 "<tr %s>"
1668 "<td class='left'><a href='%s'>%s</a></td>"
1669 "<td class='left'>%s</td>",
1670 on ? "class='stripe'" : "",
1671 s->name_urlescaped, StringBuffer_toString(s->name_htmlescaped),
1672 get_service_status(HTML, s, buf, sizeof(buf)));
1673 if (! Util_hasServiceStatus(s) || s->inf.fifo->mode < 0)
1674 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1675 else
1676 StringBuffer_append(res->outputbuffer, "<td class='right'>%04o</td>", s->inf.fifo->mode & 07777);
1677 if (! Util_hasServiceStatus(s) || s->inf.fifo->uid < 0)
1678 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1679 else
1680 StringBuffer_append(res->outputbuffer, "<td class='right'>%d</td>", s->inf.fifo->uid);
1681 if (! Util_hasServiceStatus(s) || s->inf.fifo->gid < 0)
1682 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1683 else
1684 StringBuffer_append(res->outputbuffer, "<td class='right'>%d</td>", s->inf.fifo->gid);
1685 StringBuffer_append(res->outputbuffer, "</tr>");
1686 on = ! on;
1687 }
1688 if (! header)
1689 StringBuffer_append(res->outputbuffer, "</table>");
1690 }
1691
1692
do_home_directory(HttpResponse res)1693 static void do_home_directory(HttpResponse res) {
1694 char buf[STRLEN];
1695 bool on = true;
1696 bool header = true;
1697
1698 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
1699 if (s->type != Service_Directory)
1700 continue;
1701 if (header) {
1702 StringBuffer_append(res->outputbuffer,
1703 "<table id='header-row'>"
1704 "<tr>"
1705 "<th class='left first'>Directory</th>"
1706 "<th class='left'>Status</th>"
1707 "<th class='right'>Permission</th>"
1708 "<th class='right'>UID</th>"
1709 "<th class='right'>GID</th>"
1710 "</tr>");
1711 header = false;
1712 }
1713 StringBuffer_append(res->outputbuffer,
1714 "<tr %s>"
1715 "<td class='left'><a href='%s'>%s</a></td>"
1716 "<td class='left'>%s</td>",
1717 on ? "class='stripe'" : "",
1718 s->name_urlescaped, StringBuffer_toString(s->name_htmlescaped),
1719 get_service_status(HTML, s, buf, sizeof(buf)));
1720 if (! Util_hasServiceStatus(s) || s->inf.directory->mode < 0)
1721 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1722 else
1723 StringBuffer_append(res->outputbuffer, "<td class='right'>%04o</td>", s->inf.directory->mode & 07777);
1724 if (! Util_hasServiceStatus(s) || s->inf.directory->uid < 0)
1725 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1726 else
1727 StringBuffer_append(res->outputbuffer, "<td class='right'>%d</td>", s->inf.directory->uid);
1728 if (! Util_hasServiceStatus(s) || s->inf.directory->gid < 0)
1729 StringBuffer_append(res->outputbuffer, "<td class='right'>-</td>");
1730 else
1731 StringBuffer_append(res->outputbuffer, "<td class='right'>%d</td>", s->inf.directory->gid);
1732 StringBuffer_append(res->outputbuffer, "</tr>");
1733 on = ! on;
1734 }
1735 if (! header)
1736 StringBuffer_append(res->outputbuffer, "</table>");
1737 }
1738
1739
do_home_host(HttpResponse res)1740 static void do_home_host(HttpResponse res) {
1741 char buf[STRLEN];
1742 bool on = true;
1743 bool header = true;
1744
1745 for (Service_T s = servicelist_conf; s; s = s->next_conf) {
1746 if (s->type != Service_Host)
1747 continue;
1748 if (header) {
1749 StringBuffer_append(res->outputbuffer,
1750 "<table id='header-row'>"
1751 "<tr>"
1752 "<th class='left first'>Host</th>"
1753 "<th class='left'>Status</th>"
1754 "<th class='right'>Protocol(s)</th>"
1755 "</tr>");
1756 header = false;
1757 }
1758 StringBuffer_append(res->outputbuffer,
1759 "<tr %s>"
1760 "<td class='left'><a href='%s'>%s</a></td>"
1761 "<td class='left'>%s</td>",
1762 on ? "class='stripe'" : "",
1763 s->name_urlescaped, StringBuffer_toString(s->name_htmlescaped),
1764 get_service_status(HTML, s, buf, sizeof(buf)));
1765 if (! Util_hasServiceStatus(s)) {
1766 StringBuffer_append(res->outputbuffer,
1767 "<td class='right'>-</td>");
1768 } else {
1769 StringBuffer_append(res->outputbuffer,
1770 "<td class='right'>");
1771 for (Icmp_T icmp = s->icmplist; icmp; icmp = icmp->next) {
1772 if (icmp != s->icmplist)
1773 StringBuffer_append(res->outputbuffer, " <b>|</b> ");
1774 switch (icmp->is_available) {
1775 case Connection_Init:
1776 StringBuffer_append(res->outputbuffer, "<span class='gray-text'>[Ping]</span>");
1777 break;
1778 case Connection_Failed:
1779 StringBuffer_append(res->outputbuffer, "<span class='red-text'>[Ping]</span>");
1780 break;
1781 default:
1782 StringBuffer_append(res->outputbuffer, "<span>[Ping]</span>");
1783 break;
1784 }
1785 }
1786 if (s->icmplist && s->portlist)
1787 StringBuffer_append(res->outputbuffer, " <b>|</b> ");
1788 for (Port_T port = s->portlist; port; port = port->next) {
1789 if (port != s->portlist)
1790 StringBuffer_append(res->outputbuffer, " <b>|</b> ");
1791 switch (port->is_available) {
1792 case Connection_Init:
1793 StringBuffer_append(res->outputbuffer, "<span class='gray-text'>[%s] at port %d</span>", port->protocol->name, port->target.net.port);
1794 break;
1795 case Connection_Failed:
1796 StringBuffer_append(res->outputbuffer, "<span class='red-text'>[%s] at port %d</span>", port->protocol->name, port->target.net.port);
1797 break;
1798 default:
1799 if (port->target.net.ssl.options.flags && port->target.net.ssl.certificate.validDays < port->target.net.ssl.certificate.minimumDays)
1800 StringBuffer_append(res->outputbuffer, "<span class='red-text'>[%s] at port %d</span>", port->protocol->name, port->target.net.port);
1801 else
1802 StringBuffer_append(res->outputbuffer, "<span>[%s] at port %d</span>", port->protocol->name, port->target.net.port);
1803 break;
1804 }
1805 }
1806 StringBuffer_append(res->outputbuffer, "</td>");
1807 }
1808 StringBuffer_append(res->outputbuffer, "</tr>");
1809 on = ! on;
1810 }
1811 if (! header)
1812 StringBuffer_append(res->outputbuffer, "</table>");
1813 }
1814
1815
1816 /* ------------------------------------------------------------------------- */
1817
1818
print_alerts(HttpResponse res,Mail_T s)1819 static void print_alerts(HttpResponse res, Mail_T s) {
1820 for (Mail_T r = s; r; r = r->next) {
1821 _displayTableRow(res, true, NULL, "Alert mail to", "%s", r->to ? r->to : "");
1822 StringBuffer_append(res->outputbuffer, "<tr><td>Alert on</td><td>");
1823 if (r->events == Event_Null) {
1824 StringBuffer_append(res->outputbuffer, "No events");
1825 } else if (r->events == Event_All) {
1826 StringBuffer_append(res->outputbuffer, "All events");
1827 } else {
1828 if (IS_EVENT_SET(r->events, Event_Action))
1829 StringBuffer_append(res->outputbuffer, "Action ");
1830 if (IS_EVENT_SET(r->events, Event_ByteIn))
1831 StringBuffer_append(res->outputbuffer, "ByteIn ");
1832 if (IS_EVENT_SET(r->events, Event_ByteOut))
1833 StringBuffer_append(res->outputbuffer, "ByteOut ");
1834 if (IS_EVENT_SET(r->events, Event_Checksum))
1835 StringBuffer_append(res->outputbuffer, "Checksum ");
1836 if (IS_EVENT_SET(r->events, Event_Connection))
1837 StringBuffer_append(res->outputbuffer, "Connection ");
1838 if (IS_EVENT_SET(r->events, Event_Content))
1839 StringBuffer_append(res->outputbuffer, "Content ");
1840 if (IS_EVENT_SET(r->events, Event_Data))
1841 StringBuffer_append(res->outputbuffer, "Data ");
1842 if (IS_EVENT_SET(r->events, Event_Exec))
1843 StringBuffer_append(res->outputbuffer, "Exec ");
1844 if (IS_EVENT_SET(r->events, Event_Exist))
1845 StringBuffer_append(res->outputbuffer, "Exist ");
1846 if (IS_EVENT_SET(r->events, Event_FsFlag))
1847 StringBuffer_append(res->outputbuffer, "Fsflags ");
1848 if (IS_EVENT_SET(r->events, Event_Gid))
1849 StringBuffer_append(res->outputbuffer, "Gid ");
1850 if (IS_EVENT_SET(r->events, Event_Instance))
1851 StringBuffer_append(res->outputbuffer, "Instance ");
1852 if (IS_EVENT_SET(r->events, Event_Invalid))
1853 StringBuffer_append(res->outputbuffer, "Invalid ");
1854 if (IS_EVENT_SET(r->events, Event_Link))
1855 StringBuffer_append(res->outputbuffer, "Link ");
1856 if (IS_EVENT_SET(r->events, Event_NonExist))
1857 StringBuffer_append(res->outputbuffer, "Nonexist ");
1858 if (IS_EVENT_SET(r->events, Event_Permission))
1859 StringBuffer_append(res->outputbuffer, "Permission ");
1860 if (IS_EVENT_SET(r->events, Event_PacketIn))
1861 StringBuffer_append(res->outputbuffer, "PacketIn ");
1862 if (IS_EVENT_SET(r->events, Event_PacketOut))
1863 StringBuffer_append(res->outputbuffer, "PacketOut ");
1864 if (IS_EVENT_SET(r->events, Event_Pid))
1865 StringBuffer_append(res->outputbuffer, "PID ");
1866 if (IS_EVENT_SET(r->events, Event_Icmp))
1867 StringBuffer_append(res->outputbuffer, "Ping ");
1868 if (IS_EVENT_SET(r->events, Event_PPid))
1869 StringBuffer_append(res->outputbuffer, "PPID ");
1870 if (IS_EVENT_SET(r->events, Event_Resource))
1871 StringBuffer_append(res->outputbuffer, "Resource ");
1872 if (IS_EVENT_SET(r->events, Event_Saturation))
1873 StringBuffer_append(res->outputbuffer, "Saturation ");
1874 if (IS_EVENT_SET(r->events, Event_Size))
1875 StringBuffer_append(res->outputbuffer, "Size ");
1876 if (IS_EVENT_SET(r->events, Event_Speed))
1877 StringBuffer_append(res->outputbuffer, "Speed ");
1878 if (IS_EVENT_SET(r->events, Event_Status))
1879 StringBuffer_append(res->outputbuffer, "Status ");
1880 if (IS_EVENT_SET(r->events, Event_Timeout))
1881 StringBuffer_append(res->outputbuffer, "Timeout ");
1882 if (IS_EVENT_SET(r->events, Event_Timestamp))
1883 StringBuffer_append(res->outputbuffer, "Timestamp ");
1884 if (IS_EVENT_SET(r->events, Event_Uid))
1885 StringBuffer_append(res->outputbuffer, "Uid ");
1886 if (IS_EVENT_SET(r->events, Event_Uptime))
1887 StringBuffer_append(res->outputbuffer, "Uptime ");
1888 }
1889 StringBuffer_append(res->outputbuffer, "</td></tr>");
1890 if (r->reminder)
1891 _displayTableRow(res, false, NULL, "Alert reminder", "%u cycles", r->reminder);
1892 }
1893 }
1894
1895
print_buttons(HttpRequest req,HttpResponse res,Service_T s)1896 static void print_buttons(HttpRequest req, HttpResponse res, Service_T s) {
1897 if (is_readonly(req)) {
1898 // A read-only REMOTE_USER does not get access to these buttons
1899 return;
1900 }
1901 StringBuffer_append(res->outputbuffer, "<table id='buttons'><tr>");
1902 /* Start program */
1903 if (s->start)
1904 StringBuffer_append(res->outputbuffer,
1905 "<td>"
1906 "<form method=POST action=%s>"
1907 "<input type=hidden name='securitytoken' value='%s'>"
1908 "<input type=hidden value='start' name=action>"
1909 "<input type=submit value='Start service'>"
1910 "</form>"
1911 "</td>", s->name_urlescaped, res->token);
1912 /* Stop program */
1913 if (s->stop)
1914 StringBuffer_append(res->outputbuffer,
1915 "<td>"
1916 "<form method=POST action=%s>"
1917 "<input type=hidden name='securitytoken' value='%s'>"
1918 "<input type=hidden value='stop' name=action>"
1919 "<input type=submit value='Stop service'>"
1920 "</form>"
1921 "</td>", s->name_urlescaped, res->token);
1922 /* Restart program */
1923 if ((s->start && s->stop) || s->restart)
1924 StringBuffer_append(res->outputbuffer,
1925 "<td>"
1926 "<form method=POST action=%s>"
1927 "<input type=hidden name='securitytoken' value='%s'>"
1928 "<input type=hidden value='restart' name=action>"
1929 "<input type=submit value='Restart service'>"
1930 "</form>"
1931 "</td>", s->name_urlescaped, res->token);
1932 /* (un)monitor */
1933 StringBuffer_append(res->outputbuffer,
1934 "<td>"
1935 "<form method=POST action=%s>"
1936 "<input type=hidden name='securitytoken' value='%s'>"
1937 "<input type=hidden value='%s' name=action>"
1938 "<input type=submit value='%s'>"
1939 "</form>"
1940 "</td>",
1941 s->name_urlescaped,
1942 res->token,
1943 s->monitor ? "unmonitor" : "monitor",
1944 s->monitor ? "Disable monitoring" : "Enable monitoring");
1945 StringBuffer_append(res->outputbuffer, "</tr></table>");
1946 }
1947
1948
print_service_rules_timeout(HttpResponse res,Service_T s)1949 static void print_service_rules_timeout(HttpResponse res, Service_T s) {
1950 for (ActionRate_T ar = s->actionratelist; ar; ar = ar->next) {
1951 StringBuffer_T sb = StringBuffer_create(256);
1952 _displayTableRow(res, true, "rule", "Timeout", "If restarted %d times within %d cycle(s) then %s", ar->count, ar->cycle, StringBuffer_toString(Util_printAction(ar->action->failed, sb)));
1953 StringBuffer_free(&sb);
1954 }
1955 }
1956
1957
print_service_rules_nonexistence(HttpResponse res,Service_T s)1958 static void print_service_rules_nonexistence(HttpResponse res, Service_T s) {
1959 for (NonExist_T l = s->nonexistlist; l; l = l->next) {
1960 StringBuffer_T sb = StringBuffer_create(256);
1961 _displayTableRow(res, true, "rule", "Existence", "%s", StringBuffer_toString(Util_printRule(false, sb, l->action, "If doesn't exist")));
1962 StringBuffer_free(&sb);
1963 }
1964 }
1965
1966
print_service_rules_existence(HttpResponse res,Service_T s)1967 static void print_service_rules_existence(HttpResponse res, Service_T s) {
1968 for (Exist_T l = s->existlist; l; l = l->next) {
1969 StringBuffer_T sb = StringBuffer_create(256);
1970 _displayTableRow(res, true, "rule", "Non-Existence", "%s", StringBuffer_toString(Util_printRule(false, sb, l->action, "If exist")));
1971 StringBuffer_free(&sb);
1972 }
1973 }
1974
1975
print_service_rules_port(HttpResponse res,Service_T s)1976 static void print_service_rules_port(HttpResponse res, Service_T s) {
1977 for (Port_T p = s->portlist; p; p = p->next) {
1978 StringBuffer_T sb = StringBuffer_create(256);
1979 StringBuffer_T buf = StringBuffer_create(64);
1980 StringBuffer_append(buf, "If %s [%s]:%d%s",
1981 p->check_invers ? "succeeded" : "failed", p->hostname, p->target.net.port, Util_portRequestDescription(p));
1982 if (p->outgoing.ip)
1983 StringBuffer_append(buf, " via address %s", p->outgoing.ip);
1984 StringBuffer_append(buf, " type %s/%s protocol %s with timeout %s",
1985 Util_portTypeDescription(p), Util_portIpDescription(p), p->protocol->name, Convert_time2str(p->timeout, (char[11]){}));
1986 if (p->retry > 1)
1987 StringBuffer_append(buf, " and retry %d times", p->retry);
1988 if (p->responsetime.limit > -1.)
1989 StringBuffer_append(buf, " and responsetime %s %s", operatornames[p->responsetime.operator], Convert_time2str(p->responsetime.limit, (char[11]){}));
1990 #ifdef HAVE_OPENSSL
1991 if (p->target.net.ssl.options.flags) {
1992 StringBuffer_append(buf, " using TLS");
1993 const char *options = Ssl_printOptions(&p->target.net.ssl.options, (char[STRLEN]){}, STRLEN);
1994 if (options && *options)
1995 StringBuffer_append(buf, " with options {%s}", options);
1996 if (p->target.net.ssl.certificate.minimumDays > 0)
1997 StringBuffer_append(buf, " and certificate valid for at least %d days", p->target.net.ssl.certificate.minimumDays);
1998 if (p->target.net.ssl.options.checksum)
1999 StringBuffer_append(buf, " and certificate checksum %s equal to '%s'", checksumnames[p->target.net.ssl.options.checksumType], p->target.net.ssl.options.checksum);
2000 }
2001 #endif
2002 _displayTableRow(res, true, "rule", "Port", "%s", StringBuffer_toString(Util_printRule(p->check_invers, sb, p->action, "%s", StringBuffer_toString(buf))));
2003 StringBuffer_free(&buf);
2004 StringBuffer_free(&sb);
2005 }
2006 }
2007
2008
print_service_rules_socket(HttpResponse res,Service_T s)2009 static void print_service_rules_socket(HttpResponse res, Service_T s) {
2010 for (Port_T p = s->socketlist; p; p = p->next) {
2011 StringBuffer_T sb = StringBuffer_create(256);
2012 StringBuffer_T buf = StringBuffer_create(64);
2013 StringBuffer_append(buf, "If %s %s type %s protocol %s with timeout %s", p->check_invers ? "succeeded" : "failed", p->target.unix.pathname, Util_portTypeDescription(p), p->protocol->name, Convert_time2str(p->timeout, (char[11]){}));
2014 if (p->retry > 1)
2015 StringBuffer_append(buf, " and retry %d times", p->retry);
2016 if (p->responsetime.limit > -1.)
2017 StringBuffer_append(buf, " and responsetime %s %s", operatornames[p->responsetime.operator], Convert_time2str(p->responsetime.limit, (char[11]){}));
2018 _displayTableRow(res, true, "rule", "Unix Socket", "%s", StringBuffer_toString(Util_printRule(p->check_invers, sb, p->action, "%s", StringBuffer_toString(buf))));
2019 StringBuffer_free(&buf);
2020 StringBuffer_free(&sb);
2021 }
2022 }
2023
2024
print_service_rules_icmp(HttpResponse res,Service_T s)2025 static void print_service_rules_icmp(HttpResponse res, Service_T s) {
2026 for (Icmp_T i = s->icmplist; i; i = i->next) {
2027 const char *key;
2028 StringBuffer_T sb = StringBuffer_create(256);
2029 StringBuffer_T buf = StringBuffer_create(64);
2030 switch (i->family) {
2031 case Socket_Ip4:
2032 key = "Ping4";
2033 break;
2034 case Socket_Ip6:
2035 key = "Ping6";
2036 break;
2037 default:
2038 key = "Ping";
2039 break;
2040 }
2041 StringBuffer_append(buf, "If %s count %d size %d with timeout %s", i->check_invers ? "succeeded" : "failed", i->count, i->size, Convert_time2str(i->timeout, (char[11]){}));
2042 if (i->outgoing.ip)
2043 StringBuffer_append(buf, " via address %s", i->outgoing.ip);
2044 if (i->responsetime.limit > -1.)
2045 StringBuffer_append(buf, " and responsetime %s %s", operatornames[i->responsetime.operator], Convert_time2str(i->responsetime.limit, (char[11]){}));
2046 _displayTableRow(res, true, "rule", key, "%s", StringBuffer_toString(Util_printRule(i->check_invers, sb, i->action, "%s", StringBuffer_toString(buf))));
2047 StringBuffer_free(&buf);
2048 StringBuffer_free(&sb);
2049 }
2050 }
2051
2052
print_service_rules_perm(HttpResponse res,Service_T s)2053 static void print_service_rules_perm(HttpResponse res, Service_T s) {
2054 if (s->perm) {
2055 StringBuffer_T sb = StringBuffer_create(256);
2056 if (s->perm->test_changes)
2057 Util_printRule(false, sb, s->perm->action, "If changed");
2058 else
2059 Util_printRule(false, sb, s->perm->action, "If failed %o", s->perm->perm);
2060 _displayTableRow(res, true, "rule", "Permissions", "%s", StringBuffer_toString(sb));
2061 StringBuffer_free(&sb);
2062 }
2063 }
2064
2065
print_service_rules_uid(HttpResponse res,Service_T s)2066 static void print_service_rules_uid(HttpResponse res, Service_T s) {
2067 if (s->uid) {
2068 StringBuffer_T sb = StringBuffer_create(256);
2069 _displayTableRow(res, true, "rule", "UID", "%s", StringBuffer_toString(Util_printRule(false, sb, s->uid->action, "If failed %d", s->uid->uid)));
2070 StringBuffer_free(&sb);
2071 }
2072 }
2073
2074
print_service_rules_euid(HttpResponse res,Service_T s)2075 static void print_service_rules_euid(HttpResponse res, Service_T s) {
2076 if (s->euid) {
2077 StringBuffer_T sb = StringBuffer_create(256);
2078 _displayTableRow(res, true, "rule", "EUID", "%s", StringBuffer_toString(Util_printRule(false, sb, s->euid->action, "If failed %d", s->euid->uid)));
2079 StringBuffer_free(&sb);
2080 }
2081 }
2082
2083
print_service_rules_filedescriptors(HttpResponse res,Service_T s)2084 static void print_service_rules_filedescriptors(HttpResponse res, Service_T s) {
2085 for (Filedescriptors_T o = s->filedescriptorslist; o; o = o->next) {
2086 StringBuffer_T sb = StringBuffer_create(256);
2087 if (o->total) {
2088 _displayTableRow(res, true, "rule", "Total filedescriptors", "%s", StringBuffer_toString(Util_printRule(false, sb, o->action, "If %s %lld", operatornames[o->operator], o->limit_absolute)));
2089 } else {
2090 if (o->limit_absolute > -1LL)
2091 _displayTableRow(res, true, "rule", "Filedescriptors", "%s", StringBuffer_toString(Util_printRule(false, sb, o->action, "If %s %lld", operatornames[o->operator], o->limit_absolute)));
2092 else
2093 _displayTableRow(res, true, "rule", "Filedescriptors", "%s", StringBuffer_toString(Util_printRule(false, sb, o->action, "If %s %.1f%%", operatornames[o->operator], o->limit_percent)));
2094 }
2095 StringBuffer_free(&sb);
2096 }
2097 }
2098
2099
print_service_rules_gid(HttpResponse res,Service_T s)2100 static void print_service_rules_gid(HttpResponse res, Service_T s) {
2101 if (s->gid) {
2102 StringBuffer_T sb = StringBuffer_create(256);
2103 _displayTableRow(res, true, "rule", "GID", "%s", StringBuffer_toString(Util_printRule(false, sb, s->gid->action, "If failed %d", s->gid->gid)));
2104 StringBuffer_free(&sb);
2105 }
2106 }
2107
2108
print_service_rules_secattr(HttpResponse res,Service_T s)2109 static void print_service_rules_secattr(HttpResponse res, Service_T s) {
2110 for (SecurityAttribute_T a = s->secattrlist; a; a = a->next) {
2111 StringBuffer_T sb = StringBuffer_create(256);
2112 _displayTableRow(res, true, "rule", "Security attribute", "%s", StringBuffer_toString(Util_printRule(false, sb, a->action, "If failed %s", a->attribute)));
2113 StringBuffer_free(&sb);
2114 }
2115 }
2116
2117
print_service_rules_timestamp(HttpResponse res,Service_T s)2118 static void print_service_rules_timestamp(HttpResponse res, Service_T s) {
2119 for (Timestamp_T t = s->timestamplist; t; t = t->next) {
2120 char key[STRLEN];
2121 snprintf(key, sizeof(key), "%c%s", toupper(timestampnames[t->type][0]), timestampnames[t->type] + 1);
2122 StringBuffer_T sb = StringBuffer_create(256);
2123 if (t->test_changes)
2124 Util_printRule(false, sb, t->action, "If changed");
2125 else
2126 Util_printRule(false, sb, t->action, "If %s %s", operatornames[t->operator], Convert_time2str(t->time * 1000., (char[11]){}));
2127 _displayTableRow(res, true, "rule", key, "%s", StringBuffer_toString(sb));
2128 StringBuffer_free(&sb);
2129 }
2130 }
2131
2132
print_service_rules_fsflags(HttpResponse res,Service_T s)2133 static void print_service_rules_fsflags(HttpResponse res, Service_T s) {
2134 for (FsFlag_T l = s->fsflaglist; l; l = l->next) {
2135 StringBuffer_T sb = StringBuffer_create(256);
2136 _displayTableRow(res, true, "rule", "Filesystem flags", "%s", StringBuffer_toString(Util_printRule(false, sb, l->action, "If changed")));
2137 StringBuffer_free(&sb);
2138 }
2139 }
2140
2141
print_service_rules_filesystem(HttpResponse res,Service_T s)2142 static void print_service_rules_filesystem(HttpResponse res, Service_T s) {
2143 for (FileSystem_T dl = s->filesystemlist; dl; dl = dl->next) {
2144 StringBuffer_T sb = StringBuffer_create(256);
2145 switch (dl->resource) {
2146 case Resource_Inode:
2147 if (dl->limit_absolute > -1)
2148 Util_printRule(false, sb, dl->action, "If %s %lld", operatornames[dl->operator], dl->limit_absolute);
2149 else
2150 Util_printRule(false, sb, dl->action, "If %s %.1f%%", operatornames[dl->operator], dl->limit_percent);
2151 _displayTableRow(res, true, "rule", "Inodes usage limit", "%s", StringBuffer_toString(sb));
2152 break;
2153 case Resource_InodeFree:
2154 if (dl->limit_absolute > -1)
2155 Util_printRule(false, sb, dl->action, "If %s %lld", operatornames[dl->operator], dl->limit_absolute);
2156 else
2157 Util_printRule(false, sb, dl->action, "If %s %.1f%%", operatornames[dl->operator], dl->limit_percent);
2158 _displayTableRow(res, true, "rule", "Inodes free limit", "%s", StringBuffer_toString(sb));
2159 break;
2160 case Resource_Space:
2161 if (dl->limit_absolute > -1)
2162 Util_printRule(false, sb, dl->action, "If %s %s", operatornames[dl->operator], Convert_bytes2str(dl->limit_absolute, (char[10]){}));
2163 else
2164 Util_printRule(false, sb, dl->action, "If %s %.1f%%", operatornames[dl->operator], dl->limit_percent);
2165 _displayTableRow(res, true, "rule", "Space usage limit", "%s", StringBuffer_toString(sb));
2166 break;
2167 case Resource_SpaceFree:
2168 if (dl->limit_absolute > -1)
2169 Util_printRule(false, sb, dl->action, "If %s %s", operatornames[dl->operator], Convert_bytes2str(dl->limit_absolute, (char[10]){}));
2170 else
2171 Util_printRule(false, sb, dl->action, "If %s %.1f%%", operatornames[dl->operator], dl->limit_percent);
2172 _displayTableRow(res, true, "rule", "Space free limit", "%s", StringBuffer_toString(sb));
2173 break;
2174 case Resource_ReadBytes:
2175 _displayTableRow(res, true, "rule", "Read limit", "%s", StringBuffer_toString(Util_printRule(false, sb, dl->action, "If read %s %s/s", operatornames[dl->operator], Convert_bytes2str(dl->limit_absolute, (char[10]){}))));
2176 break;
2177 case Resource_ReadOperations:
2178 _displayTableRow(res, true, "rule", "Read limit", "%s", StringBuffer_toString(Util_printRule(false, sb, dl->action, "If read %s %llu operations/s", operatornames[dl->operator], dl->limit_absolute)));
2179 break;
2180 case Resource_WriteBytes:
2181 _displayTableRow(res, true, "rule", "Write limit", "%s", StringBuffer_toString(Util_printRule(false, sb, dl->action, "If write %s %s/s", operatornames[dl->operator], Convert_bytes2str(dl->limit_absolute, (char[10]){}))));
2182 break;
2183 case Resource_WriteOperations:
2184 _displayTableRow(res, true, "rule", "Write limit", "%s", StringBuffer_toString(Util_printRule(false, sb, dl->action, "If write %s %llu operations/s", operatornames[dl->operator], dl->limit_absolute)));
2185 break;
2186 case Resource_ServiceTime:
2187 _displayTableRow(res, true, "rule", "Service time limit", "%s", StringBuffer_toString(Util_printRule(false, sb, dl->action, "If service time %s %s/operation", operatornames[dl->operator], Convert_time2str(dl->limit_absolute, (char[11]){}))));
2188 break;
2189 default:
2190 break;
2191 }
2192 StringBuffer_free(&sb);
2193 }
2194 }
2195
2196
print_service_rules_size(HttpResponse res,Service_T s)2197 static void print_service_rules_size(HttpResponse res, Service_T s) {
2198 for (Size_T sl = s->sizelist; sl; sl = sl->next) {
2199 StringBuffer_T sb = StringBuffer_create(256);
2200 if (sl->test_changes)
2201 Util_printRule(false, sb, sl->action, "If changed");
2202 else
2203 Util_printRule(false, sb, sl->action, "If %s %llu byte(s)", operatornames[sl->operator], sl->size);
2204 _displayTableRow(res, true, "rule", "Size", "%s", StringBuffer_toString(sb));
2205 StringBuffer_free(&sb);
2206 }
2207 }
2208
2209
print_service_rules_linkstatus(HttpResponse res,Service_T s)2210 static void print_service_rules_linkstatus(HttpResponse res, Service_T s) {
2211 for (LinkStatus_T l = s->linkstatuslist; l; l = l->next) {
2212 StringBuffer_T sb = StringBuffer_create(256);
2213 _displayTableRow(res, true, "rule", "Link status", "%s", StringBuffer_toString(Util_printRule(l->check_invers, sb, l->action, "If %s", l->check_invers ? "up" : "down")));
2214 StringBuffer_free(&sb);
2215 }
2216 }
2217
2218
print_service_rules_linkspeed(HttpResponse res,Service_T s)2219 static void print_service_rules_linkspeed(HttpResponse res, Service_T s) {
2220 for (LinkSpeed_T l = s->linkspeedlist; l; l = l->next) {
2221 StringBuffer_T sb = StringBuffer_create(256);
2222 _displayTableRow(res, true, "rule", "Link capacity", "%s", StringBuffer_toString(Util_printRule(false, sb, l->action, "If changed")));
2223 StringBuffer_free(&sb);
2224 }
2225 }
2226
2227
print_service_rules_linksaturation(HttpResponse res,Service_T s)2228 static void print_service_rules_linksaturation(HttpResponse res, Service_T s) {
2229 for (LinkSaturation_T l = s->linksaturationlist; l; l = l->next) {
2230 StringBuffer_T sb = StringBuffer_create(256);
2231 _displayTableRow(res, true, "rule", "Link saturation", "%s", StringBuffer_toString(Util_printRule(false, sb, l->action, "If %s %.1f%%", operatornames[l->operator], l->limit)));
2232 StringBuffer_free(&sb);
2233 }
2234 }
2235
2236
print_service_rules_uploadbytes(HttpResponse res,Service_T s)2237 static void print_service_rules_uploadbytes(HttpResponse res, Service_T s) {
2238 for (Bandwidth_T bl = s->uploadbyteslist; bl; bl = bl->next) {
2239 StringBuffer_T sb = StringBuffer_create(256);
2240 if (bl->range == Time_Second)
2241 _displayTableRow(res, true, "rule", "Upload bytes", "%s", StringBuffer_toString(Util_printRule(false, sb, bl->action, "If %s %s/s", operatornames[bl->operator], Convert_bytes2str(bl->limit, (char[10]){}))));
2242 else
2243 _displayTableRow(res, true, "rule", "Total upload bytes", "%s", StringBuffer_toString(Util_printRule(false, sb, bl->action, "If %s %s in last %d %s(s)", operatornames[bl->operator], Convert_bytes2str(bl->limit, (char[10]){}), bl->rangecount, Util_timestr(bl->range))));
2244 StringBuffer_free(&sb);
2245 }
2246 }
2247
2248
print_service_rules_uploadpackets(HttpResponse res,Service_T s)2249 static void print_service_rules_uploadpackets(HttpResponse res, Service_T s) {
2250 for (Bandwidth_T bl = s->uploadpacketslist; bl; bl = bl->next) {
2251 StringBuffer_T sb = StringBuffer_create(256);
2252 if (bl->range == Time_Second)
2253 _displayTableRow(res, true, "rule", "Upload packets", "%s", StringBuffer_toString(Util_printRule(false, sb, bl->action, "If %s %lld packets/s", operatornames[bl->operator], bl->limit)));
2254 else
2255 _displayTableRow(res, true, "rule", "Total upload packets", "%s", StringBuffer_toString(Util_printRule(false, sb, bl->action, "If %s %lld packets in last %d %s(s)", operatornames[bl->operator], bl->limit, bl->rangecount, Util_timestr(bl->range))));
2256 StringBuffer_free(&sb);
2257 }
2258 }
2259
2260
print_service_rules_downloadbytes(HttpResponse res,Service_T s)2261 static void print_service_rules_downloadbytes(HttpResponse res, Service_T s) {
2262 for (Bandwidth_T bl = s->downloadbyteslist; bl; bl = bl->next) {
2263 StringBuffer_T sb = StringBuffer_create(256);
2264 if (bl->range == Time_Second)
2265 _displayTableRow(res, true, "rule", "Download bytes", "%s", StringBuffer_toString(Util_printRule(false, sb, bl->action, "If %s %s/s", operatornames[bl->operator], Convert_bytes2str(bl->limit, (char[10]){}))));
2266 else
2267 _displayTableRow(res, true, "rule", "Total download bytes", "%s", StringBuffer_toString(Util_printRule(false, sb, bl->action, "If %s %s in last %d %s(s)", operatornames[bl->operator], Convert_bytes2str(bl->limit, (char[10]){}), bl->rangecount, Util_timestr(bl->range))));
2268 StringBuffer_free(&sb);
2269 }
2270 }
2271
2272
print_service_rules_downloadpackets(HttpResponse res,Service_T s)2273 static void print_service_rules_downloadpackets(HttpResponse res, Service_T s) {
2274 for (Bandwidth_T bl = s->downloadpacketslist; bl; bl = bl->next) {
2275 StringBuffer_T sb = StringBuffer_create(256);
2276 if (bl->range == Time_Second)
2277 _displayTableRow(res, true, "rule", "Download packets", "%s", StringBuffer_toString(Util_printRule(false, sb, bl->action, "If %s %lld packets/s", operatornames[bl->operator], bl->limit)));
2278 else
2279 _displayTableRow(res, true, "rule", "Total download packets", "%s", StringBuffer_toString(Util_printRule(false, sb, bl->action, "If %s %lld packets in last %d %s(s)", operatornames[bl->operator], bl->limit, bl->rangecount, Util_timestr(bl->range))));
2280 StringBuffer_free(&sb);
2281 }
2282 }
2283
2284
print_service_rules_uptime(HttpResponse res,Service_T s)2285 static void print_service_rules_uptime(HttpResponse res, Service_T s) {
2286 for (Uptime_T ul = s->uptimelist; ul; ul = ul->next) {
2287 StringBuffer_T sb = StringBuffer_create(256);
2288 _displayTableRow(res, true, "rule", "Uptime", "%s", StringBuffer_toString(Util_printRule(false, sb, ul->action, "If %s %s", operatornames[ul->operator], _getUptime(ul->uptime, (char[256]){}))));
2289 StringBuffer_free(&sb);
2290 }
2291 }
2292
print_service_rules_content(HttpResponse res,Service_T s)2293 static void print_service_rules_content(HttpResponse res, Service_T s) {
2294 if (s->type != Service_Process) {
2295 for (Match_T ml = s->matchignorelist; ml; ml = ml->next) {
2296 StringBuffer_T sb = StringBuffer_create(256);
2297 _displayTableRow(res, true, "rule", "Ignore content", "%s", StringBuffer_toString(Util_printRule(false, sb, ml->action, "If content %s \"%s\"", ml->not ? "!=" : "=", ml->match_string)));
2298 StringBuffer_free(&sb);
2299 }
2300 for (Match_T ml = s->matchlist; ml; ml = ml->next) {
2301 StringBuffer_T sb = StringBuffer_create(256);
2302 _displayTableRow(res, true, "rule", "Content match", "%s", StringBuffer_toString(Util_printRule(false, sb, ml->action, "If content %s \"%s\"", ml->not ? "!=" : "=", ml->match_string)));
2303 StringBuffer_free(&sb);
2304 }
2305 }
2306 }
2307
2308
print_service_rules_checksum(HttpResponse res,Service_T s)2309 static void print_service_rules_checksum(HttpResponse res, Service_T s) {
2310 if (s->checksum) {
2311 StringBuffer_T sb = StringBuffer_create(256);
2312 if (s->checksum->test_changes)
2313 Util_printRule(false, sb, s->checksum->action, "If changed %s", checksumnames[s->checksum->type]);
2314 else
2315 Util_printRule(false, sb, s->checksum->action, "If failed %s(%s)", s->checksum->hash, checksumnames[s->checksum->type]);
2316 _displayTableRow(res, true, "rule", "Checksum", "%s", StringBuffer_toString(sb));
2317 StringBuffer_free(&sb);
2318 }
2319 }
2320
2321
print_service_rules_pid(HttpResponse res,Service_T s)2322 static void print_service_rules_pid(HttpResponse res, Service_T s) {
2323 for (Pid_T l = s->pidlist; l; l = l->next) {
2324 StringBuffer_T sb = StringBuffer_create(256);
2325 _displayTableRow(res, true, "rule", "PID", "%s", StringBuffer_toString(Util_printRule(false, sb, l->action, "If changed")));
2326 StringBuffer_free(&sb);
2327 }
2328 }
2329
2330
print_service_rules_ppid(HttpResponse res,Service_T s)2331 static void print_service_rules_ppid(HttpResponse res, Service_T s) {
2332 for (Pid_T l = s->ppidlist; l; l = l->next) {
2333 StringBuffer_T sb = StringBuffer_create(256);
2334 _displayTableRow(res, true, "rule", "PPID", "%s", StringBuffer_toString(Util_printRule(false, sb, l->action, "If changed")));
2335 StringBuffer_free(&sb);
2336 }
2337 }
2338
2339
print_service_rules_program(HttpResponse res,Service_T s)2340 static void print_service_rules_program(HttpResponse res, Service_T s) {
2341 if (s->type == Service_Program) {
2342 _displayTableRow(res, false, "rule", "Program timeout", "Terminate the program if not finished within %s", Convert_time2str(s->program->timeout, (char[11]){}));
2343 for (Status_T status = s->statuslist; status; status = status->next) {
2344 StringBuffer_T sb = StringBuffer_create(256);
2345 if (status->operator == Operator_Changed)
2346 Util_printRule(false, sb, status->action, "If exit value changed");
2347 else
2348 Util_printRule(false, sb, status->action, "If exit value %s %d", operatorshortnames[status->operator], status->return_value);
2349 _displayTableRow(res, true, "rule", "Test Exit value", "%s", StringBuffer_toString(sb));
2350 StringBuffer_free(&sb);
2351 }
2352 }
2353 }
2354
2355
print_service_rules_resource(HttpResponse res,Service_T s)2356 static void print_service_rules_resource(HttpResponse res, Service_T s) {
2357 char buf[STRLEN];
2358 for (Resource_T q = s->resourcelist; q; q = q->next) {
2359 const char *key = NULL;
2360 StringBuffer_T sb = StringBuffer_create(256);
2361 switch (q->resource_id) {
2362 case Resource_CpuPercent:
2363 key = "CPU usage limit";
2364 break;
2365
2366 case Resource_CpuPercentTotal:
2367 key = "CPU usage limit (incl. children)";
2368 break;
2369
2370 case Resource_CpuUser:
2371 key = "CPU user limit";
2372 break;
2373
2374 case Resource_CpuSystem:
2375 key = "CPU system limit";
2376 break;
2377
2378 case Resource_CpuWait:
2379 key = "CPU I/O wait limit";
2380 break;
2381
2382 case Resource_CpuNice:
2383 key = "CPU nice limit";
2384 break;
2385
2386 case Resource_CpuHardIRQ:
2387 key = "CPU hardware IRQ limit";
2388 break;
2389
2390 case Resource_CpuSoftIRQ:
2391 key = "CPU software IRQ limit";
2392 break;
2393
2394 case Resource_CpuSteal:
2395 key = "CPU steal limit";
2396 break;
2397
2398 case Resource_CpuGuest:
2399 key = "CPU guest limit";
2400 break;
2401
2402 case Resource_CpuGuestNice:
2403 key = "CPU guest nice limit";
2404 break;
2405
2406 case Resource_MemoryPercent:
2407 key = "Memory usage limit";
2408 break;
2409
2410 case Resource_MemoryKbyte:
2411 key = "Memory amount limit";
2412 break;
2413
2414 case Resource_SwapPercent:
2415 key = "Swap usage limit";
2416 break;
2417
2418 case Resource_SwapKbyte:
2419 key = "Swap amount limit";
2420 break;
2421
2422 case Resource_LoadAverage1m:
2423 key = "Load average (1m)";
2424 break;
2425
2426 case Resource_LoadAverage5m:
2427 key = "Load average (5m)";
2428 break;
2429
2430 case Resource_LoadAverage15m:
2431 key = "Load average (15m)";
2432 break;
2433
2434 case Resource_LoadAveragePerCore1m:
2435 key = "Load average per core (1m)";
2436 break;
2437
2438 case Resource_LoadAveragePerCore5m:
2439 key = "Load average per core (5m)";
2440 break;
2441
2442 case Resource_LoadAveragePerCore15m:
2443 key = "Load average per core (15m)";
2444 break;
2445
2446 case Resource_Threads:
2447 key = "Threads";
2448 break;
2449
2450 case Resource_Children:
2451 key = "Children";
2452 break;
2453
2454 case Resource_MemoryKbyteTotal:
2455 key = "Memory amount limit (incl. children)";
2456 break;
2457
2458 case Resource_MemoryPercentTotal:
2459 key = "Memory usage limit (incl. children)";
2460 break;
2461
2462 case Resource_ReadBytes:
2463 key = "Disk read limit";
2464 break;
2465
2466 case Resource_ReadOperations:
2467 key = "Disk read limit";
2468 break;
2469
2470 case Resource_WriteBytes:
2471 key = "Disk write limit";
2472 break;
2473
2474 case Resource_WriteOperations:
2475 key = "Disk write limit";
2476 break;
2477
2478 default:
2479 break;
2480 }
2481 switch (q->resource_id) {
2482 case Resource_CpuPercent:
2483 case Resource_CpuPercentTotal:
2484 case Resource_MemoryPercentTotal:
2485 case Resource_CpuUser:
2486 case Resource_CpuSystem:
2487 case Resource_CpuWait:
2488 case Resource_CpuNice:
2489 case Resource_CpuHardIRQ:
2490 case Resource_CpuSoftIRQ:
2491 case Resource_CpuSteal:
2492 case Resource_CpuGuest:
2493 case Resource_CpuGuestNice:
2494 case Resource_MemoryPercent:
2495 case Resource_SwapPercent:
2496 Util_printRule(false, sb, q->action, "If %s %.1f%%", operatornames[q->operator], q->limit);
2497 break;
2498
2499 case Resource_MemoryKbyte:
2500 case Resource_SwapKbyte:
2501 case Resource_MemoryKbyteTotal:
2502 Util_printRule(false, sb, q->action, "If %s %s", operatornames[q->operator], Convert_bytes2str(q->limit, buf));
2503 break;
2504
2505 case Resource_LoadAverage1m:
2506 case Resource_LoadAverage5m:
2507 case Resource_LoadAverage15m:
2508 case Resource_LoadAveragePerCore1m:
2509 case Resource_LoadAveragePerCore5m:
2510 case Resource_LoadAveragePerCore15m:
2511 Util_printRule(false, sb, q->action, "If %s %.1f", operatornames[q->operator], q->limit);
2512 break;
2513
2514 case Resource_Threads:
2515 case Resource_Children:
2516 Util_printRule(false, sb, q->action, "If %s %.0f", operatornames[q->operator], q->limit);
2517 break;
2518
2519 case Resource_ReadBytes:
2520 case Resource_ReadBytesPhysical:
2521 case Resource_WriteBytes:
2522 case Resource_WriteBytesPhysical:
2523 Util_printRule(false, sb, q->action, "if %s %s", operatornames[q->operator], Convert_bytes2str(q->limit, (char[10]){}));
2524 break;
2525
2526 case Resource_ReadOperations:
2527 case Resource_WriteOperations:
2528 Util_printRule(false, sb, q->action, "if %s %.0f operations/s", operatornames[q->operator], q->limit);
2529 break;
2530
2531 default:
2532 break;
2533 }
2534 if (key)
2535 _displayTableRow(res, true, "rule", key, "%s", StringBuffer_toString(sb));
2536 StringBuffer_free(&sb);
2537 }
2538 }
2539
2540
is_readonly(HttpRequest req)2541 static bool is_readonly(HttpRequest req) {
2542 if (req->remote_user) {
2543 Auth_T user_creds = Util_getUserCredentials(req->remote_user);
2544 return (user_creds ? user_creds->is_readonly : true);
2545 }
2546 return false;
2547 }
2548
2549
2550 /* ----------------------------------------------------------- Status output */
2551
2552
2553 /* Print status in the given format. Text status is default. */
print_status(HttpRequest req,HttpResponse res,int version)2554 static void print_status(HttpRequest req, HttpResponse res, int version) {
2555 const char *stringFormat = get_parameter(req, "format");
2556 if (stringFormat && Str_startsWith(stringFormat, "xml")) {
2557 char buf[STRLEN];
2558 StringBuffer_T sb = StringBuffer_create(256);
2559 status_xml(sb, NULL, version, Socket_getLocalHost(req->S, buf, sizeof(buf)));
2560 StringBuffer_append(res->outputbuffer, "%s", StringBuffer_toString(sb));
2561 StringBuffer_free(&sb);
2562 set_content_type(res, "text/xml");
2563 } else {
2564 set_content_type(res, "text/plain");
2565
2566 StringBuffer_append(res->outputbuffer, "Monit %s uptime: %s\n\n", VERSION, _getUptime(ProcessTree_getProcessUptime(getpid()), (char[256]){}));
2567
2568 struct ServiceMap_T ap = {.found = 0, .data.status.res = res};
2569 const char *stringGroup = Util_urlDecode((char *)get_parameter(req, "group"));
2570 const char *stringService = Util_urlDecode((char *)get_parameter(req, "service"));
2571 if (stringGroup) {
2572 for (ServiceGroup_T sg = servicegrouplist; sg; sg = sg->next) {
2573 if (IS(stringGroup, sg->name)) {
2574 for (list_t m = sg->members->head; m; m = m->next) {
2575 status_service_txt(m->e, res);
2576 ap.found++;
2577 }
2578 break;
2579 }
2580 }
2581 } else {
2582 _serviceMapByName(stringService, _serviceMapStatus, &ap);
2583 }
2584 if (ap.found == 0) {
2585 if (stringGroup)
2586 send_error(req, res, SC_BAD_REQUEST, "Service group '%s' not found", stringGroup);
2587 else if (stringService)
2588 send_error(req, res, SC_BAD_REQUEST, "Service '%s' not found", stringService);
2589 else
2590 send_error(req, res, SC_BAD_REQUEST, "No service found");
2591 }
2592 }
2593 }
2594
2595
print_summary(HttpRequest req,HttpResponse res)2596 static void print_summary(HttpRequest req, HttpResponse res) {
2597 set_content_type(res, "text/plain");
2598
2599 StringBuffer_append(res->outputbuffer, "Monit %s uptime: %s\n", VERSION, _getUptime(ProcessTree_getProcessUptime(getpid()), (char[256]){}));
2600
2601 struct ServiceMap_T ap = {.found = 0};
2602 const char *stringGroup = Util_urlDecode((char *)get_parameter(req, "group"));
2603 const char *stringService = Util_urlDecode((char *)get_parameter(req, "service"));
2604
2605 ap.data.summary.box = TextBox_new(res->outputbuffer, 3, (TextBoxColumn_T []){
2606 {.name = "Service Name", .width = 31, .wrap = false, .align = TextBoxAlign_Left},
2607 {.name = "Status", .width = 26, .wrap = false, .align = TextBoxAlign_Left},
2608 {.name = "Type", .width = 13, .wrap = false, .align = TextBoxAlign_Left}
2609 }, true);
2610
2611 if (stringGroup) {
2612 for (ServiceGroup_T sg = servicegrouplist; sg; sg = sg->next) {
2613 if (IS(stringGroup, sg->name)) {
2614 for (list_t m = sg->members->head; m; m = m->next) {
2615 _printServiceSummary(ap.data.summary.box, m->e);
2616 ap.found++;
2617 }
2618 break;
2619 }
2620 }
2621 } else if (stringService) {
2622 _serviceMapByName(stringService, _serviceMapSummary, &ap);
2623 } else {
2624 _serviceMapByType(Service_System, _serviceMapSummary, &ap);
2625 _serviceMapByType(Service_Process, _serviceMapSummary, &ap);
2626 _serviceMapByType(Service_File, _serviceMapSummary, &ap);
2627 _serviceMapByType(Service_Fifo, _serviceMapSummary, &ap);
2628 _serviceMapByType(Service_Directory, _serviceMapSummary, &ap);
2629 _serviceMapByType(Service_Filesystem, _serviceMapSummary, &ap);
2630 _serviceMapByType(Service_Host, _serviceMapSummary, &ap);
2631 _serviceMapByType(Service_Net, _serviceMapSummary, &ap);
2632 _serviceMapByType(Service_Program, _serviceMapSummary, &ap);
2633 }
2634
2635 TextBox_free(&ap.data.summary.box);
2636
2637 if (ap.found == 0) {
2638 if (stringGroup)
2639 send_error(req, res, SC_BAD_REQUEST, "Service group '%s' not found", stringGroup);
2640 else if (stringService)
2641 send_error(req, res, SC_BAD_REQUEST, "Service '%s' not found", stringService);
2642 else
2643 send_error(req, res, SC_BAD_REQUEST, "No service found");
2644 }
2645 }
2646
2647
_updateReportStatistics(Service_T s,ReportStatics_T statistics)2648 static void _updateReportStatistics(Service_T s, ReportStatics_T statistics) {
2649 if (s->monitor == Monitor_Not)
2650 statistics->unmonitored++;
2651 else if (s->monitor & Monitor_Init)
2652 statistics->init++;
2653 else if (s->error)
2654 statistics->down++;
2655 else
2656 statistics->up++;
2657 statistics->total++;
2658 }
2659
2660
_printReport(HttpRequest req,HttpResponse res)2661 static void _printReport(HttpRequest req, HttpResponse res) {
2662 set_content_type(res, "text/plain");
2663 const char *type = get_parameter(req, "type");
2664 const char *group = Util_urlDecode((char *)get_parameter(req, "group"));
2665 struct ReportStatics_T reportStatics = {};
2666 if (group) {
2667 for (ServiceGroup_T sg = servicegrouplist; sg; sg = sg->next) {
2668 if (IS(group, sg->name)) {
2669 for (list_t m = sg->members->head; m; m = m->next) {
2670 _updateReportStatistics(m->e, &reportStatics);
2671 }
2672 }
2673 }
2674 } else {
2675 for (Service_T s = servicelist; s; s = s->next) {
2676 _updateReportStatistics(s, &reportStatics);
2677 }
2678 }
2679 if (! type) {
2680 StringBuffer_append(res->outputbuffer,
2681 "up: %*.0f (%.1f%%)\n"
2682 "down: %*.0f (%.1f%%)\n"
2683 "initialising: %*.0f (%.1f%%)\n"
2684 "unmonitored: %*.0f (%.1f%%)\n"
2685 "total: %*.0f services\n",
2686 3, reportStatics.up, 100. * reportStatics.up / reportStatics.total,
2687 3, reportStatics.down, 100. * reportStatics.down / reportStatics.total,
2688 3, reportStatics.init, 100. * reportStatics.init / reportStatics.total,
2689 3, reportStatics.unmonitored, 100. * reportStatics.unmonitored / reportStatics.total,
2690 3, reportStatics.total);
2691 } else if (Str_isEqual(type, "up")) {
2692 StringBuffer_append(res->outputbuffer, "%.0f\n", reportStatics.up);
2693 } else if (Str_isEqual(type, "down")) {
2694 StringBuffer_append(res->outputbuffer, "%.0f\n", reportStatics.down);
2695 } else if (Str_startsWith(type, "initiali")) { // allow 'initiali(s|z)ing'
2696 StringBuffer_append(res->outputbuffer, "%.0f\n", reportStatics.init);
2697 } else if (Str_isEqual(type, "unmonitored")) {
2698 StringBuffer_append(res->outputbuffer, "%.0f\n", reportStatics.unmonitored);
2699 } else if (Str_isEqual(type, "total")) {
2700 StringBuffer_append(res->outputbuffer, "%.0f\n", reportStatics.total);
2701 } else {
2702 send_error(req, res, SC_BAD_REQUEST, "Invalid report type: '%s'", type);
2703 }
2704 }
2705
2706
status_service_txt(Service_T s,HttpResponse res)2707 static void status_service_txt(Service_T s, HttpResponse res) {
2708 char buf[STRLEN];
2709 StringBuffer_append(res->outputbuffer,
2710 COLOR_BOLDCYAN "%s '%s'" COLOR_RESET "\n"
2711 " %-28s %s\n",
2712 servicetypes[s->type], s->name,
2713 "status", get_service_status(TXT, s, buf, sizeof(buf)));
2714 StringBuffer_append(res->outputbuffer,
2715 " %-28s %s\n",
2716 "monitoring status", get_monitoring_status(TXT, s, buf, sizeof(buf)));
2717 StringBuffer_append(res->outputbuffer,
2718 " %-28s %s\n",
2719 "monitoring mode", modenames[s->mode]);
2720 StringBuffer_append(res->outputbuffer,
2721 " %-28s %s\n",
2722 "on reboot", onrebootnames[s->onreboot]);
2723 _printStatus(TXT, res, s);
2724 StringBuffer_append(res->outputbuffer, "\n");
2725 }
2726
2727
get_monitoring_status(Output_Type type,Service_T s,char * buf,int buflen)2728 static char *get_monitoring_status(Output_Type type, Service_T s, char *buf, int buflen) {
2729 ASSERT(s);
2730 ASSERT(buf);
2731 if (s->monitor == Monitor_Not) {
2732 if (type == HTML)
2733 snprintf(buf, buflen, "<span class='gray-text'>Not monitored</span>");
2734 else
2735 snprintf(buf, buflen, TextColor_lightYellow("Not monitored"));
2736 } else if (s->monitor & Monitor_Waiting) {
2737 if (type == HTML)
2738 snprintf(buf, buflen, "<span>Waiting</span>");
2739 else
2740 snprintf(buf, buflen, TextColor_white("Waiting"));
2741 } else if (s->monitor & Monitor_Init) {
2742 if (type == HTML)
2743 snprintf(buf, buflen, "<span class='blue-text'>Initializing</span>");
2744 else
2745 snprintf(buf, buflen, TextColor_lightBlue("Initializing"));
2746 } else if (s->monitor & Monitor_Yes) {
2747 if (type == HTML)
2748 snprintf(buf, buflen, "<span>Monitored</span>");
2749 else
2750 snprintf(buf, buflen, "Monitored");
2751 }
2752 return buf;
2753 }
2754
2755
get_service_status(Output_Type type,Service_T s,char * buf,int buflen)2756 static char *get_service_status(Output_Type type, Service_T s, char *buf, int buflen) {
2757 ASSERT(s);
2758 ASSERT(buf);
2759 if (s->monitor == Monitor_Not || s->monitor & Monitor_Init) {
2760 get_monitoring_status(type, s, buf, buflen);
2761 } else if (s->error == 0) {
2762 snprintf(buf, buflen, type == HTML ? "<span class='green-text'>OK</span>" : TextColor_lightGreen("OK"));
2763 } else {
2764 // In the case that the service has actually some failure, the error bitmap will be non zero
2765 char *p = buf;
2766 EventTable_T *et = Event_Table;
2767 while ((*et).id) {
2768 if (s->error & (*et).id) {
2769 bool inverse = false;
2770 if ((*et).id == Event_Link && s->inverseStatus)
2771 inverse = true;
2772 if (p > buf)
2773 p += snprintf(p, buflen - (p - buf), " | ");
2774 if (s->error_hint & (*et).id) {
2775 if (type == HTML)
2776 p += snprintf(p, buflen - (p - buf), "<span class='orange-text'>%s</span>", (*et).description_changed);
2777 else
2778 p += snprintf(p, buflen - (p - buf), TextColor_lightYellow("%s", (*et).description_changed));
2779 } else {
2780 if (type == HTML)
2781 p += snprintf(p, buflen - (p - buf), "<span class='red-text'>%s</span>", inverse ? (*et).description_succeeded : (*et).description_failed);
2782 else
2783 p += snprintf(p, buflen - (p - buf), TextColor_lightRed("%s", inverse ? (*et).description_succeeded : (*et).description_failed));
2784 }
2785 }
2786 et++;
2787 }
2788 }
2789 if (s->doaction)
2790 snprintf(buf + strlen(buf), buflen - strlen(buf) - 1, " - %s pending", actionnames[s->doaction]);
2791 return buf;
2792 }
2793
2794