[ pg_ctl ] Review Request for Adding Pre-check User Script Feature

Started by 김명준over 1 year ago4 messages
#1김명준
rayjun0525@gmail.com
1 attachment(s)

Hello,

I have been considering adding a user script that performs pre-checks
before executing the start, stop, and restart operations in pg_ctl. I
believe it is necessary for pg_ctl to support an extension that can prevent
various issues that might occur when using start and stop. To this end, I
have sought a way for users to define and use their own logic. The existing
behavior remains unchanged, and the feature can be used optionally when
needed.

The verification of the code was carried out using the methods described
below, and I would like to request additional opinions or feedback. Tests
were conducted using make check and through direct testing under various
scenarios. As this is my first contribution, there might be aspects I
missed or incorrectly designed.

I would appreciate it if you could review this.

Thank you.

Myoungjun Kim / South Korea

Attachments:

pg_ctl_user_script.patchapplication/octet-stream; name=pg_ctl_user_script.patchDownload
diff --git a/src/bin/pg_ctl/pg_ctl.c b/src/bin/pg_ctl/pg_ctl.c
index 57ed8c8e29..c9332ba28c 100644
--- a/src/bin/pg_ctl/pg_ctl.c
+++ b/src/bin/pg_ctl/pg_ctl.c
@@ -101,6 +101,9 @@ static char logrotate_file[MAXPGPATH];
 
 static volatile pid_t postmasterPID = -1;
 
+static char *pre_check_start_script = NULL;	/*added*/
+static char *pre_check_stop_script = NULL;	/*added*/
+
 #ifdef WIN32
 static DWORD pgctl_start_type = SERVICE_AUTO_START;
 static SERVICE_STATUS status;
@@ -922,6 +925,18 @@ do_init(void)
 static void
 do_start(void)
 {
+        /*added*/
+        if (pre_check_start_script)
+        {
+                if (system(pre_check_start_script) != 0)
+                {
+                        write_stderr(_("%s: pre-check for start failed, aborting start\n"), progname);
+                        free(pre_check_start_script);
+                        free(pre_check_stop_script);
+                        exit(1);
+                }
+        }
+
 	pid_t		old_pid = 0;
 	pid_t		pm_pid;
 
@@ -1014,6 +1029,18 @@ do_start(void)
 static void
 do_stop(void)
 {
+        /*added*/
+        if (pre_check_stop_script)
+        {
+                if (system(pre_check_stop_script) != 0)
+                {
+                        write_stderr(_("%s: pre-check for stop failed, aborting stop\n"), progname);
+                        free(pre_check_start_script);
+                        free(pre_check_stop_script);
+                        exit(1);
+                }
+        }
+        
 	pid_t		pid;
 
 	pid = get_pgpid(false);
@@ -1082,6 +1109,14 @@ do_restart(void)
 					 progname, pid_file);
 		write_stderr(_("Is server running?\n"));
 		write_stderr(_("trying to start server anyway\n"));
+                /*added*/
+                if (pre_check_start_script && system(pre_check_start_script) != 0)
+                {
+                        write_stderr(_("%s: pre-check script for start failed, aborting start\n"), progname);
+                        free(pre_check_start_script);
+                        free(pre_check_stop_script);
+                        exit(1);
+                }
 		do_start();
 		return;
 	}
@@ -1100,9 +1135,21 @@ do_restart(void)
 
 	if (postmaster_is_alive(pid))
 	{
+                /*added*/
+                if (pre_check_stop_script && system(pre_check_stop_script) != 0)
+                {
+                        write_stderr(_("%s: pre-check script for stop failed, aborting stop\n"), progname);
+                        free(pre_check_start_script);
+                        free(pre_check_stop_script);
+                        exit(1);
+                }
+
 		if (kill(pid, sig) != 0)
 		{
 			write_stderr(_("%s: could not send stop signal (PID: %d): %m\n"), progname, (int) pid);
+                        /*added*/
+                        free(pre_check_start_script);
+                        free(pre_check_stop_script);
 			exit(1);
 		}
 
@@ -1117,6 +1164,9 @@ do_restart(void)
 			if (shutdown_mode == SMART_MODE)
 				write_stderr(_("HINT: The \"-m fast\" option immediately disconnects sessions rather than\n"
 							   "waiting for session-initiated disconnection.\n"));
+                        /*added*/
+                        free(pre_check_start_script);
+                        free(pre_check_stop_script);
 			exit(1);
 		}
 
@@ -1129,6 +1179,14 @@ do_restart(void)
 					 progname, (int) pid);
 		write_stderr(_("starting server anyway\n"));
 	}
