;; This Source Code Form is subject to the terms of the Mozilla Public ;; License, v. 2.0. If a copy of the MPL was not distributed with this ;; file, You can obtain one at http://mozilla.org/MPL/2.0/. (in-package #:syslog_helper) (defun .one-of (choices) (apply #'smug:.or (mapcar #'smug:.string= choices))) (defun .numeric-in-range (&key (min 0) max (min-inclusive t) max-inclusive) (let ((min-op (if min-inclusive #'<= #'<)) (max-op (if max-inclusive #'>= #'>))) (smug:.let* ((num (smug:.first (smug:.map 'string (smug:.is #'digit-char-p))))) (let ((num (parse-integer num))) (if (and (funcall min-op min num) (if max (funcall max-op max num) t)) (smug:.identity num) (smug:.fail)))))) (defun .time () (flet ((.min-sec () (.numeric-in-range :max 60)) (.hour () (.numeric-in-range :max 24))) (smug:.let* ((hour (smug:.prog1 (.hour) (smug:.char= #\:))) (minute (smug:.prog1 (.min-sec) (smug:.char= #\:))) (second (.min-sec))) (smug:.identity (format nil "~2,'0d:~2,'0d:~2,'0d" hour minute second))))) (defun .timestamp () (flet ((.month () (.one-of '("Jan" "Feb" "Mar" "Apr" "May" "Jun" "Jul" "Aug" "Sep" "Oct" "Nov" "Dec"))) (.day () (.numeric-in-range :max 32))) (smug:.let* ((month (smug:.prog1 (.month) (smug:.map 'list (smug:.char= #\space)))) (day (smug:.prog1 (.day) (smug:.map 'list (smug:.char= #\space)))) (time (.time))) (smug:.identity (format nil "~a ~a ~a" month day time))))) (defun .hostname () (smug:.prog1 (smug:.first (smug:.map 'string (smug:.or (smug:.char= #\.) (smug:.is #'alphanumericp)))) (smug:.char= #\space))) (defun .tag () (smug:.first (smug:.prog1 (smug:.let* ((tag (smug:.optional (smug:.map 'string (smug:.is #'alphanumericp)))) (process (smug:.optional (smug:.prog2 (smug:.char= #\[) (smug:.map 'string (smug:.is #'digit-char-p)) (smug:.char= #\]))))) (if (> (length tag) 32) (smug:.fail) (smug:.identity (list (or tag "untagged") (when process (parse-integer process)))))) (smug:.optional (smug:.char= #\:))))) (defun .dnsmasq-reply () (smug:.progn (smug:.optional (smug:.char= #\space)) (.one-of '("cached " "reply " "/tmp/hosts/dhcp " "DHCP ")) (smug:.let* ((query (smug:.prog1 (smug:.map 'string (smug:.or (smug:.char= #\-) (smug:.char= #\_) (smug:.char= #\.) (smug:.is #'alphanumericp))) (smug:.string= " is "))) (reply (smug:.prog1 (smug:.map 'string (smug:.item)) (smug:.not (smug:.item))))) (smug:.identity (list :query query :reply reply))))) (defun .dnsmasq-query () (smug:.progn (smug:.optional (smug:.char= #\space)) (smug:.string= "query") (smug:.let* ((query-type (smug:.prog2 (smug:.char= #\[) (smug:.map 'string (smug:.is #'upper-case-p)) (smug:.char= #\]) (smug:.char= #\space))) (query (smug:.prog1 (smug:.map 'string (smug:.or (smug:.char= #\-) (smug:.char= #\_) (smug:.char= #\.) (smug:.is #'alphanumericp))) (smug:.string= " from "))) (from (smug:.prog1 (smug:.map 'string (smug:.item)) (smug:.not (smug:.item))))) (smug:.identity (list :query-type query-type :query query :from from))))) (defstruct (priority (:type vector)) facility severity) (defun extract-priority (line) "Extract a priority from a syslog line and parse it into a severity/priority list" (when (char= (elt line 0) #\<) (multiple-value-bind (result end) (parse-integer line :start 1 :junk-allowed t) (when (and (< end (length line)) (char= (elt line end) #\>)) (values (multiple-value-call #'vector (floor result 8)) (1+ end)))))) (defun parse-syslog (line) (multiple-value-bind (priority end-priority) (extract-priority line) (let ((line (subseq line end-priority))) (multiple-value-bind (timestamp ts-leftover) (smug:parse (smug:.optional (smug:.prog1 (.timestamp) (smug:.map 'list (smug:.char= #\space)))) line) (multiple-value-bind (hostname hn-leftover) (smug:parse (smug:.optional (.hostname)) ts-leftover) (multiple-value-bind (tag-els leftover) (smug:parse (.tag) hn-leftover) (handle-log-message (alexandria:make-keyword (string-upcase (car tag-els))) (list :priority priority :timestamp timestamp :hostname hostname :tag-value (car tag-els) :tag-pid (cadr tag-els)) leftover line) (values (priority-facility priority) (priority-severity priority) tag-els timestamp leftover)))))))