aboutsummaryrefslogtreecommitdiff
;;; Monitoring --- Server monitoring
;;; Copyright © 2021 Masaya Tojo <masaya@tojo.tokyo>
;;;
;;; This file is part of Monitoring.
;;;
;;; Monitoring is free software; you can redistribute it and/or modify it
;;; under the terms of the GNU General Public License as published by
;;; the Free Software Foundation; either version 3 of the License, or
;;; (at your option) any later version.
;;;
;;; Monitoring 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 Monitoring.  If not, see <http://www.gnu.org/licenses/>.

(define-module (tojo-tokyo monitoring)
  #:use-module (ice-9 popen)
  #:use-module (ice-9 textual-ports)
  #:use-module (ice-9 regex)
  #:use-module ((srfi srfi-1) #:select (any))
  #:use-module ((srfi srfi-26))
  #:use-module (web client))

(define-public current-heartbeat-url
  (make-parameter
   (let ((heartbeat-file "/etc/heartbeat-url"))
     (or (and (file-exists? heartbeat-file)
              (call-with-input-file heartbeat-file get-line))
         "https://heartbeat.test"))))

(define (split-with-spaces x)
  (map match:substring (list-matches "[^ ]+" x)))

(define (command->table command header)
  (let ((port (open-input-pipe command)))
    (get-line port)                     ; skip a header line.
    (let loop ((line (get-line port))
               (table '()))
      (cond ((eof-object? line)
             (close-pipe port)
             table)
            (else
             (loop (get-line port)
                   (cons (map cons header (split-with-spaces line))
                         table)))))))

(define (df)
  (command->table "df" '(filesystem 1k-blocks used available use% mounted-on)))

(define (prefix-/dev/? x)
  (and (string? x)
       (<= 5 (string-length x))
       (string=? (substring x 0 5) "/dev/")))

(define (use%->number x)
  (string->number (string-delete #\% x)))

(define-public (disk-use%-over? threshold)
  (any (cut < threshold <>)
       (map (compose use%->number (cut assoc-ref <> 'use%))
            (filter (compose prefix-/dev/? (cut assoc-ref <> 'filesystem))
                    (df)))))

(define (df-i)
  (command->table "df -i" '(filesystem inodes iused ifree iuse% mounted-on)))

(define (iuse%->number x)
  (or (string->number (string-delete #\% x))
      0))

(define-public (disk-iuse%-over? threshold)
  (any (cut < threshold <>)
       (map (compose iuse%->number (cut assoc-ref <> 'iuse%))
            (filter (compose prefix-/dev/? (cut assoc-ref <> 'filesystem))
                    (df-i)))))

(define-syntax-rule (heartbeat (p? body ...) ...)
  (let ((heartbeat-cancel? #f))
    (when p? body ... (set! heartbeat-cancel? #t)) ...
    (unless heartbeat-cancel?
      (http-request (current-heartbeat-url)))))
(export-syntax heartbeat)