+        /*added*/
+        if (pre_check_start_script && system(pre_check_start_script) != 0)
+        {
+                write_stderr(_("%s: pre-check script for start failed, aborting start\n"), progname);
+                free(pre_check_start_script);
+                free(pre_check_stop_script);
+                exit(1);
+        }
 
 	do_start();
 }
@@ -1964,10 +2022,10 @@ do_help(void)
 	printf(_("Usage:\n"));
 	printf(_("  %s init[db]   [-D DATADIR] [-s] [-o OPTIONS]\n"), progname);
 	printf(_("  %s start      [-D DATADIR] [-l FILENAME] [-W] [-t SECS] [-s]\n"
-			 "                    [-o OPTIONS] [-p PATH] [-c]\n"), progname);
-	printf(_("  %s stop       [-D DATADIR] [-m SHUTDOWN-MODE] [-W] [-t SECS] [-s]\n"), progname);
-	printf(_("  %s restart    [-D DATADIR] [-m SHUTDOWN-MODE] [-W] [-t SECS] [-s]\n"
-			 "                    [-o OPTIONS] [-c]\n"), progname);
+                         "                    [-o OPTIONS] [-A SCRIPT] [-p PATH] [-c]\n"), progname);                   /*added*/
+        printf(_("  %s stop       [-D DATADIR] [-m SHUTDOWN-MODE] [-W] [-t SECS] [-s] [-Z SCRIPT] \n"), progname);      /*added*/
+        printf(_("  %s restart    [-D DATADIR] [-m SHUTDOWN-MODE] [-W] [-t SECS] [-s]\n"
+                         "                    [-o OPTIONS] [-A SCRIPT] [-Z SCRIPT] [-c]\n"), progname);                 /*added*/
 	printf(_("  %s reload     [-D DATADIR] [-s]\n"), progname);
 	printf(_("  %s status     [-D DATADIR]\n"), progname);
 	printf(_("  %s promote    [-D DATADIR] [-W] [-t SECS] [-s]\n"), progname);
@@ -2002,7 +2060,9 @@ do_help(void)
 	printf(_("  -o, --options=OPTIONS  command line options to pass to postgres\n"
 			 "                         (PostgreSQL server executable) or initdb\n"));
 	printf(_("  -p PATH-TO-POSTGRES    normally not necessary\n"));
-	printf(_("\nOptions for stop or restart:\n"));
+        printf(_("  -A, --pre-check-start=SCRIPT        pre-cehck user script for start\n"));                   /*added*/
+        printf(_("\nOptions for stop or restart:\n"));
+        printf(_("  -Z, --pre-check-stop=SCRIPT        pre-cehck user script for stop\n"));                     /*added*/
 	printf(_("  -m, --mode=MODE        MODE can be \"smart\", \"fast\", or \"immediate\"\n"));
 
 	printf(_("\nShutdown modes are:\n"));
@@ -2201,6 +2261,8 @@ main(int argc, char **argv)
 		{"core-files", no_argument, NULL, 'c'},
 		{"wait", no_argument, NULL, 'w'},
 		{"no-wait", no_argument, NULL, 'W'},
+                {"pre-check-start", required_argument, NULL, 'A'},		/*added*/
+                {"pre-check-stop", required_argument, NULL, 'Z'},		/*added*/
 		{NULL, 0, NULL, 0}
 	};
 
@@ -2341,6 +2403,12 @@ main(int argc, char **argv)
 			case 'c':
 				allow_core_files = true;
 				break;
+                        case 'A': /*added*/
+                                pre_check_start_script = strdup(optarg);
+                                break;
+                        case 'Z': /*added*/
+                                pre_check_stop_script = strdup(optarg);
+                                break;
 			default:
 				/* getopt_long already issued a suitable error message */
 				do_advice();
@@ -2499,6 +2567,8 @@ main(int argc, char **argv)
 		default:
 			break;
 	}
+        free(pre_check_start_script);           /*added*/
+        free(pre_check_stop_script);            /*added*/
 
 	exit(0);
 }
diff --git a/src/bin/pg_ctl/t/001_start_stop.pl b/src/bin/pg_ctl/t/001_start_stop.pl
index 6a5ef00743..4bc62e456c 100644
--- a/src/bin/pg_ctl/t/001_start_stop.pl
+++ b/src/bin/pg_ctl/t/001_start_stop.pl
@@ -40,11 +40,29 @@ else
 	print $conf "listen_addresses = '127.0.0.1'\n";
 }
 close $conf;
