gnu: datamash: Update to 1.1.1.
[jackhill/guix/guix.git] / gnu / services / databases.scm
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 ;;;
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
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?
33 postgresql-service
34 postgresql-service-type
35
36 mysql-service
37 mysql-service-type
38 mysql-configuration
39 mysql-configuration?
40
41 redis-configuration
42 redis-configuration?
43 redis-service-type))
44
45 ;;; Commentary:
46 ;;;
47 ;;; Database services.
48 ;;;
49 ;;; Code:
50
51 (define-record-type* <postgresql-configuration>
52 postgresql-configuration make-postgresql-configuration
53 postgresql-configuration?
54 (postgresql postgresql-configuration-postgresql ;<package>
55 (default postgresql))
56 (port postgresql-configuration-port
57 (default 5432))
58 (locale postgresql-configuration-locale
59 (default "en_US.utf8"))
60 (config-file postgresql-configuration-file)
61 (data-directory postgresql-configuration-data-directory))
62
63 (define %default-postgres-hba
64 (plain-file "pg_hba.conf"
65 "
66 local all all trust
67 host all all 127.0.0.1/32 trust
68 host all all ::1/128 trust"))
69
70 (define %default-postgres-ident
71 (plain-file "pg_ident.conf"
72 "# MAPNAME SYSTEM-USERNAME PG-USERNAME"))
73
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"))
79
80 (define %postgresql-accounts
81 (list (user-group (name "postgres") (system? #t))
82 (user-account
83 (name "postgres")
84 (group "postgres")
85 (system? #t)
86 (comment "PostgreSQL server user")
87 (home-directory "/var/empty")
88 (shell (file-append shadow "/sbin/nologin")))))
89
90 (define postgresql-activation
91 (match-lambda
92 (($ <postgresql-configuration> postgresql port locale config-file data-directory)
93 #~(begin
94 (use-modules (guix build utils)
95 (ice-9 match))
96
97 (let ((user (getpwnam "postgres"))
98 (initdb (string-append #$postgresql "/bin/initdb"))
99 (initdb-args
100 (append
101 (if #$locale
102 (list (string-append "--locale=" #$locale))
103 '()))))
104 ;; Create db state directory.
105 (mkdir-p #$data-directory)
106 (chown #$data-directory (passwd:uid user) (passwd:gid user))
107
108 ;; Drop privileges and init state directory in a new
109 ;; process. Wait for it to finish before proceeding.
110 (match (primitive-fork)
111 (0
112 ;; Exit with a non-zero status code if an exception is thrown.
113 (dynamic-wind
114 (const #t)
115 (lambda ()
116 (setgid (passwd:gid user))
117 (setuid (passwd:uid user))
118 (primitive-exit
119 (apply system*
120 initdb
121 "-D"
122 #$data-directory
123 initdb-args)))
124 (lambda ()
125 (primitive-exit 1))))
126 (pid (waitpid pid))))))))
127
128 (define postgresql-shepherd-service
129 (match-lambda
130 (($ <postgresql-configuration> postgresql port locale config-file data-directory)
131 (let ((start-script
132 ;; Wrapper script that switches to the 'postgres' user before
133 ;; launching daemon.
134 (program-file "start-postgres"
135 #~(let ((user (getpwnam "postgres"))
136 (postgres (string-append #$postgresql
137 "/bin/postgres")))
138 (setgid (passwd:gid user))
139 (setuid (passwd:uid user))
140 (system* postgres
141 (string-append "--config-file="
142 #$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))))))))
151
152 (define postgresql-service-type
153 (service-type (name 'postgresql)
154 (extensions
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))))))
161
162 (define* (postgresql-service #:key (postgresql postgresql)
163 (port 5432)
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.
168
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)
174 (port port)
175 (locale locale)
176 (config-file config-file)
177 (data-directory data-directory))))
178
179 \f
180 ;;;
181 ;;; MySQL.
182 ;;;
183
184 (define-record-type* <mysql-configuration>
185 mysql-configuration make-mysql-configuration
186 mysql-configuration?
187 (mysql mysql-configuration-mysql (default mariadb))
188 (port mysql-configuration-port (default 3306)))
189
190 (define %mysql-accounts
191 (list (user-group
192 (name "mysql")
193 (system? #t))
194 (user-account
195 (name "mysql")
196 (group "mysql")
197 (system? #t)
198 (home-directory "/var/empty")
199 (shell (file-append shadow "/sbin/nologin")))))
200
201 (define mysql-configuration-file
202 (match-lambda
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) "
208 "))))
209
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)))
214 #~(begin
215 (use-modules (ice-9 popen)
216 (guix build utils))
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"))
223 (mkdir-p datadir)
224 (chown datadir uid gid)
225 (mkdir-p rundir)
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))
230 ;; For MySQL.
231 (system* mysqld
232 (string-append "--defaults-file=" #$my.cnf)
233 "--initialize"
234 "--user=mysql")
235 ;; For MariaDB.
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
239 (string-append
240 "--defaults-file=" #$my.cnf)
241 "--bootstrap"
242 "--user=mysql")))
243 ;; Create the system database, as does by 'mysql_install_db'.
244 (display "create database mysql;\n" p)
245 (display "use mysql;\n" p)
246 (for-each
247 (lambda (sql)
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'.
257 (display "
258 DELETE FROM user WHERE User='';
259 DELETE FROM user WHERE User='root' AND
260 Host NOT IN ('localhost', '127.0.0.1', '::1');
261 FLUSH PRIVILEGES;
262 " p)
263 (close-pipe p))))))))
264
265 (define (mysql-shepherd-service config)
266 (list (shepherd-service
267 (provision '(mysql))
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)))))
276
277 (define mysql-service-type
278 (service-type
279 (name 'mysql)
280 (extensions
281 (list (service-extension account-service-type
282 (const %mysql-accounts))
283 (service-extension activation-service-type
284 %mysql-activation)
285 (service-extension shepherd-root-service-type
286 mysql-shepherd-service)))))
287
288 (define* (mysql-service #:key (config (mysql-configuration)))
289 "Return a service that runs @command{mysqld}, the MySQL or MariaDB
290 database server.
291
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))
295
296 \f
297 ;;;
298 ;;; Redis
299 ;;;
300
301 (define-record-type* <redis-configuration>
302 redis-configuration make-redis-configuration
303 redis-configuration?
304 (redis redis-configuration-redis ;<package>
305 (default redis))
306 (bind redis-configuration-bind
307 (default "127.0.0.1"))
308 (port redis-configuration-port
309 (default 6379))
310 (working-directory redis-configuration-working-directory
311 (default "/var/lib/redis"))
312 (config-file redis-configuration-config-file
313 (default #f)))
314
315 (define (default-redis.conf bind port working-directory)
316 (mixed-text-file "redis.conf"
317 "bind " bind "\n"
318 "port " (number->string port) "\n"
319 "dir " working-directory "\n"
320 "daemonize no\n"))
321
322 (define %redis-accounts
323 (list (user-group (name "redis") (system? #t))
324 (user-account
325 (name "redis")
326 (group "redis")
327 (system? #t)
328 (comment "Redis server user")
329 (home-directory "/var/empty")
330 (shell (file-append shadow "/sbin/nologin")))))
331
332 (define redis-activation
333 (match-lambda
334 (($ <redis-configuration> redis bind port working-directory config-file)
335 #~(begin
336 (use-modules (guix build utils)
337 (ice-9 match))
338
339 (let ((user (getpwnam "redis")))
340 (mkdir-p #$working-directory)
341 (chown #$working-directory (passwd:uid user) (passwd:gid user)))))))
342
343 (define redis-shepherd-service
344 (match-lambda
345 (($ <redis-configuration> redis bind port working-directory config-file)
346 (let ((config-file
347 (or config-file
348 (default-redis.conf bind port working-directory))))
349 (list (shepherd-service
350 (provision '(redis))
351 (documentation "Run the Redis daemon.")
352 (requirement '(user-processes syslogd))
353 (start #~(make-forkexec-constructor
354 '(#$(file-append redis "/bin/redis-server")
355 #$config-file)
356 #:user "redis"
357 #:group "redis"))
358 (stop #~(make-kill-destructor))))))))
359
360 (define redis-service-type
361 (service-type (name 'redis)
362 (extensions
363 (list (service-extension shepherd-root-service-type
364 redis-shepherd-service)
365 (service-extension activation-service-type
366 redis-activation)
367 (service-extension account-service-type
368 (const %redis-accounts))))))