"Fossies" - the Fresh Open Source Software Archive

Member "monit-5.28.0/src/http/cervlet.c" (28 Mar 2021, 159877 Bytes) of package /linux/privat/monit-5.28.0.tar.gz:


The requested HTML page contains a <FORM> tag that is unusable on "Fossies" in "automatic" (rendered) mode so that page is shown as HTML source code syntax highlighting (style: standard) with prefixed line numbers and code folding option. Alternatively you can here view or download the uninterpreted source code file. For more information about "cervlet.c" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 5.27.2_vs_5.28.0.

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