+
+# Set up scripts for pre-check
+my $pre_check_start_script = "$tempdir/pre_check_start.sh";
+my $pre_check_stop_script = "$tempdir/pre_check_stop.sh";
++
+# Create pre_check_start_script
+open(my $fh, '>', $pre_check_start_script) or die "Could not open file '$pre_check_start_script' $!";
+print $fh "#!/bin/sh\nexit 0\n";
+close $fh;
+chmod 0755, $pre_check_start_script;
++
+# Create pre_check_stop_script
+open($fh, '>', $pre_check_stop_script) or die "Could not open file '$pre_check_stop_script' $!";
+print $fh "#!/bin/sh\nexit 0\n";
+close $fh;
+chmod 0755, $pre_check_stop_script;
+
 my $ctlcmd = [
 	'pg_ctl', 'start', '-D', "$tempdir/data", '-l',
-	"$PostgreSQL::Test::Utils::log_path/001_start_stop_server.log"
+	"$PostgreSQL::Test::Utils::log_path/001_start_stop_server.log",
+        '-A', $pre_check_start_script, '-Z', $pre_check_stop_script
 ];
-command_like($ctlcmd, qr/done.*server started/s, 'pg_ctl start');
+command_like($ctlcmd, qr/done.*server started/s, 'pg_ctl start with pre-check scripts');
 
 # sleep here is because Windows builds can't check postmaster.pid exactly,
 # so they may mistake a pre-existing postmaster.pid for one created by the
@@ -53,7 +71,7 @@ command_like($ctlcmd, qr/done.*server started/s, 'pg_ctl start');
 sleep 3 if ($windows_os);
 command_fails([ 'pg_ctl', 'start', '-D', "$tempdir/data" ],
 	'second pg_ctl start fails');
-command_ok([ 'pg_ctl', 'stop', '-D', "$tempdir/data" ], 'pg_ctl stop');
+command_ok([ 'pg_ctl', 'stop', '-D', "$tempdir/data", '-Z', $pre_check_stop_script ], 'pg_ctl stop with pre-check script');
 command_fails([ 'pg_ctl', 'stop', '-D', "$tempdir/data" ],
 	'second pg_ctl stop fails');
 
@@ -61,8 +79,8 @@ command_fails([ 'pg_ctl', 'stop', '-D', "$tempdir/data" ],
 # Windows but we still want to do the restart test.
 my $logFileName = "$tempdir/data/perm-test-600.log";
 
-command_ok([ 'pg_ctl', 'restart', '-D', "$tempdir/data", '-l', $logFileName ],
-	'pg_ctl restart with server not running');
+command_ok([ 'pg_ctl', 'restart', '-D', "$tempdir/data", '-l', $logFileName, '-A', $pre_check_start_script, '-Z', $pre_check_stop_script ],
+        'pg_ctl restart with server not running and pre-check scripts');
 
 # Permissions on log file should be default
 SKIP:
@@ -89,16 +107,16 @@ SKIP:
 	chmod_recursive("$tempdir/data", 0750, 0640);
 
 	command_ok(
-		[ 'pg_ctl', 'start', '-D', "$tempdir/data", '-l', $logFileName ],
+                [ 'pg_ctl', 'start', '-D', "$tempdir/data", '-l', $logFileName, '-A', $pre_check_start_script, '-Z', $pre_check_stop_script ],
 		'start server to check group permissions');
 
 	ok(-f $logFileName);
 	ok(check_mode_recursive("$tempdir/data", 0750, 0640));
 }
 
-command_ok([ 'pg_ctl', 'restart', '-D', "$tempdir/data" ],
-	'pg_ctl restart with server running');
+command_ok([ 'pg_ctl', 'restart', '-D', "$tempdir/data", '-A', $pre_check_start_script, '-Z', $pre_check_stop_script ],
+        'pg_ctl restart with server running and pre-check scripts');
 
-system_or_bail 'pg_ctl', 'stop', '-D', "$tempdir/data";
+system_or_bail 'pg_ctl', 'stop', '-D', "$tempdir/data", '-Z', $pre_check_stop_script;
 
 done_testing();
