268 lines
12 KiB
EmacsLisp
268 lines
12 KiB
EmacsLisp
;;; bundle--linux.el --- A bundle of useful Linux information
|
|
|
|
;;; Commentary:
|
|
;; This file contains a variety of Emacs Lisp functions that provide
|
|
;; helpful descriptions for Linux directories, commands, and options.
|
|
|
|
;;; Code:
|
|
|
|
(defvar linux-filesystem-alist
|
|
'(( "/" . "Root directory, the base of the filesystem hierarchy")
|
|
("/bin" . "Essential command binaries, needed for booting")
|
|
("/boot" . "Bootloader files, kernel, and other files needed during booting")
|
|
("/dev" . "Device files representing hardware components")
|
|
("/etc" . "System-wide configuration files")
|
|
("/home" . "User home directories")
|
|
("/lib" . "Shared libraries and kernel modules")
|
|
("/media" . "Mount points for removable media like CDs and USBs")
|
|
("/mnt" . "Temporary mount points for filesystems")
|
|
("/opt" . "Optional application software packages")
|
|
("/proc" . "Virtual filesystem providing info about processes and system")
|
|
("/root" . "Home directory for the root user")
|
|
("/sbin" . "Essential system binaries, usually for the root user")
|
|
("/srv" . "Data directories for services like HTTP, FTP, etc.")
|
|
("/sys" . "Virtual filesystem for kernel objects")
|
|
("/tmp" . "Temporary files, cleared on reboot")
|
|
("/usr" . "User binaries, documentation, libraries, etc.")
|
|
("/var" . "Variable files like logs, databases, etc."))
|
|
"Alist mapping Linux directories to their descriptions.")
|
|
|
|
(defun describe-linux-directory (dirname)
|
|
"Describe the purpose of a Linux directory.
|
|
Takes DIRNAME as an argument and prints its description."
|
|
(interactive "sEnter Linux directory name (e.g., /bin): ")
|
|
(let ((description (assoc-default dirname linux-filesystem-alist)))
|
|
(if description
|
|
(message "%s: %s" dirname description)
|
|
(message "Unknown directory: %s" dirname))))
|
|
|
|
(defvar bash-regex-alist
|
|
'(("empty line" . "^$")
|
|
("backslash" . "\\\\")
|
|
("line starts with a dot" . "^\\.")
|
|
("line ends with a dot" . "\\.$")
|
|
("line starts with a dollar sign" . "^\\$")
|
|
("line starts with a caret" . "^\\^")
|
|
("left square bracket" . "\\[")
|
|
("right square bracket" . "\\]")
|
|
("entire line" . "^.*$")
|
|
("any alphanumeric character" . "[a-zA-Z0-9]")
|
|
("IP Address" . "\\b(?:[0-9]{1,3}\\.){3}[0-9]{1,3}\\b")
|
|
("email" . "[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,}")
|
|
("hex color code" . "#[a-fA-F0-9]{6}")
|
|
("date in yyyy-mm-dd" . "\\b\\d{4}-\\d{2}-\\d{2}\\b")
|
|
("time in hh:mm:ss" . "\\b\\d{2}:\\d{2}:\\d{2}\\b")
|
|
("words without vowels" . "\\b[^aeiou\s]+\\b"))
|
|
"Alist mapping Bash regular expressions to their descriptions.")
|
|
|
|
(defun describe-bash-regex (regex)
|
|
"Describe the purpose of a Bash regular expression.
|
|
Takes REGEX as an argument and prints its description."
|
|
(interactive "sEnter Bash regex (e.g., empty line): ")
|
|
(let ((description (assoc-default regex bash-regex-alist)))
|
|
(if description
|
|
(message "%s: %s" regex description)
|
|
(message "Unknown regular expression: %s" regex))))
|
|
|
|
(defvar linux-process-commands-alist
|
|
'(("ps" . "Shows a snapshot of the current processes")
|
|
("top" . "Displays dynamic real-time view of system stats and processes")
|
|
("htop" . "An interactive process viewer, similar to top but more feature-rich")
|
|
("pgrep" . "Looks up processes based on name and other attributes")
|
|
("pstree" . "Displays the process tree in a tree-like diagram")
|
|
("ps -e" . "Lists all the processes running on the system")
|
|
("ps aux" . "Displays detailed information about all processes")
|
|
("kill" . "Terminates processes by sending signals")
|
|
("killall" . "Kills all processes that match the given name")
|
|
("pkill" . "Send signals to processes based on name and other attributes"))
|
|
"Alist mapping Linux process-checking commands to their descriptions.")
|
|
|
|
(defun describe-linux-process-command (command)
|
|
"Describe the purpose of a Linux process-checking command.
|
|
Takes COMMAND as an argument and prints its description."
|
|
(interactive "sEnter Linux process command (e.g., ps): ")
|
|
(let ((description (assoc-default command linux-process-commands-alist)))
|
|
(if description
|
|
(message "%s: %s" command description)
|
|
(message "Unknown command: %s" command))))
|
|
|
|
(defvar linux-logfiles-alist
|
|
'(("/var/log/syslog" . "System messages, including the messages that are logged during system startup")
|
|
("/var/log/auth.log" . "Security/authorization information, including user logins and authentication")
|
|
("/var/log/kern.log" . "Kernel logs")
|
|
("/var/log/cron.log" . "Logs for cron jobs")
|
|
("/var/log/messages" . "General system activity logs")
|
|
("/var/log/boot.log" . "System boot log")
|
|
("/var/log/daemon.log" . "Background daemon log messages")
|
|
("/var/log/dpkg.log" . "Logs for package installations and removals")
|
|
("/var/log/mail.log" . "Mail server logs")
|
|
("/var/log/user.log" . "User-level messages"))
|
|
"Alist mapping Linux log files to their descriptions.")
|
|
|
|
(defun describe-linux-logfile (logfile)
|
|
"Describe the purpose of a Linux log file.
|
|
Takes LOGFILE as an argument and prints its description."
|
|
(interactive "sEnter Linux log file path (e.g., /var/log/syslog): ")
|
|
(let ((description (assoc-default logfile linux-logfiles-alist)))
|
|
(if description
|
|
(message "%s: %s" logfile description)
|
|
(message "Unknown log file: %s" logfile))))
|
|
|
|
(defvar linux-basic-commands-alist
|
|
'(("list directory contents" . "ls")
|
|
("change directory" . "cd")
|
|
("move or rename files" . "mv")
|
|
("copy files" . "cp")
|
|
("remove files or directories" . "rm")
|
|
("print working directory" . "pwd")
|
|
("display variable value" . "echo")
|
|
("create an empty file" . "touch")
|
|
("change file permissions" . "chmod")
|
|
("change file ownership" . "chown"))
|
|
"Alist mapping basic Linux command descriptions to their commands.")
|
|
|
|
(defun describe-basic-linux-command (command)
|
|
"Describe the purpose of a basic Linux command.
|
|
Takes COMMAND as an argument and prints its description."
|
|
(interactive "sEnter basic Linux command (e.g., ls): ")
|
|
(let ((description (assoc-default command linux-basic-commands-alist)))
|
|
(if description
|
|
(message "%s: %s" command description)
|
|
(message "Unknown command: %s" command))))
|
|
|
|
(defvar chown-options-alist
|
|
'(("-R" . "Operate on files and directories recursively")
|
|
("--from" . "Change the owner and/or group of each file only if its current owner and/or group match specified values")
|
|
("--no-dereference" . "Affect symbolic links instead of the files they point to")
|
|
("--preserve-root" . "Fail when attempting to operate recursively on '/'")
|
|
("--reference" . "Use owner and group of a reference file")
|
|
("-c" . "Report when a change is made")
|
|
("-f" . "Suppress most error messages")
|
|
("-v" . "Output a diagnostic for every file processed"))
|
|
"Alist mapping chown command options to their descriptions.")
|
|
|
|
(defun describe-chown-option (option)
|
|
"Describe the purpose of a chown option.
|
|
Takes OPTION as an argument and prints its description."
|
|
(interactive "sEnter chown option (e.g., -R): ")
|
|
(let ((description (assoc-default option chown-options-alist)))
|
|
(if description
|
|
(message "%s: %s" option description)
|
|
(message "Unknown chown option: %s" option))))
|
|
|
|
(defvar chmod-options-alist
|
|
'(("-R" . "Operate on files and directories recursively")
|
|
("--preserve-root" . "Avoid operating recursively on '/'")
|
|
("-c" . "Report when a change is made")
|
|
("-f" . "Suppress most error messages")
|
|
("-v" . "Output a diagnostic for every file processed")
|
|
("--reference" . "Use mode of a reference file")
|
|
("-w" . "Remove write permission")
|
|
("-x" . "Remove execute permission")
|
|
("-u" . "Set user ID on execution")
|
|
("-g" . "Set group ID on execution"))
|
|
"Alist mapping chmod command options to their descriptions.")
|
|
|
|
(defun describe-chmod-option (option)
|
|
"Describe the purpose of a chmod option.
|
|
Takes OPTION as an argument and prints its description."
|
|
(interactive "sEnter chmod option (e.g., -R): ")
|
|
(let ((description (assoc-default option chmod-options-alist)))
|
|
(if description
|
|
(message "%s: %s" option description)
|
|
(message "Unknown chmod option: %s" option))))
|
|
|
|
(defvar ssh-use-cases-alist
|
|
'(("remote login" . "ssh user@host")
|
|
("run command" . "ssh user@host 'command'")
|
|
("file transfer" . "scp file.txt user@host:/path/")
|
|
("secure ftp" . "sftp user@host")
|
|
("port forwarding" . "ssh -L local_port:remote_host:remote_port user@host")
|
|
("dynamic port forwarding" . "ssh -D port user@host")
|
|
("remote port forwarding" . "ssh -R remote_port:local_host:local_port user@host")
|
|
("tunneling" . "ssh -L local_port:remote_host:remote_port user@host -f -N")
|
|
("agent forwarding" . "ssh -A user@host")
|
|
("ssh multiplexing" . "ssh -M -S /tmp/ssh_socket user@host; ssh -S /tmp/ssh_socket user@host"))
|
|
"Alist mapping SSH use cases to their corresponding commands.")
|
|
|
|
(defun describe-ssh-use-case (use-case)
|
|
"Describe the SSH command for a given use case."
|
|
(interactive "sEnter the SSH use case: ")
|
|
(let ((command (assoc-default use-case ssh-use-cases-alist)))
|
|
(if command
|
|
(message "Command for %s: %s" use-case command)
|
|
(message "Use case not found"))))
|
|
|
|
(defvar mk/remote-*host-aliases*
|
|
'(("website" . "marcus@www.marcuskammer.dev")
|
|
("survey" . "cl@survey.metalisp.dev")
|
|
("pihole" . "ubuntu@pi-hole.fritz.box"))
|
|
"Alist mapping friendly host names to actual SSH-compatible host strings.")
|
|
|
|
(defun mk/remote--get-real-host (alias)
|
|
"Lookup the real host name based on a given ALIAS."
|
|
(or (cdr (assoc alias mk/remote-*host-aliases*)) alias))
|
|
|
|
(defun mk/remote--systemctl-service (alias service command)
|
|
"Execute a systemctl COMMAND on a systemd SERVICE on a remote host identified by ALIAS.
|
|
|
|
ALIAS is a string that specifies the remote host; it can be an
|
|
alias defined in `mk/remote-*host-aliases*'.
|
|
|
|
SERVICE is the name of the systemd service to operate on.
|
|
|
|
COMMAND is the systemctl command to execute on the service (e.g.,
|
|
'start', 'stop', 'status')."
|
|
(let* ((host (mk/remote--get-real-host alias))
|
|
(buffer (generate-new-buffer (format "*%s-%s-%s*" alias service command)))
|
|
(process-name (format "systemctl-%s-%s" command service)))
|
|
(make-process :name process-name
|
|
:buffer buffer
|
|
:command `("ssh" ,host "sudo" "systemctl" ,command ,service)
|
|
:sentinel (lambda (process signal)
|
|
(when (memq (process-status process) '(exit signal))
|
|
(message "Process: %s %s" process signal))))))
|
|
|
|
(defmacro mk/remote-define-systemctl-functions (&rest actions)
|
|
"Dynamically create functions to interact with systemd services on a remote host.
|
|
|
|
Each function will be named `mk/remote-ACTION-service', where
|
|
ACTION is one of the symbols in ACTIONS. The functions will take
|
|
an ALIAS and SERVICE as arguments and call
|
|
`mk/remote--systemctl-service' accordingly."
|
|
`(progn
|
|
,@(mapcar
|
|
(lambda (action)
|
|
`(defun ,(intern (format "mk/remote-%s-service" action)) (alias service)
|
|
(interactive "sEnter the host alias or name: \nsEnter the service name: ")
|
|
(mk/remote--systemctl-service alias service ,(symbol-name action))))
|
|
actions)))
|
|
|
|
(mk/remote-define-systemctl-functions start stop status)
|
|
|
|
(defun mk/remote--log (alias service filename)
|
|
(let* ((filepath (concat "/var/log/" service (unless (string-empty-p filename) (concat "/" filename))))
|
|
(host (mk/remote--get-real-host alias))
|
|
(buffer (generate-new-buffer (format "*%s-%s-%s*" alias service filename)))
|
|
(process-name (format "log-%s-%s" service filename)))
|
|
(make-process :name process-name
|
|
:buffer buffer
|
|
:command `("ssh" ,host "sudo" "tail -f" ,filepath)
|
|
:sentinel (lambda (process signal)
|
|
(when (memq (process-status process) '(exit signal))
|
|
(message "Process: %s %s" process signal))))))
|
|
|
|
(defmacro mk/define-remote-log-function (alias service &optional filename)
|
|
"Define a function to asynchronously tail a remote log file."
|
|
(let ((fname (if filename filename "")))
|
|
`(defun ,(intern (format "mk/remote-log-%s-%s-%s" alias service filename)) ()
|
|
,(format "Tail the remote log file: %s" filename)
|
|
(interactive)
|
|
(mk/remote--log ,alias ,service ,fname))))
|
|
|
|
(mk/define-remote-log-function "website" "nginx" "access.csv")
|
|
(mk/define-remote-log-function "website" "nginx" "error.log")
|
|
(mk/define-remote-log-function "website" "syslog")
|
|
(mk/define-remote-log-function "survey" "nginx" "access.csv")
|
|
(mk/define-remote-log-function "survey" "syslog")
|
|
(mk/define-remote-log-function "pihole" "pihole" "pihole.log")
|