| /* |
| * Copyright (C) Tildeslash Ltd. All rights reserved. |
| * |
| * This program is free software: you can redistribute it and/or modify |
| * it under the terms of the GNU Affero General Public License version 3. |
| * |
| * This program is distributed in the hope that it will be useful, |
| * but WITHOUT ANY WARRANTY; without even the implied warranty of |
| * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
| * GNU General Public License for more details. |
| * |
| * You should have received a copy of the GNU Affero General Public License |
| * along with this program. If not, see <http://www.gnu.org/licenses/>. |
| * |
| * In addition, as a special exception, the copyright holders give |
| * permission to link the code of portions of this program with the |
| * OpenSSL library under certain conditions as described in each |
| * individual source file, and distribute linked combinations |
| * including the two. |
| * |
| * You must obey the GNU Affero General Public License in all respects |
| * for all of the code used other than OpenSSL. |
| */ |
| |
| |
| #include "config.h" |
| |
| #ifdef HAVE_STDIO_H |
| #include <stdio.h> |
| #endif |
| |
| #ifdef HAVE_STRING_H |
| #include <string.h> |
| #endif |
| |
| #ifdef HAVE_SYS_TYPES_H |
| #include <sys/types.h> |
| #endif |
| |
| #ifdef HAVE_SYS_SOCKET_H |
| #include <sys/socket.h> |
| #endif |
| |
| #ifdef HAVE_ERRNO_H |
| #include <errno.h> |
| #endif |
| |
| #include "monit.h" |
| #include "event.h" |
| #include "process.h" |
| |
| |
| /** |
| * XML routines for status and event notification message handling. |
| * |
| * @file |
| */ |
| |
| |
| /* ----------------------------------------------------------------- Private */ |
| |
| |
| /** |
| * Prints a document header into the given buffer. |
| * @param B StringBuffer object |
| * @param V Format version |
| * @param myip The client-side IP address |
| */ |
| static void document_head(StringBuffer_T B, int V, const char *myip) { |
| StringBuffer_append(B, "<?xml version=\"1.0\" encoding=\"ISO-8859-1\"?>"); |
| if (V == 2) |
| StringBuffer_append(B, "<monit id=\"%s\" incarnation=\"%lld\" version=\"%s\"><server>", Run.id, (long long)Run.incarnation, VERSION); |
| else |
| StringBuffer_append(B, |
| "<monit>" |
| "<server>" |
| "<id>%s</id>" |
| "<incarnation>%lld</incarnation>" |
| "<version>%s</version>", |
| Run.id, |
| (long long)Run.incarnation, |
| VERSION); |
| StringBuffer_append(B, |
| "<uptime>%ld</uptime>" |
| "<poll>%d</poll>" |
| "<startdelay>%d</startdelay>" |
| "<localhostname>%s</localhostname>" |
| "<controlfile>%s</controlfile>", |
| (long)Util_getProcessUptime(Run.pidfile), |
| Run.polltime, |
| Run.startdelay, |
| Run.localhostname ? Run.localhostname : "", |
| Run.controlfile ? Run.controlfile : ""); |
| |
| if (Run.dohttpd) { |
| StringBuffer_append(B, "<httpd><address>%s</address><port>%d</port><ssl>%d</ssl></httpd>", Run.bind_addr ? Run.bind_addr : myip, Run.httpdport, Run.httpdssl); |
| |
| if (Run.mmonitcredentials) |
| StringBuffer_append(B, "<credentials><username>%s</username><password>%s</password></credentials>", Run.mmonitcredentials->uname, Run.mmonitcredentials->passwd); |
| } |
| |
| StringBuffer_append(B, |
| "</server>" |
| "<platform>" |
| "<name>%s</name>" |
| "<release>%s</release>" |
| "<version>%s</version>" |
| "<machine>%s</machine>" |
| "<cpu>%d</cpu>" |
| "<memory>%lu</memory>" |
| "<swap>%lu</swap>" |
| "</platform>", |
| systeminfo.uname.sysname, |
| systeminfo.uname.release, |
| systeminfo.uname.version, |
| systeminfo.uname.machine, |
| systeminfo.cpus, |
| systeminfo.mem_kbyte_max, |
| systeminfo.swap_kbyte_max); |
| } |
| |
| |
| /** |
| * Prints a document footer into the given buffer. |
| * @param B StringBuffer object |
| */ |
| static void document_foot(StringBuffer_T B) { |
| StringBuffer_append(B, "</monit>"); |
| } |
| |
| |
| /** |
| * Prints a service status into the given buffer. |
| * @param S Service object |
| * @param B StringBuffer object |
| * @param L Status information level |
| * @param V Format version |
| */ |
| static void status_service(Service_T S, StringBuffer_T B, short L, int V) { |
| Event_T E = S->eventlist; |
| |
| if (V == 2) |
| StringBuffer_append(B, "<service name=\"%s\"><type>%d</type>", S->name ? S->name : "", S->type); |
| else |
| StringBuffer_append(B, "<service type=\"%d\"><name>%s</name>", S->type, S->name ? S->name : ""); |
| StringBuffer_append(B, |
| "<collected_sec>%ld</collected_sec>" |
| "<collected_usec>%ld</collected_usec>" |
| "<status>%d</status>" |
| "<status_hint>%d</status_hint>" |
| "<monitor>%d</monitor>" |
| "<monitormode>%d</monitormode>" |
| "<pendingaction>%d</pendingaction>", |
| S->collected.tv_sec, |
| (long)S->collected.tv_usec, |
| S->error, |
| S->error_hint, |
| S->monitor, |
| S->mode, |
| S->doaction); |
| if (S->every.type != EVERY_CYCLE) { |
| StringBuffer_append(B, "<every><type>%d</type>", S->every.type); |
| if (S->every.type == 1) |
| StringBuffer_append(B, "<counter>%d</counter><number>%d</number>", S->every.spec.cycle.counter, S->every.spec.cycle.number); |
| else |
| StringBuffer_append(B, "<cron><![CDATA[%s]]></cron>", S->every.spec.cron); |
| StringBuffer_append(B, "</every>"); |
| } |
| |
| /* if the service is in error state, display first active error message to provide more details */ |
| while (E) { |
| if ((E->state == STATE_FAILED || E->state == STATE_CHANGED) && (S->error & E->id) && E->message) { |
| StringBuffer_append(B, "<status_message><![CDATA[%s]]></status_message>", E->message); |
| break; |
| } |
| E = E->next; |
| } |
| if (L == LEVEL_FULL) { |
| if (Util_hasServiceStatus(S)) { |
| if (S->type == TYPE_FILE || S->type == TYPE_DIRECTORY || S->type == TYPE_FIFO || S->type == TYPE_FILESYSTEM) |
| StringBuffer_append(B, "<mode>%o</mode><uid>%d</uid><gid>%d</gid>", S->inf->st_mode & 07777, (int)S->inf->st_uid, (int)S->inf->st_gid); |
| if (S->type == TYPE_FILE || S->type == TYPE_FIFO || S->type == TYPE_DIRECTORY) |
| StringBuffer_append(B, "<timestamp>%ld</timestamp>", (long)S->inf->timestamp); |
| if (S->type == TYPE_FILE) { |
| StringBuffer_append(B, "<size>%llu</size>", (unsigned long long) S->inf->priv.file.st_size); |
| if (S->checksum) |
| StringBuffer_append(B, "<checksum type=\"%s\">%s</checksum>", checksumnames[S->checksum->type], S->inf->priv.file.cs_sum); |
| } |
| if (S->type == TYPE_FILESYSTEM) { |
| StringBuffer_append(B, |
| "<flags>%d</flags>" |
| "<block>" |
| "<percent>%.1f</percent>" |
| "<usage>%.1f</usage>" |
| "<total>%.1f</total>" |
| "</block>", |
| S->inf->priv.filesystem.flags, |
| S->inf->priv.filesystem.space_percent/10., |
| S->inf->priv.filesystem.f_bsize > 0 ? (float)S->inf->priv.filesystem.space_total / (float)1048576 * (float)S->inf->priv.filesystem.f_bsize : 0, |
| S->inf->priv.filesystem.f_bsize > 0 ? (float)S->inf->priv.filesystem.f_blocks / (float)1048576 * (float)S->inf->priv.filesystem.f_bsize : 0); |
| if (S->inf->priv.filesystem.f_files > 0) { |
| StringBuffer_append(B, |
| "<inode>" |
| "<percent>%.1f</percent>" |
| "<usage>%ld</usage>" |
| "<total>%ld</total>" |
| "</inode>", |
| S->inf->priv.filesystem.inode_percent/10., |
| S->inf->priv.filesystem.inode_total, |
| S->inf->priv.filesystem.f_files); |
| } |
| } |
| if (S->type == TYPE_PROCESS) { |
| StringBuffer_append(B, |
| "<pid>%d</pid>" |
| "<ppid>%d</ppid>" |
| "<uptime>%ld</uptime>", |
| S->inf->priv.process.pid, |
| S->inf->priv.process.ppid, |
| (long)S->inf->priv.process.uptime); |
| if (Run.doprocess) { |
| StringBuffer_append(B, |
| "<children>%d</children>" |
| "<memory>" |
| "<percent>%.1f</percent>" |
| "<percenttotal>%.1f</percenttotal>" |
| "<kilobyte>%ld</kilobyte>" |
| "<kilobytetotal>%ld</kilobytetotal>" |
| "</memory>" |
| "<cpu>" |
| "<percent>%.1f</percent>" |
| "<percenttotal>%.1f</percenttotal>" |
| "</cpu>", |
| S->inf->priv.process.children, |
| S->inf->priv.process.mem_percent/10.0, |
| S->inf->priv.process.total_mem_percent/10.0, |
| S->inf->priv.process.mem_kbyte, |
| S->inf->priv.process.total_mem_kbyte, |
| S->inf->priv.process.cpu_percent/10.0, |
| S->inf->priv.process.total_cpu_percent/10.0); |
| } |
| } |
| if (S->type == TYPE_HOST && S->icmplist) { |
| for (Icmp_T i = S->icmplist; i; i = i->next) { |
| StringBuffer_append(B, |
| "<icmp>" |
| "<type>%s</type>" |
| "<responsetime>%.3f</responsetime>" |
| "</icmp>", |
| icmpnames[i->type], |
| i->is_available ? i->response : -1.); |
| } |
| } |
| if ((S->type == TYPE_HOST || S->type == TYPE_PROCESS) && S-> portlist) { |
| Port_T p; |
| for (p = S->portlist; p; p = p->next) { |
| if (p->family == AF_INET) |
| StringBuffer_append(B, |
| "<port>" |
| "<hostname>%s</hostname>" |
| "<portnumber>%d</portnumber>" |
| "<request>%s</request>" |
| "<protocol>%s</protocol>" |
| "<type>%s</type>" |
| "<responsetime>%.3f</responsetime>" |
| "</port>", |
| p->hostname?p->hostname:"", |
| p->port, |
| p->request?p->request:"", |
| p->protocol->name?p->protocol->name:"", |
| Util_portTypeDescription(p), |
| p->is_available?p->response:-1.); |
| else if (p->family == AF_UNIX) |
| StringBuffer_append(B, |
| "<unix>" |
| "<path>%s</path>" |
| "<protocol>%s</protocol>" |
| "<responsetime>%.3f</responsetime>" |
| "</unix>", |
| p->pathname?p->pathname:"", |
| p->protocol->name?p->protocol->name:"", |
| p->is_available?p->response:-1.); |
| } |
| } |
| if (S->type == TYPE_SYSTEM && Run.doprocess) { |
| StringBuffer_append(B, |
| "<system>" |
| "<load>" |
| "<avg01>%.2f</avg01>" |
| "<avg05>%.2f</avg05>" |
| "<avg15>%.2f</avg15>" |
| "</load>" |
| "<cpu>" |
| "<user>%.1f</user>" |
| "<system>%.1f</system>" |
| #ifdef HAVE_CPU_WAIT |
| "<wait>%.1f</wait>" |
| #endif |
| "</cpu>" |
| "<memory>" |
| "<percent>%.1f</percent>" |
| "<kilobyte>%ld</kilobyte>" |
| "</memory>" |
| "<swap>" |
| "<percent>%.1f</percent>" |
| "<kilobyte>%ld</kilobyte>" |
| "</swap>" |
| "</system>", |
| systeminfo.loadavg[0], |
| systeminfo.loadavg[1], |
| systeminfo.loadavg[2], |
| systeminfo.total_cpu_user_percent > 0 ? systeminfo.total_cpu_user_percent/10. : 0, |
| systeminfo.total_cpu_syst_percent > 0 ? systeminfo.total_cpu_syst_percent/10. : 0, |
| #ifdef HAVE_CPU_WAIT |
| systeminfo.total_cpu_wait_percent > 0 ? systeminfo.total_cpu_wait_percent/10. : 0, |
| #endif |
| systeminfo.total_mem_percent/10., |
| systeminfo.total_mem_kbyte, |
| systeminfo.total_swap_percent/10., |
| systeminfo.total_swap_kbyte); |
| } |
| if (S->type == TYPE_PROGRAM && S->program->started) { |
| StringBuffer_append(B, |
| "<program>" |
| "<started>%lu</started>" |
| "<status>%d</status>" |
| "</program>", |
| (unsigned long)S->program->started, |
| S->program->exitStatus); |
| } |
| } |
| } |
| StringBuffer_append(B, "</service>"); |
| } |
| |
| |
| /** |
| * Prints a servicegroups into the given buffer. |
| * @param SG ServiceGroup object |
| * @param B StringBuffer object |
| * @param L Status information level |
| */ |
| static void status_servicegroup(ServiceGroup_T SG, StringBuffer_T B, short L) { |
| StringBuffer_append(B, "<servicegroup name=\"%s\">", SG->name); |
| for (ServiceGroupMember_T SGM = SG->members; SGM; SGM = SGM->next) |
| StringBuffer_append(B, "<service>%s</service>", SGM->name); |
| StringBuffer_append(B, "</servicegroup>"); |
| } |
| |
| |
| /** |
| * Prints a event description into the given buffer. |
| * @param E Event object |
| * @param B StringBuffer object |
| */ |
| static void status_event(Event_T E, StringBuffer_T B) { |
| struct timeval *tv = Event_get_collected(E); |
| StringBuffer_append(B, |
| "<event>" |
| "<collected_sec>%ld</collected_sec>" |
| "<collected_usec>%ld</collected_usec>" |
| "<service>%s</service>" |
| "<type>%d</type>" |
| "<id>%ld</id>" |
| "<state>%d</state>" |
| "<action>%d</action>" |
| "<message><![CDATA[%s]]></message>", |
| tv->tv_sec, |
| (long)tv->tv_usec, |
| Event_get_id(E) == Event_Instance ? "Monit" : Event_get_source_name(E), |
| Event_get_source_type(E), |
| Event_get_id(E), |
| Event_get_state(E), |
| Event_get_action(E), |
| Event_get_message(E)); |
| Service_T s = Event_get_source(E); |
| if (s && s->token) |
| StringBuffer_append(B, "<token>%s</token>", s->token); |
| StringBuffer_append(B, "</event>"); |
| } |
| |
| |
| /* ------------------------------------------------------------------ Public */ |
| |
| |
| /** |
| * Get a XML formated message for event notification or general status |
| * of monitored services and resources. |
| * @param E An event object or NULL for general status |
| * @param L Status information level |
| * @param V Format version |
| * @param myip The client-side IP address |
| */ |
| void status_xml(StringBuffer_T B, Event_T E, short L, int V, const char *myip) { |
| Service_T S; |
| ServiceGroup_T SG; |
| |
| document_head(B, V, myip); |
| if (V == 2) |
| StringBuffer_append(B, "<services>"); |
| for (S = servicelist_conf; S; S = S->next_conf) |
| status_service(S, B, L, V); |
| if (V == 2) { |
| StringBuffer_append(B, "</services><servicegroups>"); |
| for (SG = servicegrouplist; SG; SG = SG->next) |
| status_servicegroup(SG, B, L); |
| StringBuffer_append(B, "</servicegroups>"); |
| } |
| if (E) |
| status_event(E, B); |
| document_foot(B); |
| } |
| |