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>
6 ;;; This file is part of GNU Guix.
8 ;;; GNU Guix is free software; you can redistribute it and/or modify it
9 ;;; under the terms of the GNU General Public License as published by
10 ;;; the Free Software Foundation; either version 3 of the License, or (at
11 ;;; your option) any later version.
13 ;;; GNU Guix is distributed in the hope that it will be useful, but
14 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
15 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 ;;; GNU General Public License for more details.
18 ;;; You should have received a copy of the GNU General Public License
19 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
21 (define-module (gnu services databases)
22 #:use-module (gnu services)
23 #:use-module (gnu services shepherd)
24 #:use-module (gnu system shadow)
25 #:use-module (gnu packages admin)
26 #:use-module (gnu packages databases)
27 #:use-module (guix records)
28 #:use-module (guix gexp)
29 #:use-module (ice-9 match)
30 #:export (postgresql-configuration
31 postgresql-configuration?
33 postgresql-service-type
38 mysql-configuration?))
42 ;;; Database services.
46 (define-record-type* <postgresql-configuration>
47 postgresql-configuration make-postgresql-configuration
48 postgresql-configuration?
49 (postgresql postgresql-configuration-postgresql ;<package>
51 (config-file postgresql-configuration-file)
52 (data-directory postgresql-configuration-data-directory))
54 (define %default-postgres-hba
55 (plain-file "pg_hba.conf"
58 host all all 127.0.0.1/32 trust
59 host all all ::1/128 trust"))
61 (define %default-postgres-ident
62 (plain-file "pg_ident.conf"
63 "# MAPNAME SYSTEM-USERNAME PG-USERNAME"))
65 (define %default-postgres-config
66 (mixed-text-file "postgresql.conf"
67 "log_destination = 'syslog'\n"
68 "hba_file = '" %default-postgres-hba "'\n"
69 "ident_file = '" %default-postgres-ident "'\n"))
71 (define %postgresql-accounts
72 (list (user-group (name "postgres") (system? #t))
77 (comment "PostgreSQL server user")
78 (home-directory "/var/empty")
79 (shell (file-append shadow "/sbin/nologin")))))
81 (define postgresql-activation
83 (($ <postgresql-configuration> postgresql config-file data-directory)
85 (use-modules (guix build utils)
88 (let ((user (getpwnam "postgres"))
89 (initdb (string-append #$postgresql "/bin/initdb")))
90 ;; Create db state directory.
91 (mkdir-p #$data-directory)
92 (chown #$data-directory (passwd:uid user) (passwd:gid user))
94 ;; Drop privileges and init state directory in a new
95 ;; process. Wait for it to finish before proceeding.
96 (match (primitive-fork)
98 ;; Exit with a non-zero status code if an exception is thrown.
102 (setgid (passwd:gid user))
103 (setuid (passwd:uid user))
104 (primitive-exit (system* initdb "-D" #$data-directory)))
106 (primitive-exit 1))))
107 (pid (waitpid pid))))))))
109 (define postgresql-shepherd-service
111 (($ <postgresql-configuration> postgresql config-file data-directory)
113 ;; Wrapper script that switches to the 'postgres' user before
115 (program-file "start-postgres"
116 #~(let ((user (getpwnam "postgres"))
117 (postgres (string-append #$postgresql
119 (setgid (passwd:gid user))
120 (setuid (passwd:uid user))
122 (string-append "--config-file="
124 "-D" #$data-directory)))))
125 (list (shepherd-service
126 (provision '(postgres))
127 (documentation "Run the PostgreSQL daemon.")
128 (requirement '(user-processes loopback syslogd))
129 (start #~(make-forkexec-constructor #$start-script))
130 (stop #~(make-kill-destructor))))))))
132 (define postgresql-service-type
133 (service-type (name 'postgresql)
135 (list (service-extension shepherd-root-service-type
136 postgresql-shepherd-service)
137 (service-extension activation-service-type
138 postgresql-activation)
139 (service-extension account-service-type
140 (const %postgresql-accounts))))))
142 (define* (postgresql-service #:key (postgresql postgresql)
143 (config-file %default-postgres-config)
144 (data-directory "/var/lib/postgresql/data"))
145 "Return a service that runs @var{postgresql}, the PostgreSQL database server.
147 The PostgreSQL daemon loads its runtime configuration from @var{config-file}
148 and stores the database cluster in @var{data-directory}."
149 (service postgresql-service-type
150 (postgresql-configuration
151 (postgresql postgresql)
152 (config-file config-file)
153 (data-directory data-directory))))
160 (define-record-type* <mysql-configuration>
161 mysql-configuration make-mysql-configuration
163 (mysql mysql-configuration-mysql (default mariadb))
164 (port mysql-configuration-port (default 3306)))
166 (define %mysql-accounts
174 (home-directory "/var/empty")
175 (shell (file-append shadow "/sbin/nologin")))))
177 (define mysql-configuration-file
179 (($ <mysql-configuration> mysql port)
180 (mixed-text-file "my.cnf" "[mysqld]
181 datadir=/var/lib/mysql
182 socket=/run/mysqld/mysqld.sock
183 port=" (number->string port) "
186 (define (%mysql-activation config)
187 "Return an activation gexp for the MySQL or MariaDB database server."
188 (let ((mysql (mysql-configuration-mysql config))
189 (my.cnf (mysql-configuration-file config)))
191 (use-modules (ice-9 popen)
193 (let* ((mysqld (string-append #$mysql "/bin/mysqld"))
194 (user (getpwnam "mysql"))
195 (uid (passwd:uid user))
196 (gid (passwd:gid user))
197 (datadir "/var/lib/mysql")
198 (rundir "/run/mysqld"))
200 (chown datadir uid gid)
202 (chown rundir uid gid)
203 ;; Initialize the database when it doesn't exist.
204 (when (not (file-exists? (string-append datadir "/mysql")))
205 (if (string-prefix? "mysql-" (strip-store-file-name #$mysql))
208 (string-append "--defaults-file=" #$my.cnf)
212 ;; XXX: The 'mysql_install_db' script doesn't work directly
213 ;; due to missing 'mkdir' in PATH.
214 (let ((p (open-pipe* OPEN_WRITE mysqld
216 "--defaults-file=" #$my.cnf)
219 ;; Create the system database, as does by 'mysql_install_db'.
220 (display "create database mysql;\n" p)
221 (display "use mysql;\n" p)
224 (call-with-input-file
225 (string-append #$mysql "/share/mysql/" sql)
226 (lambda (in) (dump-port in p))))
227 '("mysql_system_tables.sql"
228 "mysql_performance_tables.sql"
229 "mysql_system_tables_data.sql"
230 "fill_help_tables.sql"))
231 ;; Remove the anonymous user and disable root access from
232 ;; remote machines, as does by 'mysql_secure_installation'.
234 DELETE FROM user WHERE User='';
235 DELETE FROM user WHERE User='root' AND
236 Host NOT IN ('localhost', '127.0.0.1', '::1');
239 (close-pipe p))))))))
241 (define (mysql-shepherd-service config)
242 (list (shepherd-service
244 (documentation "Run the MySQL server.")
245 (start (let ((mysql (mysql-configuration-mysql config))
246 (my.cnf (mysql-configuration-file config)))
247 #~(make-forkexec-constructor
248 (list (string-append #$mysql "/bin/mysqld")
249 (string-append "--defaults-file=" #$my.cnf))
250 #:user "mysql" #:group "mysql")))
251 (stop #~(make-kill-destructor)))))
253 (define mysql-service-type
257 (list (service-extension account-service-type
258 (const %mysql-accounts))
259 (service-extension activation-service-type
261 (service-extension shepherd-root-service-type
262 mysql-shepherd-service)))))
264 (define* (mysql-service #:key (config (mysql-configuration)))
265 "Return a service that runs @command{mysqld}, the MySQL or MariaDB
268 The optional @var{config} argument specifies the configuration for
269 @command{mysqld}, which should be a @code{<mysql-configuration>} object."
270 (service mysql-service-type config))