diff options
Diffstat (limited to 'elisp')
-rw-r--r-- | elisp/borgbackup.el | 88 | ||||
-rw-r--r-- | elisp/cmk.el | 108 | ||||
-rw-r--r-- | elisp/journalctl.el | 111 | ||||
-rw-r--r-- | elisp/shepherd.el | 2 |
4 files changed, 235 insertions, 74 deletions
diff --git a/elisp/borgbackup.el b/elisp/borgbackup.el new file mode 100644 index 0000000..52c64cd --- /dev/null +++ b/elisp/borgbackup.el @@ -0,0 +1,88 @@ +;;; borgbackup.el --- Borg backup client -*- lexical-binding: t; -*- +;; +;; Copyright (C) 2023 Óscar Nájera +;; +;; Author: Óscar Nájera <hi@oscarnajera.com> +;; Maintainer: Óscar Nájera <hi@oscarnajera.com> +;; Created: October 26, 2023 +;; Modified: October 26, 2023 +;; Version: 0.0.1 +;; Homepage: https://git.oscarnajera.com/dotfiles/tree/elisp/borgbackup.el +;; Package-Requires: ((emacs "27.1")) +;; +;; This file is not part of GNU Emacs. +;; +;;; Commentary: +;; +;; Description +;; +;;; Code: + +(require 'dash) + +(defcustom borgbackup-repos '(("ssh://borgbackup@sarah/./repos/ingrid" "Admin/sarah/borg/ingrid") + ("ssh://borgbackup@sarah/./repos/oscar" "Admin/sarah/borg/oscar") + ("ssh://backup/media/Backup/daily_backup/" "borgbackup")) + "List of lists where each entry has the repo path and then the pass entry." + :type list + :group 'borgbackup) + +(defun borgbackup--cmd (cmd repo pass-path &rest args) + (let ((process-environment (list (concat "BORG_PASSCOMMAND=pass show " pass-path) + (concat "SSH_AUTH_SOCK=" (getenv "SSH_AUTH_SOCK")) + (concat "HOME=" (getenv "HOME")))) + (err-file (make-temp-file "borg-err"))) + (with-temp-buffer + (apply #'call-process "borg" nil (list (current-buffer) err-file) nil + cmd repo args) + (if (= 0 (file-attribute-size (file-attributes err-file))) ;; no error + (buffer-string) + (user-error (find-file err-file)))))) + +(-let (((&plist :archives) + (json-parse-string + (borgbackup--cmd "list" + "ssh://borgbackup@sarah/./repos/ingrid" + "Admin/sarah/borg/ingrid" "--json") + :object-type 'plist))) + archives) + +(defun borgbackup-info (repo pass-path) + (interactive + (thread-first + (completing-read "Which repo do you want: " borgbackup-repos) + (assoc borgbackup-repos #'string=))) + (message + (borgbackup--cmd "info" repo pass-path))) + +(defun borgbackup--list (repo pass-path) + (-let (((&plist :archives) + (json-parse-string + (borgbackup--cmd "list" repo pass-path "--json") + :object-type 'plist))) + + (with-current-buffer (get-buffer-create "*borg*") + (tabulated-list-mode) + (setq tabulated-list-format [("Archive" 40 t) + ("Date" 27 t) + ("ID" 64 t)]) + (setq tabulated-list-padding 2) + (setq tabulated-list-entries + (cl-map 'list + (lambda (row) + (-let (((&plist :archive :time :id) row)) + (list row (vector archive time id)))) + archives)) + (tabulated-list-init-header) + (tabulated-list-print) + (display-buffer (current-buffer))))) + +(defun borgbackup-list (repo pass-path) + (interactive + (thread-first + (completing-read "Which repo do you want: " borgbackup-repos) + (assoc borgbackup-repos #'string=))) + (borgbackup--list repo pass-path)) + +(provide 'borgbackup) +;;; borgbackup.el ends here diff --git a/elisp/cmk.el b/elisp/cmk.el index 61b71df..6023cf0 100644 --- a/elisp/cmk.el +++ b/elisp/cmk.el @@ -21,6 +21,8 @@ (require 'subr-x) (require 'cl-lib) (require 'dash) +(require 'company) +(require 'csv-mode) (defun cmk-state-coloring (state) "Font color for numeric STATE input as string." @@ -67,41 +69,49 @@ Default is \"%Y-%m-%d %H:%M\"." " "))) (defconst cmk-problem-col-spec - '(("STATE" 5 t :column "service_state" :parser cmk-state-coloring ) + `(("STATE" 5 t :column "service_state" :parser cmk-state-coloring ) ("Host" 9 t :column "host_name") ("Service" 20 t :column "service_description") - ("Since" 16 nil :column "service_last_state_change" :parser cmk-timestamp-to-date) + ("Since" 16 ,(lambda (a b) + (time-less-p + (encode-time (parse-time-string (elt (cadr a) 3))) + (encode-time (parse-time-string (elt (cadr b) 3))))) + :column "service_last_state_change" :parser cmk-timestamp-to-date) ("Check output" 18 t :column "service_plugin_output" :parser cmk-purge-bangs))) (defconst cmk-log-col-spec - '(("host_state" 5 t :column "host_state" :parser cmk-state-coloring) + '(;("host_state" 5 t :column "host_state" :parser cmk-state-coloring) + ("STATE" 5 t :column "log_state" :parser cmk-state-coloring) + ;; ("lsttype" 8 t :column "log_state_type") ("Host" 9 t :column "host_name") ("Service" 12 t :column "service_description") ("log_time" 16 t :column "log_time" :parser cmk-timestamp-to-date) - ("log_command_name" 10 t :column "log_command_name") - ("log_comment" 15 t :column "log_comment") - ("lineno" 8 t :column "log_lineno") - ("lstate" 8 t :column "log_state") - ("lsttype" 8 t :column "log_state_type") - ("log_type" 8 t :column "log_type") - ("host_scheduled_downtime_depth" 5 t :column "host_scheduled_downtime_depth") - ("host_has_been_checked" 5 t :column "host_has_been_checked") + ;; ("log_command_name" 10 t :column "log_command_name") + ;; ("log_comment" 15 t :column "log_comment") + ;; ("lineno" 8 t :column "log_lineno") + ("log_type" 16 t :column "log_type") + ;; ("host_scheduled_downtime_depth" 5 t :column "host_scheduled_downtime_depth") + ;; ("host_has_been_checked" 5 t :column "host_has_been_checked") ("log_plugin_output" 5 t :column "log_plugin_output"))) (defun cmk-parse-rows (buffer column-spec) "Return tabulated entries from BUFFER according to COLUMN-SPEC." (with-current-buffer buffer (goto-char (point-min)) - (cl-loop until (eobp) - collect - (list (count-lines 1 (point)) - (cl-map 'vector - (lambda (entry spec) - (let ((parser (or (plist-get (cdddr spec) :parser) #'identity))) - (funcall parser entry))) - (split-string (buffer-substring-no-properties (point) (line-end-position)) ";") - column-spec)) - do (forward-line)))) + (let (rows + (parsers (mapcar (lambda (spec) + (or (plist-get (cdddr spec) :parser) #'identity)) + column-spec))) + (while (not (eobp)) + (push + (list (count-lines 1 (point)) + (cl-map 'vector + (lambda (parser entry) (funcall parser entry)) + parsers + (split-string (thing-at-point 'line t) ";" nil "\n"))) + rows) + (forward-line)) + (nreverse rows)))) (defun cmk-livestatus-query (query) "One shot livestatus QUERY, return network process." @@ -120,14 +130,63 @@ Default is \"%Y-%m-%d %H:%M\"." (setq tabulated-list-entries (cmk-parse-rows (process-buffer cmks) tabulated-list-format)) (tabulated-list-print 'remember))) +(defun cmk-timestamp-eldoc-documentation () + (when-let ((number (thing-at-point 'number))) + (format-time-string "%Y-%m-%d %H:%M" (seconds-to-time number)))) + +(defconst cmk-livestatus-headers + '("Filter" "Columns" "Stats" "Limit" "ColumnHeaders" "OutputFormat")) + (define-derived-mode cmk-livestatus-mode prog-mode "livestatus" "Major mode for viewing journalctl output." + (add-function :before-until (local 'eldoc-documentation-function) + #'cmk-timestamp-eldoc-documentation) + ;; (setq-local company-backends (cons 'company-cmk-lq company-backends)) + ;; (setq-local completion-at-point-functions '(cmk-lq-capf)) + (add-hook 'completion-at-point-functions 'cmk-lq-capf nil 'local) ;; code for syntax highlighting (setq font-lock-defaults `(((,(rx bol "GET" eow) . font-lock-keyword-face) (,(rx word-start (or "hosts" "services") word-end) . font-lock-constant-face) - (,(rx bol (or "Filter" "Columns" "Stats" "Limit") ":" whitespace) . font-lock-function-name-face) + (,(concat "^" (regexp-opt cmk-livestatus-headers 'words) ":[[:space:]]") . font-lock-function-name-face) (,(rx bol (or "And" "Or" "Negate") ":" whitespace) . font-lock-keyword-face) (,(rx whitespace (or "~" "=" "=~" "~~" "<" ">" "<=" ">=") whitespace) . font-lock-keyword-face))))) +(defun cmk-custom-livestatus-query () + (interactive) + (let ((query cmk-livestatus-query) + (edit-buffer (get-buffer-create "*LQ*"))) + (with-current-buffer edit-buffer + (cmk-livestatus-mode) + (if (string-blank-p query) (insert "GET ") (insert query)) + (local-set-key "\C-c\C-c" + (lambda () + (interactive) + (let ((new-query (-> (buffer-string) (string-trim) (concat "\n\n")))) + (kill-buffer) + (let ((cmk (cmk-livestatus-query new-query))) + (switch-to-buffer (process-buffer cmk)) + (setq csv-separators '(";") + csv-separator-regexp "[;]" + csv-separator-chars '(59)) + (csv-align-mode) + (setq-local cmk-livestatus-query new-query)))))) + (switch-to-buffer edit-buffer))) + +(defun company-cmk-lq (command &optional arg &rest ignored) + (interactive (list 'interactive)) + (cl-case command + (interactive (company-begin-backend 'company-cmk-lq)) + (prefix (company-grab-word)) + (candidates (cl-loop for header in cmk-livestatus-headers + when (string-prefix-p arg header t) collect header)) + (meta (format "This value is named %s" arg)))) + +(defun cmk-lq-capf () + (when (looking-back (rx bol (+ alpha)) 1) + (list (match-beginning 0) + (match-end 0) + (cl-loop for header in cmk-livestatus-headers + when (string-prefix-p (match-string-no-properties 0) header t) + collect (concat header ": "))))) (defun cmk-edit-lq (buffer) "Open window to edit the livestatus query of current BUFFER." @@ -195,10 +254,11 @@ Negate:")) Filter: class = 1 Filter: class = 3 Filter: class = 8 -Or: 3" +Or: 3 +Filter: log_state_type = HARD" (floor (- (time-to-seconds (current-time)) - (* 4 3600)))))) + (* 24 3600)))))) ;;; Get a different csv diff --git a/elisp/journalctl.el b/elisp/journalctl.el index 2d16240..84ec1e1 100644 --- a/elisp/journalctl.el +++ b/elisp/journalctl.el @@ -7,8 +7,8 @@ ;; Created: September 07, 2022 ;; Modified: September 07, 2022 ;; Version: 0.0.1 -;; Homepage: https://github.com/titan/journalctl -;; Package-Requires: ((emacs "27.1")) +;; Homepage: https://git.oscarnajera.com/dotfiles/tree/elisp/journalctl.el +;; Package-Requires: ((emacs "28.1")) ;; ;; This file is not part of GNU Emacs. ;; @@ -19,18 +19,13 @@ ;;; Code: (require 'org) (require 'cl-extra) +(require 'transient) (defvar-local journalctl-current-host nil - "Keeps the optetes of the last call of journalctl.") + "Keeps the optetes of the last call to journalctl.") (defvar-local journalctl-current-opts nil - "Keeps the optetes of the last call of journalctl.") - -(defcustom journalctl-error-keywords - '("Failed" "failed" "Error" "error" "critical" "couldn't" "Can't" "not" "Not" "unreachable" "FATAL") - "Keywords that mark errors in journalctl output." - :group 'journalctl - :type 'string) + "Keeps the optetes of the last call to journalctl.") (defcustom journalctl-warn-keywords '("Warning" "warn" "debug") @@ -38,6 +33,12 @@ :group 'journalctl :type 'string) +(defcustom journalctl-error-keywords + '("Failed" "failed" "Error" "error" "critical" "couldn't" "Can't" "not" "Not" "unreachable" "FATAL") + "Keywords that mark errors in journalctl output." + :group 'journalctl + :type 'string) + (defcustom journalctl-starting-keywords '("Starting" "Activating" "Listening" "Reloading" "connect") "Keywords that mark start of processes or steps in journalctl output." @@ -52,16 +53,16 @@ :type 'string) ;;; faces -(defface journalctl-error-face - '((t :inherit error)) - "Face to mark errors in journalctl's output." - :group 'journalctl) - (defface journalctl-warning-face '((t :inherit warning)) "Face to mark warnings in journalctl's output." :group 'journalctl) +(defface journalctl-error-face + '((t :inherit error)) + "Face to mark errors in journalctl's output." + :group 'journalctl) + (defface journalctl-starting-face '((t :inherit success)) "Face to mark starting units in journalctl's output." @@ -88,30 +89,24 @@ :group 'journalctl) (defvar journalctl-font-lock-keywords - (let ( - ;; generate regex string for each category of keywords - (error-keywords-regexp (regexp-opt journalctl-error-keywords 'words)) - (warn-keywords-regexp (regexp-opt journalctl-warn-keywords 'words)) - (starting-keywords-regexp (regexp-opt journalctl-starting-keywords 'words)) - (finished-keywords-regexp (regexp-opt journalctl-finished-keywords 'words))) - `( - (,warn-keywords-regexp . 'journalctl-warning-face) - (,error-keywords-regexp . 'journalctl-error-face) - (,starting-keywords-regexp . 'journalctl-starting-face) - (,finished-keywords-regexp . 'journalctl-finished-face) - ("^\\([A-Z][a-z]+ [0-9]+ [0-9:]+\\) \\(\\(?:[[:alnum:]]\\|\\.\\)+\\) \\(.*?:\\)" - (1 'journalctl-timestamp-face) - (2 'journalctl-host-face) - (3 'journalctl-process-face)) - - ;; note: order above matters, because once colored, that part won't change. - ;; in general, put longer words first - ))) + ;; note: order matters, because once colored, that part won't change. + ;; in general, put longer words first + `((,(regexp-opt journalctl-warn-keywords 'words) . 'journalctl-warning-face) + (,(regexp-opt journalctl-error-keywords 'words) . 'journalctl-error-face) + (,(regexp-opt journalctl-starting-keywords 'words) . 'journalctl-starting-face) + (,(regexp-opt journalctl-finished-keywords 'words) . 'journalctl-finished-face) + (,(rx bol (group (= 3 alpha) " " (= 2 digit) " " (1+ (in digit ":"))) " " ; timestamp + (group (+ (in alphanumeric ?.))) " " ; host + (group (+? not-newline)) "[" (group (+ digit)) "]:") ; service[PID] + (1 'journalctl-timestamp-face) + (2 'journalctl-host-face) + (3 'journalctl-process-face) + (4 'font-lock-comment-face)))) (defcustom journalctl-hosts - '("/ssh:alina|sudo::" - "/ssh:nina|sudo::" - "/sudo::") + '("/sudo::" + "/ssh:ingrid|sudo::" + "/ssh:sarah|sudo::") "Valid hosts to connect for journal data." :type (list 'string) :group 'file) @@ -121,6 +116,8 @@ "--follow" "--lines" "--reverse" + "--pager-end" + "--catalog" "--grep" "--boot" "--since" @@ -129,6 +126,18 @@ "--priority") "List of possible options to be given to journalctl." ) +(transient-define-prefix journalct-opts () + "Prefix for opts." + ["infixes" + ("f" "follow" "--follow") + ("h" "host" "--host=" :choices journalctl-hosts :prompt "hus: ") + ("u" "unit" "--unit=" + :prompt "a unit: " + :reader journalctl-read-system-units + ;; :always-read t + )]) +;; (journalct-opts) + (defun journalctl--clean-buffer () "Produce a clean buffer for the log. It seems I must kill the buffer for tramp to behave correctly on the new calls." @@ -138,17 +147,21 @@ It seems I must kill the buffer for tramp to behave correctly on the new calls." (kill-buffer buffer)) (get-buffer-create name-buffer))) -(defun journalctl-system-units (host-location) +(defun journalctl--system-units (host-location) "Query HOST-LOCATION (a tramp path) for its systemd units." - (interactive (list (completing-read "Tramp host: " journalctl-hosts))) (let ((default-directory host-location)) (with-temp-buffer (start-file-process "units" (current-buffer) "systemctl" "list-units" "--all" "--quiet" "--full") (sit-for 0.1) (thread-last (split-string (buffer-string) "\n") (mapcar (lambda (line) (car (split-string line)) )) - (delq nil) - (completing-read "Unit: "))))) + (delq nil))))) + +(defun journalctl-read-system-units (_prompt host-location history) + "Query HOST-LOCATION (a tramp path) for its systemd units, given command HISTORY." + (completing-read "Unit: " + (journalctl--system-units host-location) + nil nil nil history)) (defun journalctl-remove-opt (opt) "Remove an OPT flag from the journal query." @@ -167,8 +180,8 @@ If OPT is set, remove this option." (thread-last (pcase opt ((or "--since" "--until") (org-read-date t)) - ((or "--follow" "--reverse" "--user")) - ("--unit" (journalctl-system-units host)) + ((or "--follow" "--reverse" "--user" "--pager-end" "--catalog")) + ("--unit" (journalctl-read-system-units nil host nil)) (_ (read-string (concat opt "= ")))) (list opt) (list) @@ -176,7 +189,7 @@ If OPT is set, remove this option." (journalctl host)))) (defun journalctl-edit-opts () - "Edit the value of 'journalctl-current-opts'." + "Edit the value of `journalctl-current-opts'." (interactive) (let* ((host journalctl-current-host) (opts journalctl-current-opts) @@ -188,8 +201,7 @@ If OPT is set, remove this option." (lambda () (interactive) (goto-char (point-min)) - (let ((standard-input (current-buffer))) - (journalctl host (read))) + (journalctl host (read (current-buffer))) (kill-buffer edit-buff))) (switch-to-buffer (current-buffer))))) @@ -209,9 +221,10 @@ If OPT is set, remove this option." (defun journalctl (host options) "Query the log of HOST given OPTIONS." (interactive - (let* ((picked-host (completing-read "Tramp host: " journalctl-hosts)) - (default-options `(("--unit" ,(journalctl-system-units picked-host)) ("--follow")))) - (list picked-host default-options))) + (let ((picked-host (completing-read "Tramp host: " journalctl-hosts))) + (list picked-host + `(("--unit" ,(journalctl-read-system-units nil picked-host nil)) ("--follow") + ("--lines" "100"))))) (let ((buffer (journalctl--clean-buffer)) (default-directory host)) (apply #'start-file-process "Journal" buffer "journalctl" (flatten-tree options)) diff --git a/elisp/shepherd.el b/elisp/shepherd.el index 1141d31..48a1aa8 100644 --- a/elisp/shepherd.el +++ b/elisp/shepherd.el @@ -63,7 +63,7 @@ `(,name . (lambda (service) (message (shepherd-command ,name service)))))) - '(status start stop restart enable))))) + '(start status stop restart enable))))) (provide 'shepherd) ;;; shepherd.el ends here |