#2Zaid Shabbir
zaidshabbir@gmail.com
In reply to: 김명준 (#1)
Re: [ pg_ctl ] Review Request for Adding Pre-check User Script Feature

Hello,

Can you briefly explain what’s the issues you are going through the patch?

On Tue, 16 Jul 2024 at 11:40 AM, 김명준 <rayjun0525@gmail.com> wrote:

Show quoted text

Hello,

I have been considering adding a user script that performs pre-checks
before executing the start, stop, and restart operations in pg_ctl. I
believe it is necessary for pg_ctl to support an extension that can prevent
various issues that might occur when using start and stop. To this end, I
have sought a way for users to define and use their own logic. The existing
behavior remains unchanged, and the feature can be used optionally when
needed.

The verification of the code was carried out using the methods described
below, and I would like to request additional opinions or feedback. Tests
were conducted using make check and through direct testing under various
scenarios. As this is my first contribution, there might be aspects I
missed or incorrectly designed.

I would appreciate it if you could review this.

Thank you.

Myoungjun Kim / South Korea

#3Kisoon Kwon
moxie2ks@gmail.com
In reply to: 김명준 (#1)
Re: [ pg_ctl ] Review Request for Adding Pre-check User Script Feature

Hi,

0. For more understanding, can you give me an example about your patch?
1. Instead of using chmod itself, it would be better to
use chmod_recursive().
2. It needs to follow the invent convention - it includes 4 spaces now.

Thank you,

Kisoon Kwon

2024년 7월 16일 (화) 오후 3:40, 김명준 <rayjun0525@gmail.com>님이 작성:

Show quoted text

Hello,

I have been considering adding a user script that performs pre-checks
before executing the start, stop, and restart operations in pg_ctl. I
believe it is necessary for pg_ctl to support an extension that can prevent
various issues that might occur when using start and stop. To this end, I
have sought a way for users to define and use their own logic. The existing
behavior remains unchanged, and the feature can be used optionally when
needed.

The verification of the code was carried out using the methods described
below, and I would like to request additional opinions or feedback. Tests
were conducted using make check and through direct testing under various
scenarios. As this is my first contribution, there might be aspects I
missed or incorrectly designed.

I would appreciate it if you could review this.

Thank you.

Myoungjun Kim / South Korea

#4김명준
rayjun0525@gmail.com
In reply to: Kisoon Kwon (#3)
Re: [ pg_ctl ] Review Request for Adding Pre-check User Script Feature

Hello,

Thank you for your response.

0. Here is an example of what I intended.
What I intended is to add pre-check tasks before executing pg_ctl start,
stop, and restart using the -A and -Z options.

=========================================
[test@test]$ cat true.sh
#!/bin/bash
echo 'true'
exit 0
=========================================
[test@test]$ cat false.sh
#!/bin/bash
echo 'false'
exit 1
=========================================
[test@test]$ pg_ctl start -A false.sh
false
pg_ctl: pre-check for start failed, aborting start
=========================================
[test@test]$ pg_ctl start -A true.sh
true
waiting for server to start....2024-07-19 00:16:22.768 UTC [167505] LOG:
starting PostgreSQL 18devel on
~
~
done
server started
=========================================
[test@test]$ pg_ctl stop -Z false.sh
false
pg_ctl: pre-check for stop failed, aborting stop
=========================================
[test@test]$ pg_ctl stop -Z true.sh
true
waiting for server to shut down....2024-07-19 00:21:06.282 UTC [167515]
LOG: received fast shutdown request
~
~
done
server stopped
=========================================
[test@test]$ pg_ctl restart -A false.sh -Z false.sh
false
pg_ctl: pre-check script for stop failed, aborting stop
=========================================
[test@test]$ pg_ctl restart -A false.sh -Z true.sh
true
waiting for server to shut down...2024-07-19 00:24:39.640 UTC [167530] LOG:
received fast shutdown request
~
~
done
server stopped
false
pg_ctl: pre-check script for start failed, aborting start
=========================================

1. I plan to change it to chmod_recursive() instead of using chmod itself.
2. I will modify it to use 4 spaces instead of a tab.

Thank you,

Myoungjun Kim

2024년 7월 16일 (화) 오후 5:26, Kisoon Kwon <moxie2ks@gmail.com>님이 작성:

Show quoted text

Hi,

0. For more understanding, can you give me an example about your patch?
1. Instead of using chmod itself, it would be better to
use chmod_recursive().
2. It needs to follow the invent convention - it includes 4 spaces now.

Thank you,

Kisoon Kwon

2024년 7월 16일 (화) 오후 3:40, 김명준 <rayjun0525@gmail.com>님이 작성:

Hello,

I have been considering adding a user script that performs pre-checks
before executing the start, stop, and restart operations in pg_ctl. I
believe it is necessary for pg_ctl to support an extension that can prevent
various issues that might occur when using start and stop. To this end, I
have sought a way for users to define and use their own logic. The existing
behavior remains unchanged, and the feature can be used optionally when
needed.

The verification of the code was carried out using the methods described
below, and I would like to request additional opinions or feedback. Tests
were conducted using make check and through direct testing under various
scenarios. As this is my first contribution, there might be aspects I
missed or incorrectly designed.

I would appreciate it if you could review this.

Thank you.

Myoungjun Kim / South Korea