databases.scm 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2015 David Thompson <davet@gnu.org>
  3. ;;; Copyright © 2015-2016, 2022-2023 Ludovic Courtès <ludo@gnu.org>
  4. ;;; Copyright © 2016 Leo Famulari <leo@famulari.name>
  5. ;;; Copyright © 2017 Christopher Baines <mail@cbaines.net>
  6. ;;; Copyright © 2018 Clément Lassieur <clement@lassieur.org>
  7. ;;; Copyright © 2018 Julien Lepiller <julien@lepiller.eu>
  8. ;;; Copyright © 2019 Robert Vollmert <rob@vllmrt.net>
  9. ;;; Copyright © 2020, 2022 Marius Bakke <marius@gnu.org>
  10. ;;; Copyright © 2021 David Larsson <david.larsson@selfhosted.xyz>
  11. ;;; Copyright © 2021 Aljosha Papsch <ep@stern-data.com>
  12. ;;;
  13. ;;; This file is part of GNU Guix.
  14. ;;;
  15. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  16. ;;; under the terms of the GNU General Public License as published by
  17. ;;; the Free Software Foundation; either version 3 of the License, or (at
  18. ;;; your option) any later version.
  19. ;;;
  20. ;;; GNU Guix is distributed in the hope that it will be useful, but
  21. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  22. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  23. ;;; GNU General Public License for more details.
  24. ;;;
  25. ;;; You should have received a copy of the GNU General Public License
  26. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  27. (define-module (gnu services databases)
  28. #:use-module (gnu services)
  29. #:use-module (gnu services shepherd)
  30. #:use-module (gnu system shadow)
  31. #:use-module (gnu packages admin)
  32. #:use-module (gnu packages base)
  33. #:use-module (gnu packages databases)
  34. #:use-module (guix build-system trivial)
  35. #:use-module (guix build union)
  36. #:use-module (guix deprecation)
  37. #:use-module (guix modules)
  38. #:use-module (guix packages)
  39. #:use-module (guix records)
  40. #:use-module (guix gexp)
  41. #:use-module (srfi srfi-1)
  42. #:use-module (ice-9 match)
  43. #:export (postgresql-config-file
  44. postgresql-config-file?
  45. postgresql-config-file-log-destination
  46. postgresql-config-file-hba-file
  47. postgresql-config-file-ident-file
  48. postgresql-config-file-socket-directory
  49. postgresql-config-file-extra-config
  50. postgresql-configuration
  51. postgresql-configuration?
  52. postgresql-configuration-postgresql
  53. postgresql-configuration-port
  54. postgresql-configuration-locale
  55. postgresql-configuration-file
  56. postgresql-configuration-log-directory
  57. postgresql-configuration-data-directory
  58. postgresql-configuration-extension-packages
  59. postgresql-service
  60. postgresql-service-type
  61. postgresql-role
  62. postgresql-role?
  63. postgresql-role-name
  64. postgresql-role-permissions
  65. postgresql-role-create-database?
  66. postgresql-role-configuration
  67. postgresql-role-configuration?
  68. postgresql-role-configuration-host
  69. postgresql-role-configuration-roles
  70. postgresql-role-service-type
  71. memcached-service-type
  72. memcached-configuration
  73. memcached-configuration?
  74. memcached-configuration-memecached
  75. memcached-configuration-interfaces
  76. memcached-configuration-tcp-port
  77. memcached-configuration-udp-port
  78. memcached-configuration-additional-options
  79. mysql-service
  80. mysql-service-type
  81. mysql-configuration
  82. mysql-configuration?
  83. redis-configuration
  84. redis-configuration?
  85. redis-service-type))
  86. ;;; Commentary:
  87. ;;;
  88. ;;; Database services.
  89. ;;;
  90. ;;; Code:
  91. (define %default-postgres-hba
  92. (plain-file "pg_hba.conf"
  93. "
  94. local all all peer
  95. host all all 127.0.0.1/32 md5
  96. host all all ::1/128 md5"))
  97. (define %default-postgres-ident
  98. (plain-file "pg_ident.conf"
  99. "# MAPNAME SYSTEM-USERNAME PG-USERNAME"))
  100. (define-record-type* <postgresql-config-file>
  101. postgresql-config-file make-postgresql-config-file
  102. postgresql-config-file?
  103. (log-destination postgresql-config-file-log-destination
  104. (default "syslog"))
  105. (hba-file postgresql-config-file-hba-file
  106. (default %default-postgres-hba))
  107. (ident-file postgresql-config-file-ident-file
  108. (default %default-postgres-ident))
  109. (socket-directory postgresql-config-file-socket-directory
  110. (default "/var/run/postgresql"))
  111. (extra-config postgresql-config-file-extra-config
  112. (default '())))
  113. (define-gexp-compiler (postgresql-config-file-compiler
  114. (file <postgresql-config-file>) system target)
  115. (match file
  116. (($ <postgresql-config-file> log-destination hba-file
  117. ident-file socket-directory
  118. extra-config)
  119. ;; See: https://www.postgresql.org/docs/current/config-setting.html.
  120. (define (format-value value)
  121. (cond
  122. ((boolean? value)
  123. (list (if value "on" "off")))
  124. ((number? value)
  125. (list (number->string value)))
  126. (else
  127. (list "'" value "'"))))
  128. (define contents
  129. (append-map
  130. (match-lambda
  131. ((key) '())
  132. ((key . #f) '())
  133. ((key values ...)
  134. `(,key " = " ,@(append-map format-value values) "\n")))
  135. `(("log_destination" ,log-destination)
  136. ("hba_file" ,hba-file)
  137. ("ident_file" ,ident-file)
  138. ,@(if socket-directory
  139. `(("unix_socket_directories" ,socket-directory))
  140. '())
  141. ,@extra-config)))
  142. (gexp->derivation
  143. "postgresql.conf"
  144. #~(call-with-output-file (ungexp output "out")
  145. (lambda (port)
  146. (display
  147. (string-append #$@contents)
  148. port)))
  149. #:local-build? #t))))
  150. (define-record-type* <postgresql-configuration>
  151. postgresql-configuration make-postgresql-configuration
  152. postgresql-configuration?
  153. (postgresql postgresql-configuration-postgresql ;file-like
  154. (default postgresql-10))
  155. (port postgresql-configuration-port
  156. (default 5432))
  157. (locale postgresql-configuration-locale
  158. (default "en_US.utf8"))
  159. (config-file postgresql-configuration-file
  160. (default (postgresql-config-file)))
  161. (log-directory postgresql-configuration-log-directory
  162. (default "/var/log/postgresql"))
  163. (data-directory postgresql-configuration-data-directory
  164. (default "/var/lib/postgresql/data"))
  165. (extension-packages postgresql-configuration-extension-packages
  166. (default '()))
  167. (create-account? postgresql-configuration-create-account?
  168. (default #t))
  169. (uid postgresql-configuration-uid
  170. (default #f))
  171. (gid postgresql-configuration-gid
  172. (default #f)))
  173. (define (create-postgresql-account config)
  174. (match-record config <postgresql-configuration>
  175. (create-account? uid gid)
  176. (if (not create-account?) '()
  177. (list (user-group
  178. (name "postgres")
  179. (id gid)
  180. (system? #t))
  181. (user-account
  182. (name "postgres")
  183. (group "postgres")
  184. (system? #t)
  185. (uid uid)
  186. (comment "PostgreSQL server user")
  187. (home-directory "/var/empty")
  188. (shell (file-append shadow "/sbin/nologin")))))))
  189. (define (final-postgresql postgresql extension-packages)
  190. (if (null? extension-packages)
  191. postgresql
  192. (package
  193. (inherit postgresql)
  194. (source #f)
  195. (build-system trivial-build-system)
  196. (arguments
  197. `(#:modules ((guix build utils) (guix build union))
  198. #:builder
  199. (begin
  200. (use-modules (guix build utils) (guix build union) (srfi srfi-26))
  201. (union-build (assoc-ref %outputs "out")
  202. (map (lambda (input) (cdr input))
  203. %build-inputs))
  204. #t)))
  205. (inputs
  206. `(("postgresql" ,postgresql)
  207. ,@(map (lambda (extension) (list "extension" extension))
  208. extension-packages))))))
  209. (define postgresql-activation
  210. (match-lambda
  211. (($ <postgresql-configuration> postgresql port locale config-file
  212. log-directory data-directory
  213. extension-packages)
  214. #~(begin
  215. (use-modules (guix build utils)
  216. (ice-9 match))
  217. (let ((user (getpwnam "postgres"))
  218. (initdb (string-append
  219. #$(final-postgresql postgresql
  220. extension-packages)
  221. "/bin/initdb"))
  222. (initdb-args
  223. (append
  224. (if #$locale
  225. (list (string-append "--locale=" #$locale))
  226. '()))))
  227. ;; Create db state directory.
  228. (mkdir-p #$data-directory)
  229. (chown #$data-directory (passwd:uid user) (passwd:gid user))
  230. ;; Create the socket directory.
  231. (let ((socket-directory
  232. #$(postgresql-config-file-socket-directory config-file)))
  233. (when (string? socket-directory)
  234. (mkdir-p socket-directory)
  235. (chown socket-directory (passwd:uid user) (passwd:gid user))))
  236. ;; Create the log directory.
  237. (when (string? #$log-directory)
  238. (mkdir-p #$log-directory)
  239. (chown #$log-directory (passwd:uid user) (passwd:gid user)))
  240. ;; Drop privileges and init state directory in a new
  241. ;; process. Wait for it to finish before proceeding.
  242. (match (primitive-fork)
  243. (0
  244. ;; Exit with a non-zero status code if an exception is thrown.
  245. (dynamic-wind
  246. (const #t)
  247. (lambda ()
  248. (setgid (passwd:gid user))
  249. (setuid (passwd:uid user))
  250. (primitive-exit
  251. (apply system*
  252. initdb
  253. "-D"
  254. #$data-directory
  255. initdb-args)))
  256. (lambda ()
  257. (primitive-exit 1))))
  258. (pid (waitpid pid))))))))
  259. (define postgresql-shepherd-service
  260. (match-lambda
  261. (($ <postgresql-configuration> postgresql port locale config-file
  262. log-directory data-directory
  263. extension-packages)
  264. (let* ((pg_ctl-wrapper
  265. ;; Wrapper script that switches to the 'postgres' user before
  266. ;; launching daemon.
  267. (program-file
  268. "pg_ctl-wrapper"
  269. #~(begin
  270. (use-modules (ice-9 match)
  271. (ice-9 format))
  272. (match (command-line)
  273. ((_ mode)
  274. (let ((user (getpwnam "postgres"))
  275. (pg_ctl #$(file-append
  276. (final-postgresql postgresql
  277. extension-packages)
  278. "/bin/pg_ctl"))
  279. (options (format #f "--config-file=~a -p ~d"
  280. #$config-file #$port)))
  281. (setgid (passwd:gid user))
  282. (setuid (passwd:uid user))
  283. (execl pg_ctl pg_ctl "-D" #$data-directory
  284. #$@(if (string? log-directory)
  285. (list "-l"
  286. (string-append log-directory
  287. "/pg_ctl.log"))
  288. '())
  289. "-o" options
  290. mode)))))))
  291. (pid-file (in-vicinity data-directory "postmaster.pid"))
  292. (action (lambda args
  293. #~(lambda _
  294. (invoke #$pg_ctl-wrapper #$@args)
  295. (match '#$args
  296. (("start")
  297. (call-with-input-file #$pid-file read))
  298. (_ #t))))))
  299. (list (shepherd-service
  300. (provision '(postgres postgresql))
  301. (documentation "Run the PostgreSQL daemon.")
  302. (requirement '(user-processes loopback syslogd))
  303. (modules `((ice-9 match)
  304. ,@%default-modules))
  305. (actions (list (shepherd-configuration-action config-file)))
  306. (start (action "start"))
  307. (stop (action "stop"))))))))
  308. (define postgresql-service-type
  309. (service-type
  310. (name 'postgresql)
  311. (extensions
  312. (list (service-extension shepherd-root-service-type
  313. postgresql-shepherd-service)
  314. (service-extension activation-service-type
  315. postgresql-activation)
  316. (service-extension account-service-type
  317. create-postgresql-account)
  318. (service-extension
  319. profile-service-type
  320. (compose list postgresql-configuration-postgresql))))
  321. (default-value (postgresql-configuration))
  322. (description "Run the PostgreSQL database server.")))
  323. (define-deprecated (postgresql-service #:key (postgresql postgresql)
  324. (port 5432)
  325. (locale "en_US.utf8")
  326. (config-file (postgresql-config-file))
  327. (data-directory
  328. "/var/lib/postgresql/data")
  329. (extension-packages '()))
  330. postgresql-service-type
  331. "Return a service that runs @var{postgresql}, the PostgreSQL database
  332. server.
  333. The PostgreSQL daemon loads its runtime configuration from @var{config-file}
  334. and stores the database cluster in @var{data-directory}."
  335. (service postgresql-service-type
  336. (postgresql-configuration
  337. (postgresql postgresql)
  338. (port port)
  339. (locale locale)
  340. (config-file config-file)
  341. (data-directory data-directory)
  342. (extension-packages extension-packages))))
  343. (define-record-type* <postgresql-role>
  344. postgresql-role make-postgresql-role
  345. postgresql-role?
  346. (name postgresql-role-name) ;string
  347. (permissions postgresql-role-permissions
  348. (default '(createdb login))) ;list
  349. (create-database? postgresql-role-create-database? ;boolean
  350. (default #f))
  351. (encoding postgresql-role-encoding ;string
  352. (default "UTF8"))
  353. (collation postgresql-role-collation ;string
  354. (default "en_US.utf8"))
  355. (ctype postgresql-role-ctype ;string
  356. (default "en_US.utf8"))
  357. (template postgresql-role-template ;string
  358. (default "template1")))
  359. (define-record-type* <postgresql-role-configuration>
  360. postgresql-role-configuration make-postgresql-role-configuration
  361. postgresql-role-configuration?
  362. (host postgresql-role-configuration-host ;string
  363. (default "/var/run/postgresql"))
  364. (log postgresql-role-configuration-log ;string
  365. (default "/var/log/postgresql_roles.log"))
  366. (roles postgresql-role-configuration-roles
  367. (default '()))) ;list
  368. (define (postgresql-create-roles config)
  369. ;; See: https://www.postgresql.org/docs/current/sql-createrole.html for the
  370. ;; complete permissions list.
  371. (define (format-permissions permissions)
  372. (let ((dict '(bypassrls createdb createrole login replication superuser)))
  373. (string-join (filter-map (lambda (permission)
  374. (and (member permission dict)
  375. (string-upcase
  376. (symbol->string permission))))
  377. permissions)
  378. " ")))
  379. (define (roles->queries roles)
  380. (apply mixed-text-file "queries"
  381. (append-map
  382. (lambda (role)
  383. (match-record role <postgresql-role>
  384. (name permissions create-database? encoding collation ctype
  385. template)
  386. `("SELECT NOT(EXISTS(SELECT 1 FROM pg_catalog.pg_roles WHERE \
  387. rolname = '" ,name "')) as not_exists;\n"
  388. "\\gset\n"
  389. "\\if :not_exists\n"
  390. "CREATE ROLE \"" ,name "\""
  391. " WITH " ,(format-permissions permissions)
  392. ";\n"
  393. ,@(if create-database?
  394. `("CREATE DATABASE \"" ,name "\""
  395. " OWNER \"" ,name "\"\n"
  396. " ENCODING '" ,encoding "'\n"
  397. " LC_COLLATE '" ,collation "'\n"
  398. " LC_CTYPE '" ,ctype "'\n"
  399. " TEMPLATE " ,template ";")
  400. '())
  401. "\\endif\n")))
  402. roles)))
  403. (let ((host (postgresql-role-configuration-host config))
  404. (roles (postgresql-role-configuration-roles config)))
  405. #~(let ((psql #$(file-append postgresql "/bin/psql")))
  406. (list psql "-a" "-h" #$host "-f" #$(roles->queries roles)))))
  407. (define (postgresql-role-shepherd-service config)
  408. (match-record config <postgresql-role-configuration>
  409. (log)
  410. (list (shepherd-service
  411. (requirement '(postgres))
  412. (provision '(postgres-roles))
  413. (one-shot? #t)
  414. (start
  415. #~(lambda args
  416. (let ((pid (fork+exec-command
  417. #$(postgresql-create-roles config)
  418. #:user "postgres"
  419. #:group "postgres"
  420. #:log-file #$log)))
  421. (zero? (cdr (waitpid pid))))))
  422. (documentation "Create PostgreSQL roles.")))))
  423. (define postgresql-role-service-type
  424. (service-type (name 'postgresql-role)
  425. (extensions
  426. (list (service-extension shepherd-root-service-type
  427. postgresql-role-shepherd-service)))
  428. (compose concatenate)
  429. (extend (lambda (config extended-roles)
  430. (match-record config <postgresql-role-configuration>
  431. (host roles)
  432. (postgresql-role-configuration
  433. (host host)
  434. (roles (append roles extended-roles))))))
  435. (default-value (postgresql-role-configuration))
  436. (description "Ensure the specified PostgreSQL roles are
  437. created after the PostgreSQL database is started.")))
  438. ;;;
  439. ;;; Memcached
  440. ;;;
  441. (define-record-type* <memcached-configuration>
  442. memcached-configuration make-memcached-configuration
  443. memcached-configuration?
  444. (memcached memcached-configuration-memcached ;file-like
  445. (default memcached))
  446. (interfaces memcached-configuration-interfaces
  447. (default '("0.0.0.0")))
  448. (tcp-port memcached-configuration-tcp-port
  449. (default 11211))
  450. (udp-port memcached-configuration-udp-port
  451. (default 11211))
  452. (additional-options memcached-configuration-additional-options
  453. (default '())))
  454. (define %memcached-accounts
  455. (list (user-group (name "memcached") (system? #t))
  456. (user-account
  457. (name "memcached")
  458. (group "memcached")
  459. (system? #t)
  460. (comment "Memcached server user")
  461. (home-directory "/var/empty")
  462. (shell (file-append shadow "/sbin/nologin")))))
  463. (define memcached-activation
  464. #~(begin
  465. (use-modules (guix build utils))
  466. (let ((user (getpwnam "memcached")))
  467. (mkdir-p "/var/run/memcached")
  468. (chown "/var/run/memcached"
  469. (passwd:uid user) (passwd:gid user)))))
  470. (define memcached-shepherd-service
  471. (match-lambda
  472. (($ <memcached-configuration> memcached interfaces tcp-port udp-port
  473. additional-options)
  474. (with-imported-modules (source-module-closure
  475. '((gnu build shepherd)))
  476. (list (shepherd-service
  477. (provision '(memcached))
  478. (documentation "Run the Memcached daemon.")
  479. (requirement '(user-processes loopback))
  480. (modules '((gnu build shepherd)))
  481. (start #~(make-forkexec-constructor
  482. `(#$(file-append memcached "/bin/memcached")
  483. "-l" #$(string-join interfaces ",")
  484. "-p" #$(number->string tcp-port)
  485. "-U" #$(number->string udp-port)
  486. "--daemon"
  487. ;; Memcached changes to the memcached user prior to
  488. ;; writing the pid file, so write it to a directory
  489. ;; that memcached owns.
  490. "-P" "/var/run/memcached/pid"
  491. "-u" "memcached"
  492. ,#$@additional-options)
  493. #:log-file "/var/log/memcached"
  494. #:pid-file "/var/run/memcached/pid"))
  495. (stop #~(make-kill-destructor))))))))
  496. (define memcached-service-type
  497. (service-type (name 'memcached)
  498. (extensions
  499. (list (service-extension shepherd-root-service-type
  500. memcached-shepherd-service)
  501. (service-extension activation-service-type
  502. (const memcached-activation))
  503. (service-extension account-service-type
  504. (const %memcached-accounts))))
  505. (default-value (memcached-configuration))
  506. (description "Run @command{memcached}, a daemon that provides
  507. an in-memory caching service, intended for use by dynamic web
  508. applications.")))
  509. ;;;
  510. ;;; MySQL.
  511. ;;;
  512. (define-record-type* <mysql-configuration>
  513. mysql-configuration make-mysql-configuration
  514. mysql-configuration?
  515. (mysql mysql-configuration-mysql (default mariadb))
  516. (bind-address mysql-configuration-bind-address (default "127.0.0.1"))
  517. (port mysql-configuration-port (default 3306))
  518. (socket mysql-configuration-socket (default "/run/mysqld/mysqld.sock"))
  519. (datadir mysql-configuration-datadir (default "/var/lib/mysql"))
  520. (extra-content mysql-configuration-extra-content (default ""))
  521. (extra-environment mysql-configuration-extra-environment (default #~'()))
  522. (auto-upgrade? mysql-configuration-auto-upgrade? (default #t)))
  523. (define %mysql-accounts
  524. (list (user-group
  525. (name "mysql")
  526. (system? #t))
  527. (user-account
  528. (name "mysql")
  529. (group "mysql")
  530. (system? #t)
  531. (home-directory "/var/empty")
  532. (shell (file-append shadow "/sbin/nologin")))))
  533. (define mysql-configuration-file
  534. (match-lambda
  535. (($ <mysql-configuration> mysql bind-address port socket datadir extra-content)
  536. (mixed-text-file "my.cnf" "[mysqld]
  537. datadir=" datadir "
  538. socket=" socket "
  539. bind-address=" bind-address "
  540. port=" (number->string port) "
  541. " extra-content "
  542. "))))
  543. (define (mysqld-wrapper config)
  544. "Start mysqld, and initialize the system tables if necessary."
  545. (program-file
  546. "mysqld-wrapper"
  547. (with-imported-modules (source-module-closure
  548. '((guix build utils)))
  549. (let ((mysql (mysql-configuration-mysql config))
  550. (datadir (mysql-configuration-datadir config))
  551. (my.cnf (mysql-configuration-file config)))
  552. #~(begin
  553. (use-modules (guix build utils))
  554. (let* ((mysqld (string-append #$mysql "/bin/mysqld"))
  555. (user (getpwnam "mysql"))
  556. (uid (passwd:uid user))
  557. (gid (passwd:gid user))
  558. (rundir "/run/mysqld"))
  559. (mkdir-p #$datadir)
  560. (chown #$datadir uid gid)
  561. (mkdir-p rundir)
  562. (chown rundir uid gid)
  563. (unless (file-exists? (string-append #$datadir "/mysql"))
  564. (let ((init (system* #$(mysql-install config))))
  565. (unless (= 0 (status:exit-val init))
  566. (throw 'system-error "MySQL initialization failed."))))
  567. ;; Drop privileges and start the server.
  568. (setgid gid) (setuid uid)
  569. (execl mysqld mysqld
  570. (string-append "--defaults-file=" #$my.cnf))))))))
  571. (define (mysql-shepherd-service config)
  572. (list (shepherd-service
  573. (provision '(mysql))
  574. (requirement '(user-processes))
  575. (documentation "Run the MySQL server.")
  576. (actions (list (shepherd-configuration-action
  577. (mysql-configuration-file config))))
  578. (start (let ((mysql (mysql-configuration-mysql config))
  579. (extra-env (mysql-configuration-extra-environment config))
  580. (my.cnf (mysql-configuration-file config)))
  581. #~(make-forkexec-constructor
  582. (list #$(mysqld-wrapper config))
  583. #:log-file "/var/log/mysqld.log"
  584. #:environment-variables #$extra-env)))
  585. (stop #~(make-kill-destructor)))))
  586. (define (mysql-install config)
  587. "Install MySQL system database and secure the installation."
  588. (let ((mysql (mysql-configuration-mysql config))
  589. (my.cnf (mysql-configuration-file config)))
  590. (program-file
  591. "mysql-install"
  592. (with-imported-modules (source-module-closure
  593. '((guix build utils)))
  594. #~(begin
  595. (use-modules (guix build utils))
  596. ;; Make sed, mkdir, uname, etc available for mariadb-install-db.
  597. (set-path-environment-variable "PATH" '("bin")
  598. (list #$sed #$coreutils))
  599. (if (string=? "mariadb" #$(package-name mysql))
  600. ;; For MariaDB.
  601. (system* #$(file-append mysql "/bin/mariadb-install-db")
  602. (string-append "--defaults-file=" #$my.cnf)
  603. "--skip-test-db"
  604. "--user=mysql")
  605. ;; For MySQL.
  606. (system* #$(file-append mysql "/bin/mysqld")
  607. (string-append "--defaults-file=" #$my.cnf)
  608. "--initialize"
  609. "--user=mysql")))))))
  610. (define (mysql-upgrade-wrapper config)
  611. ;; The MySQL socket and PID file may appear before the server is ready to
  612. ;; accept connections. Ensure the socket is responsive before attempting
  613. ;; to run the upgrade script.
  614. (let ((mysql (mysql-configuration-mysql config))
  615. (socket-file (mysql-configuration-socket config))
  616. (config-file (mysql-configuration-file config)))
  617. (program-file
  618. "mysql-upgrade-wrapper"
  619. #~(begin
  620. (let ((mysql-upgrade #$(file-append mysql "/bin/mysql_upgrade"))
  621. (timeout 20))
  622. (begin
  623. (let loop ((i 0))
  624. (catch 'system-error
  625. (lambda ()
  626. (let ((sock (socket PF_UNIX SOCK_STREAM 0)))
  627. (connect sock AF_UNIX #$socket-file)
  628. (close-port sock)
  629. ;; The socket is ready!
  630. (execl mysql-upgrade mysql-upgrade
  631. (string-append "--defaults-file=" #$config-file)
  632. "--user=mysql")))
  633. (lambda args
  634. (if (< i timeout)
  635. (begin
  636. (sleep 1)
  637. (loop (+ 1 i)))
  638. ;; No luck, give up.
  639. (throw 'timeout-error
  640. "MySQL server did not appear in time!")))))))))))
  641. (define (mysql-upgrade-shepherd-service config)
  642. (list (shepherd-service
  643. (provision '(mysql-upgrade))
  644. (requirement '(mysql))
  645. (one-shot? #t)
  646. (documentation "Upgrade MySQL database schemas.")
  647. (start #~(make-forkexec-constructor
  648. (list #$(mysql-upgrade-wrapper config))
  649. #:user "mysql" #:group "mysql"
  650. #:log-file "/var/log/mysql_upgrade.log")))))
  651. (define (mysql-shepherd-services config)
  652. (let ((mysql-services (mysql-shepherd-service config)))
  653. (if (mysql-configuration-auto-upgrade? config)
  654. (append mysql-services
  655. (mysql-upgrade-shepherd-service config))
  656. mysql-services)))
  657. (define mysql-service-type
  658. (service-type
  659. (name 'mysql)
  660. (extensions
  661. (list (service-extension account-service-type
  662. (const %mysql-accounts))
  663. (service-extension shepherd-root-service-type
  664. mysql-shepherd-services)))
  665. (default-value (mysql-configuration))
  666. (description "Run the MySQL or MariaDB database server,
  667. @command{mysqld}.")))
  668. (define-deprecated (mysql-service #:key (config (mysql-configuration)))
  669. mysql-service-type
  670. (service mysql-service-type config))
  671. ;;;
  672. ;;; Redis
  673. ;;;
  674. (define-record-type* <redis-configuration>
  675. redis-configuration make-redis-configuration
  676. redis-configuration?
  677. (redis redis-configuration-redis ;file-like
  678. (default redis))
  679. (bind redis-configuration-bind
  680. (default "127.0.0.1"))
  681. (port redis-configuration-port
  682. (default 6379))
  683. (working-directory redis-configuration-working-directory
  684. (default "/var/lib/redis"))
  685. (config-file redis-configuration-config-file
  686. (default #f)))
  687. (define (default-redis.conf bind port working-directory)
  688. (mixed-text-file "redis.conf"
  689. "bind " bind "\n"
  690. "port " (number->string port) "\n"
  691. "dir " working-directory "\n"
  692. "daemonize no\n"))
  693. (define %redis-accounts
  694. (list (user-group (name "redis") (system? #t))
  695. (user-account
  696. (name "redis")
  697. (group "redis")
  698. (system? #t)
  699. (comment "Redis server user")
  700. (home-directory "/var/empty")
  701. (shell (file-append shadow "/sbin/nologin")))))
  702. (define redis-activation
  703. (match-lambda
  704. (($ <redis-configuration> redis bind port working-directory config-file)
  705. #~(begin
  706. (use-modules (guix build utils)
  707. (ice-9 match))
  708. (let ((user (getpwnam "redis")))
  709. (mkdir-p #$working-directory)
  710. (chown #$working-directory (passwd:uid user) (passwd:gid user)))))))
  711. (define redis-shepherd-service
  712. (match-lambda
  713. (($ <redis-configuration> redis bind port working-directory config-file)
  714. (let ((config-file
  715. (or config-file
  716. (default-redis.conf bind port working-directory))))
  717. (list (shepherd-service
  718. (provision '(redis))
  719. (documentation "Run the Redis daemon.")
  720. (requirement '(user-processes syslogd))
  721. (actions (list (shepherd-configuration-action config-file)))
  722. (start #~(make-forkexec-constructor
  723. '(#$(file-append redis "/bin/redis-server")
  724. #$config-file)
  725. #:user "redis"
  726. #:group "redis"))
  727. (stop #~(make-kill-destructor))))))))
  728. (define redis-service-type
  729. (service-type (name 'redis)
  730. (extensions
  731. (list (service-extension shepherd-root-service-type
  732. redis-shepherd-service)
  733. (service-extension activation-service-type
  734. redis-activation)
  735. (service-extension account-service-type
  736. (const %redis-accounts))))
  737. (default-value (redis-configuration))
  738. (description "Run Redis, a caching key/value store.")))