Adding files
This commit is contained in:
6
00-linuxplus-lesson-history.el
Normal file
6
00-linuxplus-lesson-history.el
Normal file
@@ -0,0 +1,6 @@
|
||||
;;; 00-linuxplus-lesson-history.el --- linuxplus-lesson-history
|
||||
|
||||
(defvar linuxplus-lesson-history nil
|
||||
"Alist of Linux+ lesson names and reported scores.")
|
||||
|
||||
;;; 00-linuxplus-lesson-history.el ends here
|
||||
7
01-linuxplus-lesson-history-file.el
Normal file
7
01-linuxplus-lesson-history-file.el
Normal file
@@ -0,0 +1,7 @@
|
||||
;;; 01-linuxplus-lesson-history-file.el --- linuxplus-lesson-history-file
|
||||
|
||||
(defvar linuxplus-lesson-history-file
|
||||
(expand-file-name "~/.emacs.d/linuxplus-lesson-history.el")
|
||||
"File used to save `linuxplus-lesson-history` across Emacs sessions.")
|
||||
|
||||
;;; 01-linuxplus-lesson-history-file.el ends here
|
||||
10
02-linuxplus-save-lesson-history.el
Normal file
10
02-linuxplus-save-lesson-history.el
Normal file
@@ -0,0 +1,10 @@
|
||||
;;; 02-linuxplus-save-lesson-history.el --- linuxplus-save-lesson-history
|
||||
|
||||
(defun linuxplus-save-lesson-history ()
|
||||
"Save lesson history to disk."
|
||||
(with-temp-file linuxplus-lesson-history-file
|
||||
(prin1 `(setq linuxplus-lesson-history ',linuxplus-lesson-history)
|
||||
(current-buffer))
|
||||
(insert "\n")))
|
||||
|
||||
;;; 02-linuxplus-save-lesson-history.el ends here
|
||||
8
03-linuxplus-load-lesson-history.el
Normal file
8
03-linuxplus-load-lesson-history.el
Normal file
@@ -0,0 +1,8 @@
|
||||
;;; 03-linuxplus-load-lesson-history.el --- linuxplus-load-lesson-history
|
||||
|
||||
(defun linuxplus-load-lesson-history ()
|
||||
"Load lesson history from disk."
|
||||
(when (file-exists-p linuxplus-lesson-history-file)
|
||||
(load-file linuxplus-lesson-history-file)))
|
||||
|
||||
;;; 03-linuxplus-load-lesson-history.el ends here
|
||||
12
04-linuxplus-record-score.el
Normal file
12
04-linuxplus-record-score.el
Normal file
@@ -0,0 +1,12 @@
|
||||
;;; 04-linuxplus-record-score.el --- linuxplus-record-score
|
||||
|
||||
(defun linuxplus-record-score (lesson score)
|
||||
"Record and persist lesson SCORE."
|
||||
(let ((entry (assoc lesson linuxplus-lesson-history)))
|
||||
(if entry
|
||||
(setcdr entry score)
|
||||
(push (cons lesson score) linuxplus-lesson-history)))
|
||||
(linuxplus-save-lesson-history)
|
||||
(message "Recorded %s score: %s" lesson score))
|
||||
|
||||
;;; 04-linuxplus-record-score.el ends here
|
||||
16
05-linuxplus-show-scores.el
Normal file
16
05-linuxplus-show-scores.el
Normal file
@@ -0,0 +1,16 @@
|
||||
;;; 05-linuxplus-show-scores.el --- linuxplus-show-scores
|
||||
|
||||
(defun linuxplus-show-scores ()
|
||||
"Display saved Linux+ lesson scores."
|
||||
(interactive)
|
||||
(with-current-buffer (get-buffer-create "*Linux+ Scores*")
|
||||
(read-only-mode -1)
|
||||
(erase-buffer)
|
||||
(insert "Linux+ Lesson Scores\n====================\n\n")
|
||||
(dolist (entry linuxplus-lesson-history)
|
||||
(insert (format "%s: %s\n" (car entry) (cdr entry))))
|
||||
(goto-char (point-min))
|
||||
(view-mode 1)
|
||||
(pop-to-buffer (current-buffer))))
|
||||
|
||||
;;; 05-linuxplus-show-scores.el ends here
|
||||
156
06-linuxplus-quiz-mode.el
Normal file
156
06-linuxplus-quiz-mode.el
Normal file
@@ -0,0 +1,156 @@
|
||||
;;; 06-linuxplus-quiz-mode.el --- linuxplus-quiz-mode
|
||||
|
||||
(defvar-local linuxplus-quiz-questions nil
|
||||
"Questions for the current Linux+ quiz buffer.")
|
||||
|
||||
(defvar-local linuxplus-quiz-answers nil
|
||||
"Correct answers for the current Linux+ quiz buffer.")
|
||||
|
||||
(defvar-local linuxplus-quiz-user-answers nil
|
||||
"User answers for the current Linux+ quiz buffer.")
|
||||
|
||||
(defvar-local linuxplus-quiz-current-index 0
|
||||
"Current question index in the Linux+ quiz buffer.")
|
||||
|
||||
(defvar-local linuxplus-quiz-lesson-name nil
|
||||
"Lesson name associated with the current Linux+ quiz buffer.")
|
||||
|
||||
(defun linuxplus-quiz-answer-marker (answer)
|
||||
"Return a display marker string for ANSWER."
|
||||
(cond
|
||||
((eq answer 'y) "[y]")
|
||||
((eq answer 'n) "[n]")
|
||||
(t "[ ]")))
|
||||
|
||||
(defun linuxplus-quiz-answer-string (answer)
|
||||
"Return a readable string for ANSWER."
|
||||
(cond
|
||||
((eq answer 'y) "y")
|
||||
((eq answer 'n) "n")
|
||||
(t "unanswered")))
|
||||
|
||||
(defun linuxplus-quiz-result-string (user-answer correct-answer)
|
||||
"Return a readable result string comparing USER-ANSWER to CORRECT-ANSWER."
|
||||
(if (eq user-answer correct-answer)
|
||||
"correct"
|
||||
"incorrect"))
|
||||
|
||||
(defun linuxplus-quiz-render ()
|
||||
"Render the current Linux+ quiz buffer."
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer)
|
||||
(insert "Linux+ Quiz\n")
|
||||
(insert "===========\n\n")
|
||||
(insert "y = yes, n = no, b = back, f = forward, x = finish, q = quit\n\n")
|
||||
(dotimes (i (length linuxplus-quiz-questions))
|
||||
(let ((prefix (if (= i linuxplus-quiz-current-index) ">" " "))
|
||||
(marker (linuxplus-quiz-answer-marker
|
||||
(nth i linuxplus-quiz-user-answers)))
|
||||
(question (nth i linuxplus-quiz-questions)))
|
||||
(insert (format "%s %s %d. %s\n"
|
||||
prefix marker (1+ i) question)))))
|
||||
(goto-char (point-min)))
|
||||
|
||||
(defun linuxplus-quiz-finish ()
|
||||
"Finish the current quiz, record the score, and show detailed results."
|
||||
(interactive)
|
||||
(let ((score 0)
|
||||
(total (length linuxplus-quiz-questions)))
|
||||
(dotimes (i total)
|
||||
(when (eq (nth i linuxplus-quiz-user-answers)
|
||||
(nth i linuxplus-quiz-answers))
|
||||
(setq score (1+ score))))
|
||||
(linuxplus-record-score linuxplus-quiz-lesson-name score)
|
||||
(let ((inhibit-read-only t))
|
||||
(erase-buffer)
|
||||
(insert "Linux+ Quiz Results\n")
|
||||
(insert "===================\n\n")
|
||||
(insert (format "Lesson: %s\n" linuxplus-quiz-lesson-name))
|
||||
(insert (format "Score: %d/%d\n\n" score total))
|
||||
(insert "Detailed review:\n\n")
|
||||
(dotimes (i total)
|
||||
(let ((question (nth i linuxplus-quiz-questions))
|
||||
(user-answer (nth i linuxplus-quiz-user-answers))
|
||||
(correct-answer (nth i linuxplus-quiz-answers)))
|
||||
(insert (format "%d. %s\n" (1+ i) question))
|
||||
(insert (format " Your answer: %s\n"
|
||||
(linuxplus-quiz-answer-string user-answer)))
|
||||
(insert (format " Correct answer: %s\n"
|
||||
(linuxplus-quiz-answer-string correct-answer)))
|
||||
(insert (format " Result: %s\n\n"
|
||||
(linuxplus-quiz-result-string
|
||||
user-answer correct-answer))))))
|
||||
(goto-char (point-min))
|
||||
(view-mode 1)
|
||||
(message "Quiz complete. Score: %d/%d" score total)))
|
||||
|
||||
(defun linuxplus-quiz-answer-y ()
|
||||
"Answer yes to the current Linux+ quiz question."
|
||||
(interactive)
|
||||
(setf (nth linuxplus-quiz-current-index linuxplus-quiz-user-answers) 'y)
|
||||
(if (< linuxplus-quiz-current-index
|
||||
(1- (length linuxplus-quiz-questions)))
|
||||
(setq linuxplus-quiz-current-index
|
||||
(1+ linuxplus-quiz-current-index))
|
||||
(linuxplus-quiz-render))
|
||||
(when (derived-mode-p 'linuxplus-quiz-mode)
|
||||
(linuxplus-quiz-render)))
|
||||
|
||||
(defun linuxplus-quiz-answer-n ()
|
||||
"Answer no to the current Linux+ quiz question."
|
||||
(interactive)
|
||||
(setf (nth linuxplus-quiz-current-index linuxplus-quiz-user-answers) 'n)
|
||||
(if (< linuxplus-quiz-current-index
|
||||
(1- (length linuxplus-quiz-questions)))
|
||||
(setq linuxplus-quiz-current-index
|
||||
(1+ linuxplus-quiz-current-index))
|
||||
(linuxplus-quiz-render))
|
||||
(when (derived-mode-p 'linuxplus-quiz-mode)
|
||||
(linuxplus-quiz-render)))
|
||||
|
||||
(defun linuxplus-quiz-next ()
|
||||
"Move to next question without changing answer."
|
||||
(interactive)
|
||||
(when (< linuxplus-quiz-current-index
|
||||
(1- (length linuxplus-quiz-questions)))
|
||||
(setq linuxplus-quiz-current-index
|
||||
(1+ linuxplus-quiz-current-index))
|
||||
(linuxplus-quiz-render)))
|
||||
|
||||
(defun linuxplus-quiz-prev ()
|
||||
"Move to previous question."
|
||||
(interactive)
|
||||
(when (> linuxplus-quiz-current-index 0)
|
||||
(setq linuxplus-quiz-current-index
|
||||
(1- linuxplus-quiz-current-index))
|
||||
(linuxplus-quiz-render)))
|
||||
|
||||
(defvar linuxplus-quiz-mode-map
|
||||
(let ((map (make-sparse-keymap)))
|
||||
(define-key map (kbd "y") #'linuxplus-quiz-answer-y)
|
||||
(define-key map (kbd "n") #'linuxplus-quiz-answer-n)
|
||||
(define-key map (kbd "f") #'linuxplus-quiz-next)
|
||||
(define-key map (kbd "b") #'linuxplus-quiz-prev)
|
||||
(define-key map (kbd "x") #'linuxplus-quiz-finish)
|
||||
(define-key map (kbd "q") #'quit-window)
|
||||
map)
|
||||
"Keymap for `linuxplus-quiz-mode`.")
|
||||
|
||||
(define-derived-mode linuxplus-quiz-mode special-mode "LinuxPlus-Quiz"
|
||||
"Major mode for Linux+ quiz buffers."
|
||||
(setq buffer-read-only t))
|
||||
|
||||
(defun linuxplus-start-quiz (lesson questions answers buffer)
|
||||
"Start a Linux+ quiz for LESSON using QUESTIONS and ANSWERS in BUFFER."
|
||||
(let ((buf (get-buffer-create buffer)))
|
||||
(with-current-buffer buf
|
||||
(linuxplus-quiz-mode)
|
||||
(setq linuxplus-quiz-lesson-name lesson)
|
||||
(setq linuxplus-quiz-questions questions)
|
||||
(setq linuxplus-quiz-answers answers)
|
||||
(setq linuxplus-quiz-user-answers (make-list (length questions) nil))
|
||||
(setq linuxplus-quiz-current-index 0)
|
||||
(linuxplus-quiz-render))
|
||||
(pop-to-buffer buf)))
|
||||
|
||||
;;; 06-linuxplus-quiz-mode.el ends here
|
||||
60
10-ssh-service-lesson.el
Normal file
60
10-ssh-service-lesson.el
Normal file
@@ -0,0 +1,60 @@
|
||||
;;; 10-ssh-service-lesson.el --- ssh-service-lesson
|
||||
|
||||
(defun ssh-service-lesson ()
|
||||
"Hands-on Linux+ lesson for managing the SSH service with systemctl."
|
||||
(interactive)
|
||||
(delete-other-windows)
|
||||
(let ((lesson-buffer (get-buffer-create "*SSH Service Lesson*"))
|
||||
(shell (or shell-file-name (getenv "SHELL") "/bin/sh")))
|
||||
(switch-to-buffer lesson-buffer)
|
||||
(read-only-mode -1)
|
||||
(erase-buffer)
|
||||
(insert
|
||||
"Linux+ Lesson: SSH Service Management\n"
|
||||
"====================================\n\n"
|
||||
"Concept:\n"
|
||||
"Learn the difference between starting/stopping a service now and\n"
|
||||
"enabling/disabling it for boot.\n\n"
|
||||
"VM requirement:\n"
|
||||
"- Use a systemd-based Linux VM.\n"
|
||||
"- Debian/Ubuntu usually use service name: ssh\n"
|
||||
"- Fedora/Rocky/Alma/RHEL usually use service name: sshd\n\n"
|
||||
"In the terminal below, determine whether your VM uses ssh or sshd:\n"
|
||||
" systemctl status ssh\n"
|
||||
" systemctl status sshd\n\n"
|
||||
"Use the correct service name for the rest of the lesson.\n\n"
|
||||
"Hands-on tasks:\n"
|
||||
"A. Check status:\n"
|
||||
" sudo systemctl status <service>\n\n"
|
||||
"B. Stop the service:\n"
|
||||
" sudo systemctl stop <service>\n"
|
||||
" Then check status again.\n\n"
|
||||
"C. Start the service:\n"
|
||||
" sudo systemctl start <service>\n"
|
||||
" Then check status again.\n\n"
|
||||
"D. Disable the service:\n"
|
||||
" sudo systemctl disable <service>\n\n"
|
||||
"E. Re-enable the service:\n"
|
||||
" sudo systemctl enable <service>\n\n"
|
||||
"What to notice:\n"
|
||||
"- start = runs now\n"
|
||||
"- stop = stops now\n"
|
||||
"- enable = starts at boot\n"
|
||||
"- disable = does not start at boot\n\n"
|
||||
"Important Linux+ trap:\n"
|
||||
"A service can be enabled but not currently running.\n"
|
||||
"If so, use:\n"
|
||||
" systemctl start <service>\n\n"
|
||||
"When you finish the hands-on portion, run:\n"
|
||||
" M-x ssh-service-quiz\n")
|
||||
(goto-char (point-min))
|
||||
(view-mode 1)
|
||||
(split-window-below)
|
||||
(other-window 1)
|
||||
(let ((term-buffer (get-buffer "*term*")))
|
||||
(if (buffer-live-p term-buffer)
|
||||
(switch-to-buffer term-buffer)
|
||||
(ansi-term shell "term")))
|
||||
(other-window -1)))
|
||||
|
||||
;;; 10-ssh-service-lesson.el ends here
|
||||
15
11-ssh-service-quiz.el
Normal file
15
11-ssh-service-quiz.el
Normal file
@@ -0,0 +1,15 @@
|
||||
;;; 11-ssh-service-quiz.el --- ssh-service-quiz
|
||||
|
||||
(defun ssh-service-quiz ()
|
||||
(interactive)
|
||||
(linuxplus-start-quiz
|
||||
'ssh-service-lesson
|
||||
'("Does start run now?"
|
||||
"Does enable run at boot?"
|
||||
"Does disable stop running service?"
|
||||
"Use start if enabled but stopped?"
|
||||
"Are enable and start the same?")
|
||||
'(y y n y n)
|
||||
"*SSH Quiz*"))
|
||||
|
||||
;;; 11-ssh-service-quiz.el ends here
|
||||
14
20-top-cpu-fields-lesson.el
Normal file
14
20-top-cpu-fields-lesson.el
Normal file
@@ -0,0 +1,14 @@
|
||||
;;; 20-top-cpu-fields-lesson.el --- top-cpu-fields-lesson
|
||||
|
||||
(defun top-cpu-fields-lesson ()
|
||||
(interactive)
|
||||
(delete-other-windows)
|
||||
(switch-to-buffer "*Top Lesson*")
|
||||
(erase-buffer)
|
||||
(insert "Use 'top' and observe CPU fields.\n")
|
||||
(split-window-below)
|
||||
(other-window 1)
|
||||
(ansi-term (getenv "SHELL") "term")
|
||||
(other-window -1))
|
||||
|
||||
;;; 20-top-cpu-fields-lesson.el ends here
|
||||
15
21-top-cpu-fields-quiz.el
Normal file
15
21-top-cpu-fields-quiz.el
Normal file
@@ -0,0 +1,15 @@
|
||||
;;; 21-top-cpu-fields-quiz.el --- top-cpu-fields-quiz
|
||||
|
||||
(defun top-cpu-fields-quiz ()
|
||||
(interactive)
|
||||
(linuxplus-start-quiz
|
||||
'top-cpu-fields-lesson
|
||||
'("Does wa indicate disk?"
|
||||
"Does st indicate VM?"
|
||||
"Does id mean idle?"
|
||||
"Does us mean user?"
|
||||
"Does sy mean system?")
|
||||
'(y y y y y)
|
||||
"*Top Quiz*"))
|
||||
|
||||
;;; 21-top-cpu-fields-quiz.el ends here
|
||||
16
linuxplus-load-all.el
Normal file
16
linuxplus-load-all.el
Normal file
@@ -0,0 +1,16 @@
|
||||
;;; linuxplus-load-all.el --- linuxplus-load-all
|
||||
|
||||
(require 'seq)
|
||||
|
||||
(defvar linuxplus-base-directory
|
||||
(file-name-directory (or load-file-name buffer-file-name)))
|
||||
|
||||
(defun linuxplus-load-all ()
|
||||
(interactive)
|
||||
(dolist (f (sort (directory-files linuxplus-base-directory t "^[0-9].*\\.el$")
|
||||
#'string-lessp))
|
||||
(load-file f))
|
||||
(linuxplus-load-lesson-history)
|
||||
(message "Linux+ loaded."))
|
||||
|
||||
;;; linuxplus-load-all.el ends here
|
||||
Reference in New Issue
Block a user