admin.scm 21 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2016 Jan Nieuwenhuizen <janneke@gnu.org>
  3. ;;; Copyright © 2016-2023 Ludovic Courtès <ludo@gnu.org>
  4. ;;; Copyright © 2020 Brice Waegeneire <brice@waegenei.re>
  5. ;;; Copyright © 2023 Giacomo Leidi <goodoldpaul@autistici.org>
  6. ;;;
  7. ;;; This file is part of GNU Guix.
  8. ;;;
  9. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  10. ;;; under the terms of the GNU General Public License as published by
  11. ;;; the Free Software Foundation; either version 3 of the License, or (at
  12. ;;; your option) any later version.
  13. ;;;
  14. ;;; GNU Guix is distributed in the hope that it will be useful, but
  15. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;;; GNU General Public License for more details.
  18. ;;;
  19. ;;; You should have received a copy of the GNU General Public License
  20. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  21. (define-module (gnu services admin)
  22. #:use-module (gnu packages admin)
  23. #:use-module ((gnu packages base)
  24. #:select (canonical-package findutils coreutils sed))
  25. #:use-module (gnu packages certs)
  26. #:use-module (gnu packages package-management)
  27. #:use-module (gnu services)
  28. #:use-module (gnu services configuration)
  29. #:use-module (gnu services mcron)
  30. #:use-module (gnu services shepherd)
  31. #:use-module (gnu system accounts)
  32. #:use-module ((gnu system shadow) #:select (account-service-type))
  33. #:use-module ((guix store) #:select (%store-prefix))
  34. #:use-module (guix gexp)
  35. #:use-module (guix modules)
  36. #:use-module (guix packages)
  37. #:use-module (guix records)
  38. #:use-module (srfi srfi-1)
  39. #:use-module (ice-9 match)
  40. #:use-module (ice-9 vlist)
  41. #:export (%default-rotations
  42. %rotated-files
  43. log-rotation
  44. log-rotation?
  45. log-rotation-frequency
  46. log-rotation-files
  47. log-rotation-options
  48. log-rotation-post-rotate
  49. %default-log-rotation-options
  50. rottlog-configuration
  51. rottlog-configuration?
  52. rottlog-service
  53. rottlog-service-type
  54. log-cleanup-service-type
  55. log-cleanup-configuration
  56. log-cleanup-configuration?
  57. log-cleanup-configuration-directory
  58. log-cleanup-configuration-expiry
  59. log-cleanup-configuration-schedule
  60. file-database-service-type
  61. file-database-configuration
  62. file-database-configuration?
  63. file-database-configuration-package
  64. file-database-configuration-schedule
  65. file-database-configuration-excluded-directories
  66. %default-file-database-update-schedule
  67. %default-file-database-excluded-directories
  68. package-database-service-type
  69. package-database-configuration
  70. package-database-configuration?
  71. package-database-configuration-package
  72. package-database-configuration-schedule
  73. package-database-configuration-method
  74. package-database-configuration-channels
  75. unattended-upgrade-service-type
  76. unattended-upgrade-configuration
  77. unattended-upgrade-configuration?
  78. unattended-upgrade-configuration-operating-system-file
  79. unattended-upgrade-configuration-operating-system-expression
  80. unattended-upgrade-configuration-channels
  81. unattended-upgrade-configuration-schedule
  82. unattended-upgrade-configuration-services-to-restart
  83. unattended-upgrade-configuration-system-expiration
  84. unattended-upgrade-configuration-maximum-duration
  85. unattended-upgrade-configuration-log-file))
  86. ;;; Commentary:
  87. ;;;
  88. ;;; This module implements configuration of rottlog by writing
  89. ;;; /etc/rottlog/{rc,hourly|daily|weekly}. Example usage
  90. ;;;
  91. ;;; (mcron-service)
  92. ;;; (service rottlog-service-type)
  93. ;;;
  94. ;;; Code:
  95. (define-record-type* <log-rotation> log-rotation make-log-rotation
  96. log-rotation?
  97. (files log-rotation-files) ;list of strings
  98. (frequency log-rotation-frequency ;symbol
  99. (default 'weekly))
  100. (post-rotate log-rotation-post-rotate ;#f | gexp
  101. (default #f))
  102. (options log-rotation-options ;list of strings
  103. (default %default-log-rotation-options)))
  104. (define %default-log-rotation-options
  105. ;; Default log rotation options: append ".gz" to file names.
  106. '("storefile @FILENAME.@COMP_EXT"
  107. "notifempty"))
  108. (define %rotated-files
  109. ;; Syslog files subject to rotation.
  110. '("/var/log/messages" "/var/log/secure" "/var/log/debug"
  111. "/var/log/maillog" "/var/log/mcron.log"))
  112. (define %default-rotations
  113. (list (log-rotation ;syslog files
  114. (files %rotated-files)
  115. (frequency 'weekly)
  116. (options `(;; These files are worth keeping for a few weeks.
  117. "rotate 16"
  118. ;; Run post-rotate once per rotation
  119. "sharedscripts"
  120. ,@%default-log-rotation-options))
  121. ;; Restart syslogd after rotation.
  122. (post-rotate #~(let ((pid (call-with-input-file "/var/run/syslog.pid"
  123. read)))
  124. (kill pid SIGHUP))))
  125. (log-rotation
  126. (files '("/var/log/guix-daemon.log"))
  127. (options `("rotate 4" ;don't keep too many of them
  128. ,@%default-log-rotation-options)))))
  129. (define (log-rotation->config rotation)
  130. "Return a string-valued gexp representing the rottlog configuration snippet
  131. for ROTATION."
  132. (define post-rotate
  133. (let ((post (log-rotation-post-rotate rotation)))
  134. (and post
  135. (program-file "rottlog-post-rotate.scm" post))))
  136. #~(let ((post #$post-rotate))
  137. (string-append (string-join '#$(log-rotation-files rotation) ",")
  138. " {"
  139. #$(string-join (log-rotation-options rotation)
  140. "\n " 'prefix)
  141. (if post
  142. (string-append "\n postrotate\n " post
  143. "\n endscript\n")
  144. "")
  145. "\n}\n")))
  146. (define (log-rotations->/etc-entries rotations)
  147. "Return the list of /etc entries for ROTATIONS, a list of <log-rotation>."
  148. (define (frequency-file frequency rotations)
  149. (computed-file (string-append "rottlog." (symbol->string frequency))
  150. #~(call-with-output-file #$output
  151. (lambda (port)
  152. (for-each (lambda (str)
  153. (display str port))
  154. (list #$@(map log-rotation->config
  155. rotations)))))))
  156. (let* ((frequencies (delete-duplicates
  157. (map log-rotation-frequency rotations)))
  158. (table (fold (lambda (rotation table)
  159. (vhash-consq (log-rotation-frequency rotation)
  160. rotation table))
  161. vlist-null
  162. rotations)))
  163. (map (lambda (frequency)
  164. `(,(symbol->string frequency)
  165. ,(frequency-file frequency
  166. (vhash-foldq* cons '() frequency table))))
  167. frequencies)))
  168. (define (default-jobs rottlog)
  169. (list #~(job '(next-hour '(0)) ;midnight
  170. #$(file-append rottlog "/sbin/rottlog"))
  171. #~(job '(next-hour '(12)) ;noon
  172. #$(file-append rottlog "/sbin/rottlog"))))
  173. (define-record-type* <rottlog-configuration>
  174. rottlog-configuration make-rottlog-configuration
  175. rottlog-configuration?
  176. (rottlog rottlog-rottlog ;file-like
  177. (default rottlog))
  178. (rc-file rottlog-rc-file ;file-like
  179. (default (file-append rottlog "/etc/rc")))
  180. (rotations rottlog-rotations ;list of <log-rotation>
  181. (default %default-rotations))
  182. (jobs rottlog-jobs ;list of <mcron-job>
  183. (default #f)))
  184. (define (rottlog-etc config)
  185. `(("rottlog"
  186. ,(file-union "rottlog"
  187. (cons `("rc" ,(rottlog-rc-file config))
  188. (log-rotations->/etc-entries
  189. (rottlog-rotations config)))))))
  190. (define (rottlog-jobs-or-default config)
  191. (or (rottlog-jobs config)
  192. (default-jobs (rottlog-rottlog config))))
  193. (define rottlog-service-type
  194. (service-type
  195. (name 'rottlog)
  196. (description
  197. "Periodically rotate log files using GNU@tie{}Rottlog and GNU@tie{}mcron.
  198. Old log files are removed or compressed according to the configuration.")
  199. (extensions (list (service-extension etc-service-type rottlog-etc)
  200. (service-extension mcron-service-type
  201. rottlog-jobs-or-default)
  202. ;; Add Rottlog to the global profile so users can access
  203. ;; the documentation.
  204. (service-extension profile-service-type
  205. (compose list rottlog-rottlog))))
  206. (compose concatenate)
  207. (extend (lambda (config rotations)
  208. (rottlog-configuration
  209. (inherit config)
  210. (rotations (append (rottlog-rotations config)
  211. rotations)))))
  212. (default-value (rottlog-configuration))))
  213. ;;;
  214. ;;; Build log removal.
  215. ;;;
  216. (define-record-type* <log-cleanup-configuration>
  217. log-cleanup-configuration make-log-cleanup-configuration
  218. log-cleanup-configuration?
  219. (directory log-cleanup-configuration-directory) ;string
  220. (expiry log-cleanup-configuration-expiry ;integer (seconds)
  221. (default (* 6 30 24 3600)))
  222. (schedule log-cleanup-configuration-schedule ;string or gexp
  223. (default "30 12 01,08,15,22 * *")))
  224. (define (log-cleanup-program directory expiry)
  225. (program-file "delete-old-logs"
  226. (with-imported-modules '((guix build utils))
  227. #~(begin
  228. (use-modules (guix build utils))
  229. (let* ((now (car (gettimeofday)))
  230. (logs (find-files #$directory
  231. (lambda (file stat)
  232. (> (- now (stat:mtime stat))
  233. #$expiry)))))
  234. (format #t "deleting ~a log files from '~a'...~%"
  235. (length logs) #$directory)
  236. (for-each delete-file logs))))))
  237. (define (log-cleanup-mcron-jobs configuration)
  238. (match-record configuration <log-cleanup-configuration>
  239. (directory expiry schedule)
  240. (list #~(job #$schedule
  241. #$(log-cleanup-program directory expiry)))))
  242. (define log-cleanup-service-type
  243. (service-type
  244. (name 'log-cleanup)
  245. (extensions
  246. (list (service-extension mcron-service-type
  247. log-cleanup-mcron-jobs)))
  248. (description
  249. "Periodically delete old log files.")))
  250. ;;;
  251. ;;; File databases.
  252. ;;;
  253. (define %default-file-database-update-schedule
  254. ;; Default mcron schedule for the periodic 'updatedb' job: once every
  255. ;; Sunday.
  256. "10 23 * * 0")
  257. (define %default-file-database-excluded-directories
  258. ;; Regexps of directories excluded from the 'locate' database.
  259. (list (%store-prefix)
  260. "/tmp" "/var/tmp" "/var/cache" ".*/\\.cache"
  261. "/run/udev"))
  262. (define (string-or-gexp? obj)
  263. (or (string? obj) (gexp? obj)))
  264. (define string-list?
  265. (match-lambda
  266. (((? string?) ...) #t)
  267. (_ #f)))
  268. (define-configuration/no-serialization file-database-configuration
  269. (package
  270. (file-like (let-system (system target)
  271. ;; Unless we're cross-compiling, avoid pulling a second copy
  272. ;; of findutils.
  273. (if target
  274. findutils
  275. (canonical-package findutils))))
  276. "The GNU@tie{}Findutils package from which the @command{updatedb} command
  277. is taken.")
  278. (schedule
  279. (string-or-gexp %default-file-database-update-schedule)
  280. "String or G-exp denoting an mcron schedule for the periodic
  281. @command{updatedb} job (@pxref{Guile Syntax,,, mcron, GNU@tie{}mcron}).")
  282. (excluded-directories
  283. (string-list %default-file-database-excluded-directories)
  284. "List of regular expressions of directories to ignore when building the
  285. file database. By default, this includes @file{/tmp} and @file{/gnu/store};
  286. the latter should instead be indexed by @command{guix locate} (@pxref{Invoking
  287. guix locate}). This list is passed to the @option{--prunepaths} option of
  288. @command{updatedb} (@pxref{Invoking updatedb,,, find, GNU@tie{}Findutils})."))
  289. (define (file-database-mcron-jobs configuration)
  290. (match-record configuration <file-database-configuration>
  291. (package schedule excluded-directories)
  292. (let ((updatedb (program-file
  293. "updatedb"
  294. #~(begin
  295. ;; 'updatedb' is a shell script that expects various
  296. ;; commands in $PATH.
  297. (setenv "PATH"
  298. (string-append #$package "/bin:"
  299. #$(canonical-package coreutils)
  300. "/bin:"
  301. #$(canonical-package sed)
  302. "/bin"))
  303. (execl #$(file-append package "/bin/updatedb")
  304. "updatedb"
  305. #$(string-append "--prunepaths="
  306. (string-join
  307. excluded-directories)))))))
  308. (list #~(job #$schedule #$updatedb)))))
  309. (define file-database-service-type
  310. (service-type
  311. (name 'file-database)
  312. (extensions (list (service-extension mcron-service-type
  313. file-database-mcron-jobs)))
  314. (description
  315. "Periodically update the file database used by the @command{locate} command,
  316. which lets you search for files by name. The database is created by running
  317. the @command{updatedb} command.")
  318. (default-value (file-database-configuration))))
  319. (define %default-package-database-update-schedule
  320. ;; Default mcron schedule for the periodic 'guix locate --update' job: once
  321. ;; every Monday.
  322. "10 23 * * 1")
  323. (define-configuration/no-serialization package-database-configuration
  324. (package (file-like guix)
  325. "The Guix package to use.")
  326. (schedule (string-or-gexp
  327. %default-package-database-update-schedule)
  328. "String or G-exp denoting an mcron schedule for the periodic
  329. @command{guix locate --update} job (@pxref{Guile Syntax,,, mcron,
  330. GNU@tie{}mcron}).")
  331. (method (symbol 'store)
  332. "Indexing method for @command{guix locate}. The default value,
  333. @code{'store}, yields a more complete database but is relatively expensive in
  334. terms of CPU and input/output.")
  335. (channels (gexp #~%default-channels)
  336. "G-exp denoting the channels to use when updating the database
  337. (@pxref{Channels})."))
  338. (define (package-database-mcron-jobs configuration)
  339. (match-record configuration <package-database-configuration>
  340. (package schedule method channels)
  341. (let ((channels (scheme-file "channels.scm" channels)))
  342. (list #~(job #$schedule
  343. ;; XXX: The whole thing's running as "root" just because it
  344. ;; needs write access to /var/cache/guix/locate.
  345. (string-append #$(file-append package "/bin/guix")
  346. " time-machine -C " #$channels
  347. " -- locate --update --method="
  348. #$(symbol->string method)))))))
  349. (define package-database-service-type
  350. (service-type
  351. (name 'package-database)
  352. (extensions (list (service-extension mcron-service-type
  353. package-database-mcron-jobs)))
  354. (description
  355. "Periodically update the package database used by the @code{guix locate} command,
  356. which lets you search for packages that provide a given file.")
  357. (default-value (package-database-configuration))))
  358. ;;;
  359. ;;; Unattended upgrade.
  360. ;;;
  361. (define-record-type* <unattended-upgrade-configuration>
  362. unattended-upgrade-configuration make-unattended-upgrade-configuration
  363. unattended-upgrade-configuration?
  364. (operating-system-file unattended-upgrade-operating-system-file
  365. (default "/run/current-system/configuration.scm"))
  366. (operating-system-expression unattended-upgrade-operating-system-expression
  367. (default #f))
  368. (schedule unattended-upgrade-configuration-schedule
  369. (default "30 01 * * 0"))
  370. (channels unattended-upgrade-configuration-channels
  371. (default #~%default-channels))
  372. (services-to-restart unattended-upgrade-configuration-services-to-restart
  373. (default '(mcron)))
  374. (system-expiration unattended-upgrade-system-expiration
  375. (default (* 3 30 24 3600)))
  376. (maximum-duration unattended-upgrade-maximum-duration
  377. (default 3600))
  378. (log-file unattended-upgrade-configuration-log-file
  379. (default %unattended-upgrade-log-file)))
  380. (define %unattended-upgrade-log-file
  381. "/var/log/unattended-upgrade.log")
  382. (define (unattended-upgrade-mcron-jobs config)
  383. (define channels
  384. (scheme-file "channels.scm"
  385. (unattended-upgrade-configuration-channels config)))
  386. (define log
  387. (unattended-upgrade-configuration-log-file config))
  388. (define services
  389. (unattended-upgrade-configuration-services-to-restart config))
  390. (define expiration
  391. (unattended-upgrade-system-expiration config))
  392. (define config-file
  393. (unattended-upgrade-operating-system-file config))
  394. (define expression
  395. (unattended-upgrade-operating-system-expression config))
  396. (define arguments
  397. (if expression
  398. #~(list "-e" (object->string '#$expression))
  399. #~(list #$config-file)))
  400. (define code
  401. (with-imported-modules (source-module-closure '((guix build utils)
  402. (gnu services herd)))
  403. #~(begin
  404. (use-modules (guix build utils)
  405. (gnu services herd)
  406. (srfi srfi-19)
  407. (srfi srfi-34))
  408. (define log
  409. (open-file #$log "a0"))
  410. (define (timestamp)
  411. (date->string (time-utc->date (current-time time-utc))
  412. "[~4]"))
  413. (define (alarm-handler . _)
  414. (format #t "~a time is up, aborting upgrade~%"
  415. (timestamp))
  416. (exit 1))
  417. ;; 'guix time-machine' needs X.509 certificates to authenticate the
  418. ;; Git host.
  419. (setenv "SSL_CERT_DIR"
  420. #$(file-append nss-certs "/etc/ssl/certs"))
  421. ;; Make sure the upgrade doesn't take too long.
  422. (sigaction SIGALRM alarm-handler)
  423. (alarm #$(unattended-upgrade-maximum-duration config))
  424. ;; Redirect stdout/stderr to LOG to save the output of 'guix' below.
  425. (redirect-port log (current-output-port))
  426. (redirect-port log (current-error-port))
  427. (format #t "~a starting upgrade...~%" (timestamp))
  428. (guard (c ((invoke-error? c)
  429. (report-invoke-error c)))
  430. (apply invoke #$(file-append guix "/bin/guix")
  431. "time-machine" "-C" #$channels
  432. "--" "system" "reconfigure" #$arguments)
  433. ;; 'guix system delete-generations' fails when there's no
  434. ;; matching generation. Thus, catch 'invoke-error?'.
  435. (guard (c ((invoke-error? c)
  436. (report-invoke-error c)))
  437. (invoke #$(file-append guix "/bin/guix")
  438. "system" "delete-generations"
  439. #$(string-append (number->string expiration)
  440. "s")))
  441. (format #t "~a restarting services...~%" (timestamp))
  442. (for-each restart-service '#$services)
  443. ;; XXX: If 'mcron' has been restarted, perhaps this isn't
  444. ;; reached.
  445. (format #t "~a upgrade complete~%" (timestamp))))))
  446. (define upgrade
  447. (program-file "unattended-upgrade" code))
  448. (list #~(job #$(unattended-upgrade-configuration-schedule config)
  449. #$upgrade)))
  450. (define (unattended-upgrade-log-rotations config)
  451. (list (log-rotation
  452. (files
  453. (list (unattended-upgrade-configuration-log-file config))))))
  454. (define unattended-upgrade-service-type
  455. (service-type
  456. (name 'unattended-upgrade)
  457. (extensions
  458. (list (service-extension mcron-service-type
  459. unattended-upgrade-mcron-jobs)
  460. (service-extension rottlog-service-type
  461. unattended-upgrade-log-rotations)))
  462. (description
  463. "Periodically upgrade the system from the current configuration.")
  464. (default-value (unattended-upgrade-configuration))))
  465. ;;; admin.scm ends here