| 1 | #!/sbin/openrc-run |
| 2 | |
| 3 | : ${pg_version:=$(pg_versions get-default)} |
| 4 | |
| 5 | name="PostgreSQL $pg_version" |
| 6 | description="PostgreSQL server" |
| 7 | |
| 8 | extra_started_commands="stop_fast stop_force stop_smart reload reload_force promote" |
| 9 | description_stop_fast="Stop using Fast Shutdown mode (SIGINT)" |
| 10 | description_stop_force="Stop using Immediate Shutdown mode (SIGQUIT)" |
| 11 | description_stop_smart="Stop using Smart Shutdown mode (SIGTERM)" |
| 12 | description_reload="Reload configuration" |
| 13 | description_reload_force="Reload configuration and restart if needed" |
| 14 | description_promote="Promote standby server to master - exit recovery and begin read-write operations" |
| 15 | |
| 16 | extra_stopped_commands="setup" |
| 17 | description_setup="Initialize a new $name cluster" |
| 18 | |
| 19 | : ${user:="postgres"} |
| 20 | : ${group:="postgres"} |
| 21 | |
| 22 | : ${auto_setup:="yes"} |
| 23 | : ${start_timeout:=10} |
| 24 | # nice_timeout, rude_timeout and force_timeout are for backward compatibility. |
| 25 | : ${stop_smart_timeout:=${nice_timeout:-5}} |
| 26 | : ${stop_fast_timeout:=${rude_timeout:-10}} |
| 27 | : ${stop_force_timeout:=${force_timeout:-0}} |
| 28 | |
| 29 | : ${data_dir:="/var/lib/postgresql/$pg_version/data"} |
| 30 | : ${conf_dir:="/etc/postgresql"} |
| 31 | : ${logfile:="/var/log/postgresql/postmaster.log"} |
| 32 | : ${env_vars:=} |
| 33 | : ${pg_opts:=} |
| 34 | : ${port:=5432} |
| 35 | : ${initdb_opts:="-E UTF-8 --locale-provider=icu --icu-locale=en-001-x-icu --data-checksums"} |
| 36 | |
| 37 | command="/usr/libexec/postgresql$pg_version/postgres" |
| 38 | |
| 39 | conffile="$conf_dir/postgresql.conf" |
| 40 | pidfile="$data_dir/postmaster.pid" |
| 41 | start_stop_daemon_args=" |
| 42 | --user $user |
| 43 | --group $group |
| 44 | --pidfile $pidfile |
| 45 | --wait 100" |
| 46 | |
| 47 | depend() { |
| 48 | use net |
| 49 | after firewall |
| 50 | |
| 51 | if [ "$(get_config log_destination)" = "syslog" ]; then |
| 52 | use logger |
| 53 | fi |
| 54 | } |
| 55 | |
| 56 | start_pre() { |
| 57 | check_deprecated_var nice_timeout stop_smart_timeout |
| 58 | check_deprecated_var rude_timeout stop_fast_timeout |
| 59 | check_deprecated_var rude_quit stop_fast_timeout |
| 60 | check_deprecated_var force_timeout stop_force_timeout |
| 61 | check_deprecated_var force_quit stop_force_timeout |
| 62 | check_deprecated_var env_vars 'export NAME=VALUE' |
| 63 | |
| 64 | # For backward compatibility only. |
| 65 | [ "$rude_quit" = no ] && [ "stop_fast_timeout" -eq 10 ] && stop_fast_timeout=0 |
| 66 | [ "$force_quit" = yes ] && [ "$stop_force_timeout" -eq 0 ] && stop_force_timeout=2 |
| 67 | |
| 68 | if [ ! -d "$data_dir/base" ]; then |
| 69 | if yesno "$auto_setup"; then |
| 70 | setup || return 1 |
| 71 | else |
| 72 | eerror "Database not found at: $data_dir" |
| 73 | eerror "Please make sure that 'data_dir' points to the right path." |
| 74 | eerror "You can run '/etc/init.d/postgresql setup' to setup a new database cluster." |
| 75 | return 1 |
| 76 | fi |
| 77 | fi |
| 78 | |
| 79 | # This is mainly for backward compatibility with the former $conf_dir default value. |
| 80 | if [ "$conf_dir" = /etc/postgresql ] && ! [ -f "$conf_dir/postgresql.conf" ]; then |
| 81 | conf_dir=$data_dir |
| 82 | fi |
| 83 | |
| 84 | local socket_dirs=$(get_config "unix_socket_directories" "/run/postgresql") |
| 85 | local port=$(get_config "port" "$port") |
| 86 | |
| 87 | start_stop_daemon_args="$start_stop_daemon_args --env PGPORT=$port" |
| 88 | |
| 89 | local var; for var in $env_vars; do |
| 90 | start_stop_daemon_args="$start_stop_daemon_args --env $var" |
| 91 | done |
| 92 | |
| 93 | ( |
| 94 | # Set the proper permission for the socket paths and create them if |
| 95 | # they don't exist. |
| 96 | set -f; IFS="," |
| 97 | for dir in $socket_dirs; do |
| 98 | if [ -e "${dir%/}/.s.PGSQL.$port" ]; then |
| 99 | eerror "Socket conflict. A server is already listening on:" |
| 100 | eerror " ${dir%/}/.s.PGSQL.$port" |
| 101 | eerror "Hint: Change 'port' to listen on a different socket." |
| 102 | return 1 |
| 103 | elif [ "${dir%/}" != "/tmp" ]; then |
| 104 | checkpath -d -m 1775 -o $user:$group "$dir" |
| 105 | fi |
| 106 | done |
| 107 | ) |
| 108 | } |
| 109 | |
| 110 | start() { |
| 111 | ebegin "Starting $name" |
| 112 | |
| 113 | rm -f "$pidfile" |
| 114 | start-stop-daemon --start \ |
| 115 | $start_stop_daemon_args \ |
| 116 | --exec /usr/bin/pg_ctl \ |
| 117 | -- start \ |
| 118 | --silent \ |
| 119 | -w --timeout="$start_timeout" \ |
| 120 | --log="$logfile" \ |
| 121 | --pgdata="$conf_dir" \ |
| 122 | -o "--data-directory=$data_dir $pg_opts" |
| 123 | |
| 124 | if eend $? "Failed to start $name"; then |
| 125 | service_set_value "command" "$command" |
| 126 | service_set_value "pidfile" "$pidfile" |
| 127 | else |
| 128 | eerror "Check the log for a possible explanation of the above error:" |
| 129 | eerror " $logfile" |
| 130 | return 1 |
| 131 | fi |
| 132 | } |
| 133 | |
| 134 | stop() { |
| 135 | local command=$(service_get_value "command" || echo "$command") |
| 136 | local pidfile=$(service_get_value "pidfile" || echo "$pidfile") |
| 137 | local retry='' |
| 138 | |
| 139 | [ "$stop_smart_timeout" -eq 0 ] \ |
| 140 | || retry="SIGTERM/$stop_smart_timeout" |
| 141 | [ "$stop_fast_timeout" -eq 0 ] \ |
| 142 | || retry="${retry:+$retry/}SIGINT/$stop_fast_timeout" |
| 143 | [ "$stop_force_timeout" -eq 0 ] \ |
| 144 | || retry="${retry:+$retry/}SIGQUIT/$stop_force_timeout" |
| 145 | [ "$retry" ] \ |
| 146 | || retry='SIGINT/5' |
| 147 | |
| 148 | local seconds=$(( $stop_smart_timeout + $stop_fast_timeout + $stop_force_timeout )) |
| 149 | |
| 150 | ebegin "Stopping $name (this can take up to $seconds seconds)" |
| 151 | |
| 152 | start-stop-daemon --stop \ |
| 153 | --exec "$command" \ |
| 154 | --retry "$retry" \ |
| 155 | --progress \ |
| 156 | --pidfile "$pidfile" |
| 157 | eend $? "Failed to stop $name" |
| 158 | } |
| 159 | |
| 160 | stop_smart() { |
| 161 | _stop SIGTERM "smart shutdown" |
| 162 | } |
| 163 | |
| 164 | stop_fast() { |
| 165 | _stop SIGINT "fast shutdown" |
| 166 | } |
| 167 | |
| 168 | stop_force() { |
| 169 | _stop SIGQUIT "immediate shutdown" |
| 170 | } |
| 171 | |
| 172 | _stop() { |
| 173 | local command=$(service_get_value "command" || echo "$command") |
| 174 | local pidfile=$(service_get_value "pidfile" || echo "$pidfile") |
| 175 | |
| 176 | ebegin "Stopping $name ($2)" |
| 177 | |
| 178 | start-stop-daemon --stop \ |
| 179 | --exec "$command" \ |
| 180 | --signal "$1" \ |
| 181 | --pidfile "$pidfile" \ |
| 182 | && mark_service_stopped "$RC_SVCNAME" |
| 183 | eend $? "Failed to stop $name" |
| 184 | } |
| 185 | |
| 186 | reload() { |
| 187 | ebegin "Reloading $name configuration" |
| 188 | |
| 189 | start-stop-daemon --signal HUP --pidfile "$pidfile" && check_config_errors |
| 190 | local retval=$? |
| 191 | |
| 192 | is_pending_restart || true |
| 193 | |
| 194 | eend $retval |
| 195 | } |
| 196 | |
| 197 | reload_force() { |
| 198 | ebegin "Reloading $name configuration" |
| 199 | |
| 200 | start-stop-daemon --signal HUP --pidfile "$pidfile" && check_config_errors |
| 201 | local retval=$? |
| 202 | |
| 203 | if [ $retval -eq 0 ] && is_pending_restart; then |
| 204 | rc-service --nodeps "$RC_SVCNAME" restart |
| 205 | retval=$? |
| 206 | fi |
| 207 | eend $retval |
| 208 | } |
| 209 | |
| 210 | promote() { |
| 211 | ebegin "Promoting $name to master" |
| 212 | |
| 213 | cd "$data_dir" # to avoid the: could not change directory to "/root" |
| 214 | su $user -c "pg_ctl promote --wait --log=$logfile --pgdata=$conf_dir -o '--data-directory=$data_dir'" |
| 215 | eend $? |
| 216 | } |
| 217 | |
| 218 | setup() { |
| 219 | local bkpdir |
| 220 | |
| 221 | ebegin "Creating a new $name database cluster" |
| 222 | |
| 223 | if [ -d "$data_dir/base" ]; then |
| 224 | eend 1 "$data_dir/base already exists!"; return 1 |
| 225 | fi |
| 226 | |
| 227 | # If data_dir exists, backup configs. |
| 228 | if [ -d "$data_dir" ]; then |
| 229 | bkpdir="$(mktemp -d)" |
| 230 | find "$data_dir" -type f -name "*.conf" -maxdepth 1 \ |
| 231 | -exec mv -v {} "$bkpdir"/ \; |
| 232 | rm -rf "$data_dir"/* |
| 233 | fi |
| 234 | |
| 235 | install -d -m 0700 -o $user -g $group "$data_dir" |
| 236 | install -d -m 0750 -o $user -g $group "$conf_dir" |
| 237 | |
| 238 | cd "$data_dir" # to avoid the: could not change directory to "/root" |
| 239 | su $user -c "/usr/bin/initdb $initdb_opts --pgdata $data_dir" |
| 240 | local retval=$? |
| 241 | |
| 242 | if [ -d "$bkpdir" ]; then |
| 243 | # Move backuped configs back. |
| 244 | mv -v "$bkpdir"/* "$data_dir"/ |
| 245 | rm -rf "$bkpdir" |
| 246 | fi |
| 247 | |
| 248 | local conf_dir=$(readlink -f "$conf_dir") |
| 249 | |
| 250 | if [ "${data_dir%/}" != "${conf_dir%/}" ]; then |
| 251 | # Move configs from data_dir to conf_dir and symlink them to data_dir. |
| 252 | local name newname |
| 253 | for name in postgresql.conf pg_hba.conf pg_ident.conf; do |
| 254 | newname="$name" |
| 255 | [ ! -e "$conf_dir"/$name ] || newname="$name.new" |
| 256 | |
| 257 | mv "$data_dir"/$name "$conf_dir"/$newname |
| 258 | ln -s "$conf_dir"/$name "$data_dir"/$name |
| 259 | done |
| 260 | fi |
| 261 | |
| 262 | eend $retval |
| 263 | } |
| 264 | |
| 265 | |
| 266 | get_config() { |
| 267 | local name="$1" |
| 268 | local default="${2:-}" |
| 269 | |
| 270 | if [ ! -f "$conffile" ]; then |
| 271 | printf '%s\n' "$default" |
| 272 | return 1 |
| 273 | fi |
| 274 | sed -En "/^\s*${name}\b/{ # find line starting with the name |
| 275 | s/^\s*${name}\s*=?\s*([^#]+).*/\1/; # capture the value |
| 276 | s/\s*$//; # trim trailing whitespaces |
| 277 | s/^['\"](.*)['\"]$/\1/; # remove delimiting quotes |
| 278 | p |
| 279 | }" "$conffile" \ |
| 280 | | grep . || printf '%s\n' "$default" |
| 281 | } |
| 282 | |
| 283 | check_config_errors() { |
| 284 | local out; out=$(psql_command " |
| 285 | select |
| 286 | sourcefile || ': line ' || sourceline || ': ' || error || |
| 287 | case when name is not null |
| 288 | then ': ' || name || ' = ''' || setting || '''' |
| 289 | else '' |
| 290 | end |
| 291 | from pg_file_settings |
| 292 | where error is not null |
| 293 | and name not in (select name from pg_settings where pending_restart = true); |
| 294 | ") |
| 295 | if [ $? -eq 0 ] && [ "$out" ]; then |
| 296 | eerror 'Configuration file contains errors:' |
| 297 | printf '%s\n' "$out" | while read line; do |
| 298 | eerror " $line" |
| 299 | done |
| 300 | return 1 |
| 301 | fi |
| 302 | } |
| 303 | |
| 304 | is_pending_restart() { |
| 305 | local out; out=$(psql_command "select name from pg_settings where pending_restart = true;") |
| 306 | |
| 307 | if [ $? -eq 0 ] && [ "$out" ]; then |
| 308 | ewarn 'PostgreSQL must be restarted to apply changes in the following parameters:' |
| 309 | local line; for line in $out; do |
| 310 | ewarn " $line" |
| 311 | done |
| 312 | return 0 |
| 313 | fi |
| 314 | return 1 |
| 315 | } |
| 316 | |
| 317 | check_deprecated_var() { |
| 318 | local old_name="$1" |
| 319 | local new_name="$2" |
| 320 | |
| 321 | if [ -n "$(getval "$old_name")" ]; then |
| 322 | ewarn "Variable '$old_name' is deprecated, please use '$new_name' instead." |
| 323 | fi |
| 324 | } |
| 325 | |
| 326 | getval() { |
| 327 | eval "printf '%s\n' \"\$$1\"" |
| 328 | } |
| 329 | |
| 330 | psql_command() { |
| 331 | su $user -c "psql --no-psqlrc --no-align --tuples-only -q -c \"$1\"" |
| 332 | } |