Subject: bug#49155: closed (Re: [bug#49155] [PATCH] gnu: mysql: Support custom data dir.)

From: [email-masked] (GNU bug Tracking System)
Date: Sun, 20 Nov 2022 16:57:02 +0000

Your bug report

#49155: [PATCH] gnu: mysql: Support custom data dir.

which was filed against the guix-patches package, has been closed.

The explanation is attached below, along with your original report.
If you require more details, please reply to [email-masked].

-- 
49155: https://debbugs.gnu.org/cgi/bugreport.cgi?bug=49155
GNU Bug Tracking System
Contact [email-masked] with problems

Subject: Re: [bug#49155] [PATCH] gnu: mysql: Support custom data dir.
From: Marius Bakke <[email-masked]>
To: Aljosha Papsch <[email-masked]>, [email-masked]
Cc: Ellen Papsch <[email-masked]>
Date: Sun, 20 Nov 2022 17:56:12 +0100
List-Id: <debbugs-submit.debbugs.gnu.org>

Hi!

Thanks for the patch, and sorry for the slooow response...

Aljosha Papsch <[email-masked]> skriver:

> From: Ellen Papsch <[email-masked]>
>
> * gnu/services/databases.scm (mysql-configuration): Add datadir property.
> * gnu/services/databases.scm (mysql-configuration-file): Replace hard coded
> data dir with property from config.
> * gnu/services/databases.scm (%mysql-activation): Remove activation, it runs
> before PID 1. The data dir may reside on a file system not mounted at this
> time.
> * gnu/services/databases.scm (mysql-install-shepherd-service): Create service
> which replaces the activation. Provide mysql-install.
> * gnu/services/databases.scm (mysql-shepherd-service): Move invocation of
> mysqld to mysql-start program-file, because the invocation gotten more
> complex. Require mysql-install.
> * gnu/services/databases.scm (mysql-start): Invoke mysqld only if a lock file
> appears.
> * gnu/services/databases.scm (mysql-shepherd-services): Prepend the install
> service before the normal service.
> * gnu/services/databases.scm (mysql-upgrade-wrapper): Increase timeout to
>   20s to let the mysql install procedure finish.

I have finally applied this patch, along with a follow-up commit to make
the mysql-upgrade service also work with a custom datadir.

I also ended up dropping the 'mysql-install' service in favor of a
mysqld wrapper script that calls the mysql-install script at startup
when the "mysql" DB is missing.

Pushed in c7b266fdf2754c139803c156677bf2828c78d072.

Subject: [PATCH] gnu: mysql: Support custom data dir.
From: Aljosha Papsch <[email-masked]>
To: [email-masked]
Cc: Ellen Papsch <[email-masked]>
Date: Mon, 21 Jun 2021 15:27:52 +0200
List-Id: <debbugs-submit.debbugs.gnu.org>

From: Ellen Papsch <[email-masked]>

* gnu/services/databases.scm (mysql-configuration): Add datadir property.
* gnu/services/databases.scm (mysql-configuration-file): Replace hard coded
data dir with property from config.
* gnu/services/databases.scm (%mysql-activation): Remove activation, it runs
before PID 1. The data dir may reside on a file system not mounted at this
time.
* gnu/services/databases.scm (mysql-install-shepherd-service): Create service
which replaces the activation. Provide mysql-install.
* gnu/services/databases.scm (mysql-shepherd-service): Move invocation of
mysqld to mysql-start program-file, because the invocation gotten more
complex. Require mysql-install.
* gnu/services/databases.scm (mysql-start): Invoke mysqld only if a lock file
appears.
* gnu/services/databases.scm (mysql-shepherd-services): Prepend the install
service before the normal service.
* gnu/services/databases.scm (mysql-upgrade-wrapper): Increase timeout to
  20s to let the mysql install procedure finish.
---
 gnu/services/databases.scm | 187 +++++++++++++++++++++++--------------
 1 file changed, 119 insertions(+), 68 deletions(-)

diff --git a/gnu/services/databases.scm b/gnu/services/databases.scm
index eba88cdb68..f8f78619d3 100644
--- a/gnu/services/databases.scm
+++ b/gnu/services/databases.scm
@@ -8,6 +8,7 @@
 ;;; Copyright © 2019 Robert Vollmert <[email-masked]>
 ;;; Copyright © 2020 Marius Bakke <[email-masked]>
 ;;; Copyright © 2021 David Larsson <[email-masked]>
+;;; Copyright © 2021 Aljosha Papsch <[email-masked]>
 ;;;
 ;;; This file is part of GNU Guix.
 ;;;
@@ -527,6 +528,7 @@ created after the PostgreSQL database is started.")))
   (bind-address mysql-configuration-bind-address (default "127.0.0.1"))
   (port mysql-configuration-port (default 3306))
   (socket mysql-configuration-socket (default "/run/mysqld/mysqld.sock"))
+  (datadir mysql-configuration-datadir (default "/var/lib/mysql"))
   (extra-content mysql-configuration-extra-content (default ""))
   (extra-environment mysql-configuration-extra-environment (default #~'()))
   (auto-upgrade? mysql-configuration-auto-upgrade? (default #t)))
@@ -544,85 +546,133 @@ created after the PostgreSQL database is started.")))
 
 (define mysql-configuration-file
   (match-lambda
-    (($ <mysql-configuration> mysql bind-address port socket extra-content)
+    (($ <mysql-configuration> mysql bind-address port socket datadir extra-content)
      (mixed-text-file "my.cnf" "[mysqld]
-datadir=/var/lib/mysql
+datadir=" datadir "
 socket=" socket "
 bind-address=" bind-address "
 port=" (number->string port) "
 " extra-content "
 "))))
 
-(define (%mysql-activation config)
-  "Return an activation gexp for the MySQL or MariaDB database server."
-  (let ((mysql  (mysql-configuration-mysql config))
-        (my.cnf (mysql-configuration-file config)))
-    #~(begin
-        (use-modules (ice-9 popen)
-                     (guix build utils))
-        (let* ((mysqld  (string-append #$mysql "/bin/mysqld"))
-               (user    (getpwnam "mysql"))
-               (uid     (passwd:uid user))
-               (gid     (passwd:gid user))
-               (datadir "/var/lib/mysql")
-               (rundir  "/run/mysqld"))
-          (mkdir-p datadir)
-          (chown datadir uid gid)
-          (mkdir-p rundir)
-          (chown rundir uid gid)
-          ;; Initialize the database when it doesn't exist.
-          (when (not (file-exists? (string-append datadir "/mysql")))
-            (if (string-prefix? "mysql-" (strip-store-file-name #$mysql))
-                ;; For MySQL.
-                (system* mysqld
-                         (string-append "--defaults-file=" #$my.cnf)
-                         "--initialize"
-                         "--user=mysql")
-                ;; For MariaDB.
-                ;; XXX: The 'mysql_install_db' script doesn't work directly
-                ;;      due to missing 'mkdir' in PATH.
-                (let ((p (open-pipe* OPEN_WRITE mysqld
-                                     (string-append
-                                      "--defaults-file=" #$my.cnf)
-                                     "--bootstrap"
-                                     "--user=mysql")))
-                  ;; Create the system database, as does by 'mysql_install_db'.
-                  (display "create database mysql;\n" p)
-                  (display "use mysql;\n" p)
-                  (for-each
-                   (lambda (sql)
-                     (call-with-input-file
-                         (string-append #$mysql:lib "/share/mysql/" sql)
-                       (lambda (in) (dump-port in p))))
-                   '("mysql_system_tables.sql"
-                     "mysql_performance_tables.sql"
-                     "mysql_system_tables_data.sql"
-                     "fill_help_tables.sql"))
-                  ;; Remove the anonymous user and disable root access from
-                  ;; remote machines, as does by 'mysql_secure_installation'.
-                  (display "
-DELETE FROM user WHERE User='';
-DELETE FROM user WHERE User='root' AND
-  Host NOT IN  ('localhost', '127.0.0.1', '::1');
-FLUSH PRIVILEGES;
-" p)
-                  (close-pipe p))))))))
+(define (mysql-with-install-lock)
+  "Return a loop function which evals thunk when the install is locked."
+  #~(lambda (thunk)
+      (let loop ((i 0))
+        (let ((timeout   10)
+              (lock-stat (stat "/var/lib/mysql.lock" #f)))
+          (if (and (not (eq? lock-stat #f))
+                   (eq? (stat:type lock-stat) 'regular))
+              (apply thunk '())
+              (if (< i timeout)
+                  (begin
+                    (sleep 1)
+                    (loop (+ 1 i)))
+                  (throw 'timeout-error
+                         "MySQL installation not locked in time!")))))))
+
+(define (mysql-start config)
+  "Start mysqld if install lock file appears"
+  (program-file
+   "mysql-start"
+   (let ((mysql     (mysql-configuration-mysql config))
+         (my.cnf    (mysql-configuration-file config)))
+     #~(let ((mysqld (string-append #$mysql "/bin/mysqld"))
+             (with-lock #$(mysql-with-install-lock)))
+         (with-lock (lambda ()
+                      (execl mysqld mysqld
+                             (string-append "--defaults-file=" #$my.cnf))))))))
 
 (define (mysql-shepherd-service config)
   (list (shepherd-service
          (provision '(mysql))
+         (requirement '(mysql-install))
          (documentation "Run the MySQL server.")
-         (start (let ((mysql  (mysql-configuration-mysql config))
+         (start (let ((mysql (mysql-configuration-mysql config))
                       (extra-env (mysql-configuration-extra-environment config))
                       (my.cnf (mysql-configuration-file config)))
                   #~(make-forkexec-constructor
-                     (list (string-append #$mysql "/bin/mysqld")
-                           (string-append "--defaults-file=" #$my.cnf))
-                           #:user "mysql" #:group "mysql"
-                           #:log-file "/var/log/mysqld.log"
-                           #:environment-variables #$extra-env)))
+                     (list #$(mysql-start config))
+                     #:user "mysql" #:group "mysql"
+                     #:log-file "/var/log/mysqld.log"
+                     #:environment-variables #$extra-env)))
          (stop #~(make-kill-destructor)))))
 
+(define (mysql-install config)
+  "Install MySQL system database and secure the installation."
+  (let ((mysql   (mysql-configuration-mysql config))
+        (my.cnf  (mysql-configuration-file config))
+        (datadir (mysql-configuration-datadir config))
+        (extra-env (mysql-configuration-extra-environment config)))
+    (program-file
+     "mysql-install"
+     (with-imported-modules (source-module-closure
+                             '((ice-9 popen)
+                               (guix build utils)))
+       #~(begin
+           (use-modules (ice-9 popen)
+                        (guix build utils))
+           (let* ((mysqld  (string-append #$mysql "/bin/mysqld"))
+                  (user    (getpwnam "mysql"))
+                  (uid     (passwd:uid user))
+                  (gid     (passwd:gid user))
+                  (datadir #$datadir)
+                  (rundir  "/run/mysqld"))
+             (mkdir-p datadir)
+             (chown datadir uid gid)
+             (mkdir-p rundir)
+             (chown rundir uid gid)
+             ;; Initialize the database when it doesn't exist.
+             (when (not (file-exists? (string-append datadir "/mysql")))
+               (if (string-prefix? "mysql-" (strip-store-file-name #$mysql))
+                   ;; For MySQL.
+                   (system* mysqld
+                            (string-append "--defaults-file=" #$my.cnf)
+                            "--initialize"
+                            "--user=mysql")
+                   ;; For MariaDB.
+                   ;; XXX: The 'mysql_install_db' script doesn't work directly
+                   ;;      due to missing 'mkdir' in PATH.
+                   (let ((p (open-pipe* OPEN_WRITE mysqld
+                                        (string-append
+                                         "--defaults-file=" #$my.cnf)
+                                        "--bootstrap"
+                                        "--user=mysql")))
+                     ;; Create the system database, as does by 'mysql_install_db'.
+                     (display "create database mysql;\n" p)
+                     (display "use mysql;\n" p)
+                     (for-each
+                      (lambda (sql)
+                        (call-with-input-file
+                            (string-append #$mysql:lib "/share/mysql/" sql)
+                          (lambda (in) (dump-port in p))))
+                      '("mysql_system_tables.sql"
+                        "mysql_performance_tables.sql"
+                        "mysql_system_tables_data.sql"
+                        "fill_help_tables.sql"))
+                     ;; Remove the anonymous user and disable root access from
+                     ;; remote machines, as does by 'mysql_secure_installation'.
+                     (display "
+DELETE FROM user WHERE User='';
+DELETE FROM user WHERE User='root' AND
+  Host NOT IN  ('localhost', '127.0.0.1', '::1');
+FLUSH PRIVILEGES;
+" p)
+                     (close-pipe p))))
+             (call-with-output-file "/var/lib/mysql.lock"
+               (lambda (p)
+                 (write #t p)))))))))
+
+(define (mysql-install-shepherd-service config)
+  (list (shepherd-service
+         (provision '(mysql-install))
+         (requirement '(file-systems))
+         (one-shot? #t)
+         (documentation "Install MySQL system database and secure installation.")
+         (start #~(make-forkexec-constructor
+                   (list #$(mysql-install config))
+                   #:log-file "/var/log/mysqld-install.log")))))
+
 (define (mysql-upgrade-wrapper mysql socket-file)
   ;; The MySQL socket and PID file may appear before the server is ready to
   ;; accept connections.  Ensure the socket is responsive before attempting
@@ -631,7 +681,7 @@ FLUSH PRIVILEGES;
    "mysql-upgrade-wrapper"
    #~(begin
        (let ((mysql-upgrade #$(file-append mysql "/bin/mysql_upgrade"))
-             (timeout 10))
+             (timeout 20))
          (begin
            (let loop ((i 0))
              (catch 'system-error
@@ -663,11 +713,14 @@ FLUSH PRIVILEGES;
                      (list #$(mysql-upgrade-wrapper mysql socket))
                      #:user "mysql" #:group "mysql"))))))
 
+
 (define (mysql-shepherd-services config)
-  (if (mysql-configuration-auto-upgrade? config)
-      (append (mysql-shepherd-service config)
+  (let ((min-services (append (mysql-install-shepherd-service config)
+                              (mysql-shepherd-service config))))
+    (if (mysql-configuration-auto-upgrade? config)
+      (append min-services
               (mysql-upgrade-shepherd-service config))
-      (mysql-shepherd-service config)))
+      min-services)))
 
 (define mysql-service-type
   (service-type
@@ -675,8 +728,6 @@ FLUSH PRIVILEGES;
    (extensions
     (list (service-extension account-service-type
                              (const %mysql-accounts))
-          (service-extension activation-service-type
-                             %mysql-activation)
           (service-extension shepherd-root-service-type
                              mysql-shepherd-services)))
    (default-value (mysql-configuration))))
-- 
2.32.0