aboutsummaryrefslogtreecommitdiffstats
path: root/elisp
diff options
context:
space:
mode:
Diffstat (limited to 'elisp')
-rw-r--r--elisp/borgbackup.el88
-rw-r--r--elisp/cmk.el108
-rw-r--r--elisp/journalctl.el111
-rw-r--r--elisp/shepherd.el2
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