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>
7 ;;; This file is part of GNU Guix.
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.
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.
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/>.
22 (define-module (gnu services databases)
23 #:use-module (gnu services)
24 #:use-module (gnu services shepherd)
25 #:use-module (gnu system shadow)
26 #:use-module (gnu packages admin)
27 #:use-module (gnu packages databases)
28 #:use-module (guix records)
29 #:use-module (guix gexp)
30 #:use-module (ice-9 match)
31 #:export (postgresql-configuration
32 postgresql-configuration?
34 postgresql-service-type
47 ;;; Database services.
51 (define-record-type* <postgresql-configuration>
52 postgresql-configuration make-postgresql-configuration
53 postgresql-configuration?
54 (postgresql postgresql-configuration-postgresql ;<package>
56 (port postgresql-configuration-port
58 (locale postgresql-configuration-locale
59 (default "en_US.utf8"))
60 (config-file postgresql-configuration-file)
61 (data-directory postgresql-configuration-data-directory))
63 (define %default-postgres-hba
64 (plain-file "pg_hba.conf"
67 host all all 127.0.0.1/32 trust
68 host all all ::1/128 trust"))
70 (define %default-postgres-ident
71 (plain-file "pg_ident.conf"
72 "# MAPNAME SYSTEM-USERNAME PG-USERNAME"))
74 (define %default-postgres-config
75 (mixed-text-file "postgresql.conf"
76 "log_destination = 'syslog'\n"
77 "hba_file = '" %default-postgres-hba "'\n"
78 "ident_file = '" %default-postgres-ident "'\n"))
80 (define %postgresql-accounts
81 (list (user-group (name "postgres") (system? #t))
86 (comment "PostgreSQL server user")
87 (home-directory "/var/empty")
88 (shell (file-append shadow "/sbin/nologin")))))
90 (define postgresql-activation
92 (($ <postgresql-configuration> postgresql port locale config-file data-directory)
94 (use-modules (guix build utils)
97 (let ((user (getpwnam "postgres"))
98 (initdb (string-append #$postgresql "/bin/initdb"))
102 (list (string-append "--locale=" #$locale))
104 ;; Create db state directory.
105 (mkdir-p #$data-directory)
106 (chown #$data-directory (passwd:uid user) (passwd:gid user))
108 ;; Drop privileges and init state directory in a new
109 ;; process. Wait for it to finish before proceeding.
110 (match (primitive-fork)
112 ;; Exit with a non-zero status code if an exception is thrown.
116 (setgid (passwd:gid user))
117 (setuid (passwd:uid user))
125 (primitive-exit 1))))
126 (pid (waitpid pid))))))))
128 (define postgresql-shepherd-service
130 (($ <postgresql-configuration> postgresql port locale config-file data-directory)
132 ;; Wrapper script that switches to the 'postgres' user before
134 (program-file "start-postgres"
135 #~(let ((user (getpwnam "postgres"))
136 (postgres (string-append #$postgresql
138 (setgid (passwd:gid user))
139 (setuid (passwd:uid user))
141 (string-append "--config-file="
143 "-p" (number->string #$port)
144 "-D" #$data-directory)))))
145 (list (shepherd-service
146 (provision '(postgres))
147 (documentation "Run the PostgreSQL daemon.")
148 (requirement '(user-processes loopback syslogd))
149 (start #~(make-forkexec-constructor #$start-script))
150 (stop #~(make-kill-destructor))))))))
152 (define postgresql-service-type
153 (service-type (name 'postgresql)
155 (list (service-extension shepherd-root-service-type
156 postgresql-shepherd-service)
157 (service-extension activation-service-type
158 postgresql-activation)
159 (service-extension account-service-type
160 (const %postgresql-accounts))))))
162 (define* (postgresql-service #:key (postgresql postgresql)
164 (locale "en_US.utf8")
165 (config-file %default-postgres-config)
166 (data-directory "/var/lib/postgresql/data"))
167 "Return a service that runs @var{postgresql}, the PostgreSQL database server.
169 The PostgreSQL daemon loads its runtime configuration from @var{config-file}
170 and stores the database cluster in @var{data-directory}."
171 (service postgresql-service-type
172 (postgresql-configuration
173 (postgresql postgresql)
176 (config-file config-file)
177 (data-directory data-directory))))
184 (define-record-type* <mysql-configuration>
185 mysql-configuration make-mysql-configuration
187 (mysql mysql-configuration-mysql (default mariadb))
188 (port mysql-configuration-port (default 3306)))
190 (define %mysql-accounts
198 (home-directory "/var/empty")
199 (shell (file-append shadow "/sbin/nologin")))))
201 (define mysql-configuration-file
203 (($ <mysql-configuration> mysql port)
204 (mixed-text-file "my.cnf" "[mysqld]
205 datadir=/var/lib/mysql
206 socket=/run/mysqld/mysqld.sock
207 port=" (number->string port) "
210 (define (%mysql-activation config)
211 "Return an activation gexp for the MySQL or MariaDB database server."
212 (let ((mysql (mysql-configuration-mysql config))
213 (my.cnf (mysql-configuration-file config)))
215 (use-modules (ice-9 popen)
217 (let* ((mysqld (string-append #$mysql "/bin/mysqld"))
218 (user (getpwnam "mysql"))
219 (uid (passwd:uid user))
220 (gid (passwd:gid user))
221 (datadir "/var/lib/mysql")
222 (rundir "/run/mysqld"))
224 (chown datadir uid gid)
226 (chown rundir uid gid)
227 ;; Initialize the database when it doesn't exist.
228 (when (not (file-exists? (string-append datadir "/mysql")))
229 (if (string-prefix? "mysql-" (strip-store-file-name #$mysql))
232 (string-append "--defaults-file=" #$my.cnf)
236 ;; XXX: The 'mysql_install_db' script doesn't work directly
237 ;; due to missing 'mkdir' in PATH.
238 (let ((p (open-pipe* OPEN_WRITE mysqld
240 "--defaults-file=" #$my.cnf)
243 ;; Create the system database, as does by 'mysql_install_db'.
244 (display "create database mysql;\n" p)
245 (display "use mysql;\n" p)
248 (call-with-input-file
249 (string-append #$mysql "/share/mysql/" sql)
250 (lambda (in) (dump-port in p))))
251 '("mysql_system_tables.sql"
252 "mysql_performance_tables.sql"
253 "mysql_system_tables_data.sql"
254 "fill_help_tables.sql"))
255 ;; Remove the anonymous user and disable root access from
256 ;; remote machines, as does by 'mysql_secure_installation'.
258 DELETE FROM user WHERE User='';
259 DELETE FROM user WHERE User='root' AND
260 Host NOT IN ('localhost', '127.0.0.1', '::1');
263 (close-pipe p))))))))
265 (define (mysql-shepherd-service config)
266 (list (shepherd-service
268 (documentation "Run the MySQL server.")
269 (start (let ((mysql (mysql-configuration-mysql config))
270 (my.cnf (mysql-configuration-file config)))
271 #~(make-forkexec-constructor
272 (list (string-append #$mysql "/bin/mysqld")
273 (string-append "--defaults-file=" #$my.cnf))
274 #:user "mysql" #:group "mysql")))
275 (stop #~(make-kill-destructor)))))
277 (define mysql-service-type
281 (list (service-extension account-service-type
282 (const %mysql-accounts))
283 (service-extension activation-service-type
285 (service-extension shepherd-root-service-type
286 mysql-shepherd-service)))))
288 (define* (mysql-service #:key (config (mysql-configuration)))
289 "Return a service that runs @command{mysqld}, the MySQL or MariaDB
292 The optional @var{config} argument specifies the configuration for
293 @command{mysqld}, which should be a @code{<mysql-configuration>} object."
294 (service mysql-service-type config))
301 (define-record-type* <redis-configuration>
302 redis-configuration make-redis-configuration
304 (redis redis-configuration-redis ;<package>
306 (bind redis-configuration-bind
307 (default "127.0.0.1"))
308 (port redis-configuration-port
310 (working-directory redis-configuration-working-directory
311 (default "/var/lib/redis"))
312 (config-file redis-configuration-config-file
315 (define (default-redis.conf bind port working-directory)
316 (mixed-text-file "redis.conf"
318 "port " (number->string port) "\n"
319 "dir " working-directory "\n"
322 (define %redis-accounts
323 (list (user-group (name "redis") (system? #t))
328 (comment "Redis server user")
329 (home-directory "/var/empty")
330 (shell (file-append shadow "/sbin/nologin")))))
332 (define redis-activation
334 (($ <redis-configuration> redis bind port working-directory config-file)
336 (use-modules (guix build utils)
339 (let ((user (getpwnam "redis")))
340 (mkdir-p #$working-directory)
341 (chown #$working-directory (passwd:uid user) (passwd:gid user)))))))
343 (define redis-shepherd-service
345 (($ <redis-configuration> redis bind port working-directory config-file)
348 (default-redis.conf bind port working-directory))))
349 (list (shepherd-service
351 (documentation "Run the Redis daemon.")
352 (requirement '(user-processes syslogd))
353 (start #~(make-forkexec-constructor
354 '(#$(file-append redis "/bin/redis-server")
358 (stop #~(make-kill-destructor))))))))
360 (define redis-service-type
361 (service-type (name 'redis)
363 (list (service-extension shepherd-root-service-type
364 redis-shepherd-service)
365 (service-extension activation-service-type
367 (service-extension account-service-type
368 (const %redis-accounts))))))