mode-line-net.lisp 6.0 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173
  1. ;;; mode-line-net.lisp --- Network info for the mode line
  2. ;; Copyright © 2009 Vitaly Mayatskikh
  3. ;; Copyright © 2019 Alex Kost <alezost@gmail.com>
  4. ;; This program is free software; you can redistribute it and/or modify
  5. ;; it under the terms of the GNU General Public License as published by
  6. ;; the Free Software Foundation, either version 3 of the License, or
  7. ;; (at your option) any later version.
  8. ;;
  9. ;; This program is distributed in the hope that it will be useful,
  10. ;; but WITHOUT ANY WARRANTY; without even the implied warranty of
  11. ;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  12. ;; GNU General Public License for more details.
  13. ;;
  14. ;; You should have received a copy of the GNU General Public License
  15. ;; along with this program. If not, see <http://www.gnu.org/licenses/>.
  16. ;;; Commentary:
  17. ;; This file originates from
  18. ;; <https://github.com/stumpwm/stumpwm-contrib/blob/master/modeline/net>.
  19. ;; I do not like some things that module does, also I wanted to make a
  20. ;; more sophisticated formatting, so I adjusted it for my needs.
  21. ;; Meaning of "/sys/class/net/*" and "/sys/class/rfkill/rfkillN/*" files
  22. ;; can be found at:
  23. ;; <https://www.kernel.org/doc/Documentation/ABI/testing/sysfs-class-net>,
  24. ;; <https://www.kernel.org/doc/Documentation/ABI/stable/sysfs-class-rfkill>.
  25. ;;; Code:
  26. (defpackage #:al/stumpwm-net
  27. (:use :common-lisp
  28. :stumpwm)
  29. (:export #:net-mode-line-string))
  30. (in-package #:al/stumpwm-net)
  31. (defvar *net-devices*
  32. (mapcar (lambda (dir)
  33. ;; Is there a better way to do this?
  34. (first (last (pathname-directory dir))))
  35. (uiop:subdirectories "/sys/class/net/"))
  36. "List of available network devices (interfaces).")
  37. (defvar *net-device*
  38. ;; At first, search for "wlp*" (wlan), then for "enp*" (eth).
  39. (or (find-if (lambda (name) (ppcre:scan "^w" name))
  40. *net-devices*)
  41. (find-if (lambda (name) (ppcre:scan "^e" name))
  42. *net-devices*))
  43. "Currently used network device.")
  44. (defun net-device-file-name (&optional (device *net-device*))
  45. "Return sysfs file name of the DEVICE."
  46. (concat "/sys/class/net/" device))
  47. (defun net-device-parameter (file-name &key (device *net-device*)
  48. to-number)
  49. "Return a line (string) from '/sys/class/net/DEVICE/FILE-NAME'.
  50. If DEVICE is nil, use `*net-device*'.
  51. If TO-NUMBER is non-nil, convert this string into a number.
  52. Return nil in case of any error."
  53. (al/read-sys-file
  54. (concat (net-device-file-name device) "/" file-name)
  55. to-number))
  56. (defvar *net-rfkill-dirs* nil
  57. "Alist of (DEVICE . RFKILL-DIR) pairs.")
  58. (defun net-rfkill-dir (device)
  59. "Return the sysfs rfkill directory for the network DEVICE."
  60. (let ((assoc (assoc device *net-rfkill-dirs*)))
  61. (if assoc
  62. (cdr assoc)
  63. (let ((dir (car (directory (concat (net-device-file-name device)
  64. "/phy*/rfkill*")))))
  65. (push (cons device dir) *net-rfkill-dirs*)
  66. dir))))
  67. (defun net-rfkill-state (&optional (device *net-device*))
  68. "Return the current rfkill state of the network DEVICE.
  69. If the interface is blocked, return `:hard' or `:soft'.
  70. Otherwise, return nil."
  71. (let ((dir (net-rfkill-dir device)))
  72. (defun blocked? (type)
  73. (not (zerop (al/read-sys-file
  74. (merge-pathnames dir type) t))))
  75. (and dir
  76. (or (and (blocked? "hard") :hard)
  77. (and (blocked? "soft") :soft)))))
  78. (defvar *last-rx* 0)
  79. (defvar *last-tx* 0)
  80. (defvar *last-time* 0)
  81. (defun net-state (&optional (device *net-device*))
  82. "Return values for the current state of the network DEVICE.
  83. If the interface is 'rfkill'-ed, return `:soft' or `:hard'.
  84. If the interface is down, return `:down'.
  85. If the interface is up, return `:up download-speed upload-speed' values.
  86. Otherwise, return `:unknown' value."
  87. (or (net-rfkill-state device)
  88. (let ((state (net-device-parameter "operstate"
  89. :device device)))
  90. (cond
  91. ((string= state "down")
  92. :down)
  93. ((string= state "up")
  94. (let* ((now (/ (get-internal-real-time)
  95. internal-time-units-per-second))
  96. (rx (net-device-parameter "statistics/rx_bytes"
  97. :device device
  98. :to-number t))
  99. (tx (net-device-parameter "statistics/tx_bytes"
  100. :device device
  101. :to-number t))
  102. (dt (- now *last-time*))
  103. (drx (- rx *last-rx*))
  104. (dtx (- tx *last-tx*)))
  105. (setq *last-rx* rx
  106. *last-tx* tx
  107. *last-time* now)
  108. (values :up
  109. (round (/ drx dt))
  110. (round (/ dtx dt)))))
  111. (t :unknown)))))
  112. (defun format-float (num)
  113. "Return formatted string from the floating number NUM."
  114. (cond
  115. ((>= 10 num)
  116. (format nil "~4,2F" num))
  117. ((>= 100 num)
  118. (format nil "~4,1F" num))
  119. (t
  120. (format nil "~4D" (round num)))))
  121. (defun format-bytes (bytes)
  122. "Return formatted string from the number of BYTES."
  123. (if (numberp bytes)
  124. (let ((mb (/ bytes 1e6)))
  125. (if (> mb 1)
  126. (concat (format-float mb) "^[^2M^]")
  127. (concat (format-float (/ bytes 1e3)) "^[^nk^]")))
  128. ""))
  129. (defun net-mode-line-string ()
  130. "Return a string with NET info suitable for the mode-line."
  131. (multiple-value-bind (state down up)
  132. (net-state *net-device*)
  133. (let ((fmt-device (ecase state
  134. (:up "^b^6*~A")
  135. (:down "^B^5*~A")
  136. (:soft "^b^7*~A")
  137. (:hard "~A")
  138. (:unknown "^B^1*~A"))))
  139. (concat "^["
  140. (if (and down up)
  141. (format nil (concat fmt-device "^7 ~A ~A")
  142. *net-device*
  143. (format-bytes down)
  144. (format-bytes up))
  145. (format nil fmt-device *net-device*))
  146. "^]"))))
  147. ;;; mode-line-net.lisp ends here