monit-dev
[Top][All Lists]
Advanced

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[monit-dev] Patch for JSON output


From: paul
Subject: [monit-dev] Patch for JSON output
Date: Sun, 15 Mar 2009 01:08:31 +0100
User-agent: Thunderbird 2.0.0.17 (Windows/20080914)

Hi,

I'm not a C coder by any means but I thought it would be easier to write the code to handle _status?format=json requests than spending my life with XML parsing.

It's a plain translation of xml.c so I don't claim authorship or anything. The code is known to yield valid JSON on my box (tested with pythons json parser) but far from completely tested.

The patch is against 5.0beta7, I hope someone finds it useful.

cheers
 Paul
diff -Naur monit-5.0_beta7/http/cervlet.c monit-5.0_beta7.patched/http/cervlet.c
--- monit-5.0_beta7/http/cervlet.c      2009-02-15 13:46:18.000000000 +0100
+++ monit-5.0_beta7.patched/http/cervlet.c      2009-03-14 23:03:13.000000000 
+0100
@@ -2473,6 +2473,13 @@
     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 -Naur monit-5.0_beta7/json.c monit-5.0_beta7.patched/json.c
--- monit-5.0_beta7/json.c      1970-01-01 01:00:00.000000000 +0100
+++ monit-5.0_beta7.patched/json.c      2009-03-14 23:03:13.000000000 +0100
@@ -0,0 +1,445 @@
+/*
+ * 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 <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 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 <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 "monitor.h"
+#include "event.h"
+#include "process.h"
+
+
+/**
+ *  JSON routines for status and event notification message handling.
+ *
+ *  @author Martin Pala, <address@hidden>
+ *
+ *  @version \$Id: json.c,v 1.54 2009/02/13 09:18:12 hauk Exp $
+ *
+ *  @file
+ */
+
+
+/* ------------------------------------------------------------- Definitions */
+
+
+/** Defines an output buffer object */
+typedef struct mybuffer {
+  char          *buf;                               /**< Output buffer       */
+  size_t         bufsize;                           /**< Output buffer size  */
+  size_t         bufused;                           /**< Output buffer usage */
+} Buffer_T;
+
+
+/* -------------------------------------------------------------- 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_event(Event_T, Buffer_T *);
+static void buf_print(Buffer_T *, const char *, ...);
+
+
+/* ------------------------------------------------------------------ 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;
+
+  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
+  {
+    buf_print(&B, ", \"servicelist\":[ ");
+    for(S = servicelist_conf; S; S = S->next_conf)
+    {
+      status_service(S, &B, L);
+      S->next_conf ? buf_print(&B, ", ") : buf_print(&B, " ");
+    }
+    buf_print(&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) {
+
+
+  buf_print(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)
+  {
+    buf_print(B,
+      ", \"httpd\": {"
+      "\"address\":\"%s\","
+      "\"port\":%d,"
+      "\"ssl\":%d }",
+      Run.bind_addr?Run.bind_addr:"",
+      Run.httpdport,
+      Run.httpdssl);
+  }
+ 
+  buf_print(B,
+   ", \"platform\": {"
+   "\"name\":\"%s\", \"release\":\"%s\", \"version\":\"%s\","
+   "\"machine\":\"%s\", \"cpu\":%d, \"memory\":%lu }}}",
+   systeminfo.uname.sysname,
+   systeminfo.uname.release,
+   systeminfo.uname.version,
+   systeminfo.uname.machine,
+   systeminfo.cpus,
+   systeminfo.mem_kbyte_max);
+
+}
+
+
+/**
+ * Prints a document footer into the given buffer.
+ * @param B Buffer object
+ */
+static void document_foot(Buffer_T *B) {
+
+  buf_print(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;
+  buf_print(B,
+        "{"
+        "\"type\":%d, \"collected_sec\":%ld, "
+        "\"collected_usec\":%ld, \"name\":\"%s\", \"status\":%llu, "
+        "\"monitor\":%d, \"monitormode\":%d, \"pendingaction\":%d, "
+        "\"group\":\"%s\"",
+           S->type,
+           S->collected.tv_sec,
+           S->collected.tv_usec,
+           S->name?S->name:"",
+           S->error,
+           S->monitor,
+           S->mode,
+           S->doaction,
+           S->group?S->group:"");
+  /* 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) {
+       buf_print(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) {
+        buf_print(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)  {
+        buf_print(B,
+               ", \"timestamp\":%ld",
+               (long)S->inf->timestamp);
+      }
+      if(S->type == TYPE_FILE) {
+        buf_print(B,
+               ", \"size\":%llu",
+               (unsigned long long) S->inf->st_size);
+        if(S->checksum) {
+          buf_print(B,
+                 ", \"checksum_type\":\"%s\", \"checksum\":\"%s\"",
+                 checksumnames[S->checksum->type], S->inf->cs_sum);
+        }
+      }
+      if(S->type == TYPE_FILESYSTEM) {
+        buf_print(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) {
+          buf_print(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) {
+        buf_print(B,
+        ", \"pid\":%d, \"ppid\":%d, \"uptime\":%ld",
+               S->inf->pid,
+               S->inf->ppid,
+               (long)S->inf->uptime);
+        if(Run.doprocess) {
+          buf_print(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;
+        buf_print(B, ", [");
+        for(i= S->icmplist; i; i= i->next) {
+          buf_print(B,
+          "\"icmp\": {"
+          "\"type\":\"%s\", \"responsetime\":%.3f }%s",
+                 icmpnames[i->type],
+                 i->is_available?i->response:-1.,
+          i->next?", ":" ");
+        }
+        buf_print(B, "]");
+      }
+      if((S->type == TYPE_HOST || S->type == TYPE_PROCESS) && S-> portlist) {
+        Port_T p;
+        buf_print(B, "[");
+        for(p= S->portlist; p; p= p->next) {
+          if(p->family == AF_INET) {
+            buf_print(B,
+            "\"port\": { "
+            "\"hostname\":\"%s\", \"portnumber\":%d, \"request\":\"%s\", "
+            "\"protocol\":\"%s\", \"type\":\"%s\", \"responsetime\":%.3f }%s",
+                   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.,
+            p->next?", ":" ");
+         
+          } else if(p->family == AF_UNIX) {
+            buf_print(B,
+            "\"unix\": { "
+            "\"path\":\"%s\", \"protocol\":\"%s\", \"responsetime\":%.3f }%s",
+                   p->pathname?p->pathname:"",
+                   p->protocol->name?p->protocol->name:"",
+                   p->is_available?p->response:-1.,
+            p->next?", ":" ");
+          }
+        }
+        buf_print(B, "]");
+      }
+      if(S->type == TYPE_SYSTEM && Run.doprocess) {
+        buf_print(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);
+      }
+    }
+  }
+  buf_print(B, "}"); //closing service
+}
+
+
+/**
+ * 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);
+
+  buf_print(B,
+    "{"
+    "\"collected_sec\":%ld, \"collected_usec\":%ld, "
+    "\"service\":\"%s\", \"type\":%d, \"group\":\"%s\", \"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_source_group(E),
+    Event_get_id(E),
+    Event_get_state(E),
+    Event_get_action(E),
+    Event_get_message(E));
+  if (s->token) {
+    buf_print(B,
+      ", \"token\":\"%s\"",
+      s->token);
+  }
+  buf_print(B,
+    "}");
+}
+
+
+/**
+ * Prints a string into the given buffer.
+ * @param B Buffer object
+ * @param m A formated string to be written to the buffer
+ */
+static void buf_print(Buffer_T *B, const char *m, ...) {
+  if(m)
+  {
+    va_list  ap;
+    char    *buf;
+    long     need= 0;
+    ssize_t  have= 0;
+
+    va_start(ap, m);
+    buf = Util_formatString(m, ap, &need);
+    va_end(ap);
+
+    have = (*B).bufsize - (*B).bufused;
+    if(have <= need)
+    {
+      (*B).bufsize += (need + STRLEN);
+      (*B).buf = xresize((*B).buf, (*B).bufsize);
+      if(!(*B).bufused)
+      {
+        memset((*B).buf, 0, (*B).bufsize);
+      }
+    }
+    memcpy(&(*B).buf[(*B).bufused], buf, need);
+    (*B).bufused += need;
+    (*B).buf[(*B).bufused]= 0;
+    FREE(buf);
+  }
+}
+
diff -Naur monit-5.0_beta7/monitor.h monit-5.0_beta7.patched/monitor.h
--- monit-5.0_beta7/monitor.h   2009-02-15 13:46:17.000000000 +0100
+++ monit-5.0_beta7.patched/monitor.h   2009-03-14 23:03:13.000000000 +0100
@@ -908,6 +908,7 @@
 int  check_service_status(Service_T);
 void printhash(char *);  
 char *status_xml(Event_T, short);
+char *status_json(Event_T, short);
 int  handle_mmonit(Event_T);
 int  do_wakeupcall();
 

reply via email to

[Prev in Thread] Current Thread [Next in Thread]