databases.scm 30 KB

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