diff --git a/http/cervlet.c b/http/cervlet.c index 6ebd985..09599fd 100644 --- a/http/cervlet.c +++ b/http/cervlet.c @@ -2281,6 +2281,13 @@ static void print_status(HttpRequest req, HttpResponse res, int version) FREE(D); set_content_type(res, "text/xml"); } + else if(stringFormat && Util_startsWith(stringFormat, "json")) + { + char *D = status_json(NULL, level); + out_print(res, "%s", D); + FREE(D); + set_content_type(res, "application/json"); + } else { char *uptime = Util_getUptime(Util_getProcessUptime(Run.pidfile), " "); diff --git a/json.c b/json.c new file mode 100644 index 0000000..c94d0a9 --- /dev/null +++ b/json.c @@ -0,0 +1,435 @@ +/* + * Copyright (C) 2009 Tildeslash Ltd. All rights reserved. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU 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 General Public License + * along with this program. If not, see . + * + * 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 General Public License in all respects + * for all of the code used other than OpenSSL. If you modify + * file(s) with this exception, you may extend this exception to your + * version of the file(s), but you are not obligated to do so. If you + * do not wish to do so, delete this exception statement from your + * version. If you delete this exception statement from all source + * files in the program, then also delete it here. + */ + + +#include + +#ifdef HAVE_STDIO_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_SYS_TYPES_H +#include +#endif + +#ifdef HAVE_SYS_SOCKET_H +#include +#endif + +#ifdef HAVE_ERRNO_H +#include +#endif + +#include "monitor.h" +#include "event.h" +#include "process.h" + + +/** + * JSON routines for status and event notification message handling. + * + * @author Martin Pala, + * + * @file + */ + + + +/* -------------------------------------------------------------- Prototypes */ + + +static void document_head(Buffer_T *); +static void document_foot(Buffer_T *); +static void status_service(Service_T, Buffer_T *, short); +static void status_servicegroup(ServiceGroup_T, Buffer_T *, short); +static void status_event(Event_T, Buffer_T *); + + +/* ------------------------------------------------------------------ Public */ + + +/** + * Return JSON 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 + * @return JSON document or NULL in the case of error. The caller must free +* the memory. + */ +char *status_json(Event_T E, short L) { + + Buffer_T B; + Service_T S; + ServiceGroup_T SG; + + memset(&B, 0, sizeof(Buffer_T)); + + document_head(&B); + + if(E) + { + /* there is no use for status level in the event (at least now) */ + status_event(E, &B); + } + else + { + Util_stringbuffer(&B, ", \"services\":[ "); + for(S = servicelist_conf; S; S = S->next_conf) + { + status_service(S, &B, L); + S->next_conf ? Util_stringbuffer(&B, ", ") : Util_stringbuffer(&B, " "); + } + Util_stringbuffer(&B, " ]"); + Util_stringbuffer(&B, ", \"servicegroups\":[ "); + for(SG = servicegrouplist; SG; SG = SG->next) + { + status_servicegroup(SG, &B, L); + SG->next ? Util_stringbuffer(&B, ", ") : Util_stringbuffer(&B, " "); + } + Util_stringbuffer(&B, " ]"); + } + document_foot(&B); + + return B.buf; + +} + + +/* ----------------------------------------------------------------- Private */ + + +/** + * Prints a document header into the given buffer. + * @param B Buffer object + */ +static void document_head(Buffer_T *B) { + + + Util_stringbuffer(B, + "{\"monit\": " + "{\"server\": {" + "\"id\":\"%s\", " + "\"incarnation\":%ld, " + "\"version\":\"%s\", " + "\"uptime\":%ld, " + "\"poll\":%d, " + "\"startdelay\":%d, " + "\"localhostname\":\"%s\", " + "\"controlfile\":\"%s\"", + Run.id, + Run.incarnation, + VERSION, + (long)Util_getProcessUptime(Run.pidfile), + Run.polltime, + Run.startdelay, + Run.localhostname ? Run.localhostname : "", + Run.controlfile ? Run.controlfile : ""); + + if(Run.dohttpd) + { + Util_stringbuffer(B, + ", \"httpd\": {" + "\"address\":\"%s\"," + "\"port\":%d," + "\"ssl\":%d }", + Run.bind_addr?Run.bind_addr:"", + Run.httpdport, + Run.httpdssl); + + if (Run.mmonitcredentials) + Util_stringbuffer(B, + ", \"credentials\": {" + "\"username\": \"%s\"," + "\"password\": \"%s\"" + "}", + Run.mmonitcredentials->uname, + Run.mmonitcredentials->passwd); + + } + + Util_stringbuffer(B, + "}," + "\"platform\": {" + "\"name\":\"%s\", \"release\":\"%s\", \"version\":\"%s\"," + "\"machine\":\"%s\", \"cpu\":%d, \"memory\":%lu, \"swap\":%lu" + " }", + 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 Buffer object + */ +static void document_foot(Buffer_T *B) { + + Util_stringbuffer(B, "}}"); //closing monit + +} + + +/** + * Prints a service status into the given buffer. + * @param S Service object + * @param B Buffer object + * @param L Status information level + */ +static void status_service(Service_T S, Buffer_T *B, short L) { + Event_T E = S->eventlist; + Util_stringbuffer(B, + "{" + "\"type\":%d, \"collected_sec\":%ld, " + "\"collected_usec\":%ld, \"name\":\"%s\", \"status\":%d, \"status_hint\":%d, " + "\"monitor\":%d, \"monitormode\":%d, \"pendingaction\":%d", + S->type, + S->collected.tv_sec, + S->collected.tv_usec, + S->name?S->name:"", + S->error, + S->error_hint, + S->monitor, + S->mode, + S->doaction); + /* if the service is in error state, display first active error message to provide more details */ + while (E) { + if (E->state == STATE_FAILED && (S->error & E->id) && E->message) { + Util_stringbuffer(B, ", \"status_message\":\"%s\"", 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) { + Util_stringbuffer(B, + ", \"mode\":%o, \"uid\":%d, \"gid\":%d", + 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) { + Util_stringbuffer(B, + ", \"timestamp\":%ld", + (long)S->inf->timestamp); + } + if(S->type == TYPE_FILE) { + Util_stringbuffer(B, + ", \"size\":%llu", + (unsigned long long) S->inf->st_size); + if(S->checksum) { + Util_stringbuffer(B, + ", \"checksum\": {\"type\": \"%s\", \"sum\":\"%s\" }", + checksumnames[S->checksum->type], S->inf->cs_sum); + } + } + if(S->type == TYPE_FILESYSTEM) { + Util_stringbuffer(B, + ", \"flags\":%ld, " + "\"block\": { \"percent\":%.1f, \"usage\":\"%.1f MB\", " + "\"total\":\"%.1f MB\" }", + S->inf->flags, + S->inf->space_percent/10., + (float)S->inf->space_total / (float)1048576 * (float)S->inf->f_bsize, + (float)S->inf->f_blocks / (float)1048576 * (float)S->inf->f_bsize); + if(S->inf->f_files > 0) { + Util_stringbuffer(B, + ", \"inode\": {" + "\"percent\":%.1f, \"usage\":%ld, " + "\"total\":%ld }", + S->inf->inode_percent/10., + S->inf->inode_total, + S->inf->f_files); + } + } + if(S->type == TYPE_PROCESS) { + Util_stringbuffer(B, + ", \"pid\":%d, \"ppid\":%d, \"uptime\":%ld", + S->inf->pid, + S->inf->ppid, + (long)S->inf->uptime); + if(Run.doprocess) { + Util_stringbuffer(B, + ", \"children\":%d, " + "\"memory\": {" + "\"percent\":%.1f, \"percenttotal\":%.1f, " + "\"kilobyte\":%ld, \"kilobytetotal\":%ld }, " + "\"cpu\": {\"percent\":%.1f, \"percenttotal\":%.1f }", + S->inf->children, + S->inf->mem_percent/10.0, + S->inf->total_mem_percent/10.0, + S->inf->mem_kbyte, + S->inf->total_mem_kbyte, + S->inf->cpu_percent/10.0, + S->inf->total_cpu_percent/10.0); + } + } + if(S->type == TYPE_HOST && S->icmplist) { + Icmp_T i; + Util_stringbuffer(B, ", \"icmps\": ["); + for(i= S->icmplist; i; i= i->next) { + Util_stringbuffer(B, + " { \"type\":\"%s\", \"responsetime\":%.3f }%s", + icmpnames[i->type], + i->is_available?i->response:-1., + i->next?", ":" "); + } + Util_stringbuffer(B, "]"); + } + if((S->type == TYPE_HOST || S->type == TYPE_PROCESS) && S-> portlist) { + Port_T p; + Util_stringbuffer(B, ", \"ports\": ["); + for(p= S->portlist; p; p= p->next) { + if(p->family == AF_INET) { + Util_stringbuffer(B, + " { \"family\": \"inet\", \"protocol\":\"%s\", " + "\"hostname\":\"%s\", \"portnumber\":%d, \"request\":\"%s\", " + " \"type\":\"%s\", \"responsetime\":%.3f }%s", + p->protocol->name?p->protocol->name:"", + p->hostname?p->hostname:"", + p->port, + p->request?p->request:"", + Util_portTypeDescription(p), + p->is_available?p->response:-1., + p->next?", ":" "); + + } else if(p->family == AF_UNIX) { + Util_stringbuffer(B, + " { \"family\": \"unix\", \"protocol\":\"%s\", " + "\"path\":\"%s\", \"responsetime\":%.3f }%s", + p->protocol->name?p->protocol->name:"", + p->pathname?p->pathname:"", + p->is_available?p->response:-1., + p->next?", ":" "); + } + } + Util_stringbuffer(B, "]"); + } + if(S->type == TYPE_SYSTEM && Run.doprocess) { + Util_stringbuffer(B, + ", \"system\": {" + "\"load\": { \"avg01\":%.2f, \"avg05\":%.2f, " + "\"avg15\":%.2f }, " + "\"cpu\": { \"user\":%.1f, \"system\":%.1f" +#ifdef HAVE_CPU_WAIT + ", \"wait\":%.1f" +#endif + "}, \"memory\": {" + "\"percent\":%.1f, \"kilobyte\":%.ld }}", + systeminfo.loadavg[0], + systeminfo.loadavg[1], + systeminfo.loadavg[2], + systeminfo.total_cpu_user_percent/10., + systeminfo.total_cpu_syst_percent/10., + #ifdef HAVE_CPU_WAIT + systeminfo.total_cpu_wait_percent/10., + #endif + systeminfo.total_mem_percent/10., + systeminfo.total_mem_kbyte); + } + } + } + Util_stringbuffer(B, "}"); //closing service +} + +/** + * Prints a servicegroups into the given buffer. + * @param SG ServiceGroup object + * @param B Buffer object + * @param L Status information level + */ +static void status_servicegroup(ServiceGroup_T SG, Buffer_T *B, short L) { + ServiceGroupMember_T SGM; + + Util_stringbuffer(B, + "{ \"name\": \"%s\", " + "\"services\": [ ", + SG->name); + for (SGM = SG->members; SGM; SGM = SGM->next) { + Util_stringbuffer(B, "\"%s\"", SGM->name); + Util_stringbuffer(B, SGM->next ? ", ": " "); + } + Util_stringbuffer(B, "]}"); +} + + +/** + * Prints a event description into the given buffer. + * @param E Event object + * @param B Buffer object + */ +static void status_event(Event_T E, Buffer_T *B) { + + Service_T s; + struct timeval *tv; + + ASSERT(E); + + if(!(s = Event_get_source(E))) + return; + + tv = Event_get_collected(E); + + Util_stringbuffer(B, + "{" + "\"collected_sec\":%ld, \"collected_usec\":%ld, " + "\"service\":\"%s\", \"type\":%d, \"id\":%d, " + "\"state\":%d, \"action\":%d, \"message\":\"%s\"", + tv->tv_sec, + tv->tv_usec, + 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)); + if (s->token) { + Util_stringbuffer(B, + ", \"token\":\"%s\"", + s->token); + } + Util_stringbuffer(B, + "}"); +} diff --git a/monitor.h b/monitor.h index 87f9e28..2ccd9c1 100644 --- a/monitor.h +++ b/monitor.h @@ -967,6 +967,7 @@ void reset_procinfo(Service_T); int check_service_status(Service_T); void printhash(char *); char *status_xml(Event_T, short, int); +char *status_json(Event_T, short); int handle_mmonit(Event_T); int do_wakeupcall();