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>&nbsp;&gt;&nbsp;<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 &copy; 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 &copy; 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>&nbsp;</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>&nbsp;</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]&nbsp;[%.2f]&nbsp;[%.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&nbsp;", systeminfo.cpu.usage.user > 0. ? systeminfo.cpu.usage.user : 0.);
1326         if (systeminfo.statisticsAvailable & Statistics_CpuSystem)
1327                 StringBuffer_append(res->outputbuffer, "%.1f%%sy&nbsp;", systeminfo.cpu.usage.system > 0. ? systeminfo.cpu.usage.system : 0.);
1328         if (systeminfo.statisticsAvailable & Statistics_CpuNice)
1329                 StringBuffer_append(res->outputbuffer, "%.1f%%ni&nbsp;", systeminfo.cpu.usage.nice > 0. ? systeminfo.cpu.usage.nice : 0.);
1330         if (systeminfo.statisticsAvailable & Statistics_CpuIOWait)
1331                 StringBuffer_append(res->outputbuffer, "%.1f%%wa&nbsp;", 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, "&lt;");
1456                                                 else if (output[i] == '>')
1457                                                         StringBuffer_append(res->outputbuffer, "&gt;");
1458                                                 else if (output[i] == '&')
1459                                                         StringBuffer_append(res->outputbuffer, "&amp;");
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&#47;s</td>", Convert_bytes2str(Link_getBytesOutPerSecond(s->inf.net->stats), buf));
1517                         StringBuffer_append(res->outputbuffer, "<td class='right'>%s&#47;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, "&nbsp;&nbsp;<b>|</b>&nbsp;&nbsp;");
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, "&nbsp;&nbsp;<b>|</b>&nbsp;&nbsp;");
1788                         for (Port_T port = s->portlist; port; port = port->next) {
1789                                 if (port != s->portlist)
1790                                         StringBuffer_append(res->outputbuffer, "&nbsp;&nbsp;<b>|</b>&nbsp;&nbsp;");
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