summaryrefslogtreecommitdiffstats
path: root/postgresql-setup.in
blob: 0d78a69af7b00346f09b42e59d663d01a4c77924 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
#!/bin/bash
#
# postgresql-setup - Initialization and upgrade operations for PostgreSQL

test -z "$PATH" && export PATH="/sbin:/usr/sbin:/bin:/usr/bin"

test x"$PGSETUP_DEBUG" != x && set -x && PS4='${LINENO}: '

# Full PostgreSQL version, e.g. 9.0.2
PGVERSION=@PGVERSION@

# Major version of PostgreSQL, e.g. 9.0
PGMAJORVERSION=@PGMAJORVERSION@

# Directory containing the postmaster executable
PGENGINE=@PGENGINE@

# Previous major version, e.g., 8.4, for upgrades
PREVMAJORVERSION=@PREVMAJORVERSION@

# Directory containing the previous postmaster executable
PREVPGENGINE=@PREVPGENGINE@

# Distribution README file
README_DIST=@README_DIST@

# Log file for initdb
PGLOG=@INITDB_LOG@

# Log file for pg_upgrade
PGUPLOG=@UPGRADE_LOG@

SYSCONFIG_DIR=@PKGCONFIG_DIR@

SU=@SU@

USAGE_STRING=$"\
Usage: $0 MODE [OPTION...] [--unit UNIT_NAME]

Script is aimed to help sysadmin with basic database cluster administration.

The UNIT_NAME is used for selection of proper sysconfig or unit configuration
file; For more info and howto/when use this script please look at the docu file
$README_DIST.  The 'postgresql'
string is used when no UNIT_NAME is explicitly passed.

Available operation mode:
  --initdb      Create a new PostgreSQL database cluster.  This is usually the
                first action you perform after PostgreSQL server installation.
  --upgrade     Upgrade PostgreSQL database cluster to be usable with new
                server.  Use this if you upgraded your PostgreSQL server to
                newer major version (currently from $PREVMAJORVERSION \
to $PGMAJORVERSION).

Options:
  --unit        unit ID of PostgreSQL to run against
  --help        show this help
  --version     show version of this package
  --debug       show basic debugging information

Environment:
  PGSETUP_INITDB_OPTIONS     Options carried by this variable are passed to
                             subsequent call of \`initdb\` binary (see man
                             initdb(1)).  This variable is used also during
                             'upgrade' mode because the new cluster is actually
                             re-initialized from the old one.
  PGSETUP_PGUPGRADE_OPTIONS  Options in this variable are passed next to the
                             subsequent call of \`pg_upgrade\`.  For more info
                             about possible options please look at man
                             pg_upgrade(1).
  PGSETUP_DEBUG              Set to '1' if you want to see debugging output."


die()     { echo >&2 $"FATAL: $@" ; exit 1 ; }
error()   { echo >&2 $"ERROR: $@" ; }
error_q() { echo >&2 $"       $@" ; }
warn()    { echo >&2 $"WARNING: $@" ; }
info()    { echo >&2 $" * $@" ; }
debug()   { test "$option_debug" = "1" && echo >&2 $"DEBUG: $@";  }


print_version()
{
    echo "postgresql@DISTSUFF@-setup @VERSION@"
    echo $"Built against PostgreSQL version @PGMAJORVERSION@ and configured"
    echo $"to upgrade from PostgreSQL version @PREVMAJORVERSION@."
}


# code shared between initdb and upgrade actions
perform_initdb()
{
    if [ ! -e "$pgdata" ]; then
        mkdir "$pgdata" || return 1
        chown postgres:postgres "$pgdata"
        chmod go-rwx "$pgdata"
    fi

    # Clean up SELinux tagging for pgdata
    [ -x /sbin/restorecon ] && /sbin/restorecon "$pgdata"

    # Create the initdb log file if needed
    if [ ! -e "$PGLOG" -a ! -h "$PGLOG" ]; then
        touch "$PGLOG" || return 1
        chown postgres:postgres "$PGLOG"
        chmod go-rwx "$PGLOG"
        [ -x /sbin/restorecon ] && /sbin/restorecon "$PGLOG"
    fi

    # Initialize the database
    initdbcmd="$PGENGINE/initdb --pgdata='$pgdata' --auth='ident'"
    initdbcmd+=" $PGSETUP_INITDB_OPTIONS"

    $SU -l postgres -c "$initdbcmd" >> "$PGLOG" 2>&1 < /dev/null

    # Create directory for postmaster log files
    mkdir "$pgdata/pg_log"
    chown postgres:postgres "$pgdata/pg_log"
    chmod go-rwx "$pgdata/pg_log"
    [ -x /sbin/restorecon ] && /sbin/restorecon "$pgdata/pg_log"

    local pgconf="$pgdata/postgresql.conf"
    sed -i "s|^[[:space:]#]*port[[:space:]]=[^#]*|port = $pgport |g" \
            "$pgconf" \
        && grep "^port = " "$pgconf" >/dev/null

    test $? -ne 0 && {
        error "can not change port in $pgdata/postgresql.conf"
        return 1
    }

    if [ -f "$pgdata/PG_VERSION" ]; then
        return 0
    fi

    return 1
}


initdb()
{
    if [ -f "$pgdata/PG_VERSION" ]; then
        error $"Data directory $pgdata is not empty!"
        script_result=1
    else
        info $"Initializing database in $pgdata."
        if perform_initdb; then
            info $"Initialized."
        else
            error $"Initializing database failed, see $PGLOG"
            script_result=1
        fi
    fi
}


upgrade()
{
    # must see previous version in PG_VERSION
    if [ ! -f "$pgdata/PG_VERSION" -o \
         x`cat "$pgdata/PG_VERSION"` != x"$PREVMAJORVERSION" ]
    then
        error   $"Cannot upgrade because the database in $pgdata is not of"
        error_q $"compatible previous version $PREVMAJORVERSION."
        exit 1
    fi
    if [ ! -x "$PGENGINE/pg_upgrade" ]; then
        echo
        echo $"Please install the postgresql-upgrade RPM."
        echo
        exit 5
    fi

    # Set up log file for pg_upgrade
    rm -f "$PGUPLOG"
    touch "$PGUPLOG" || exit 1
    chown postgres:postgres "$PGUPLOG"
    chmod go-rwx "$PGUPLOG"
    [ -x /sbin/restorecon ] && /sbin/restorecon "$PGUPLOG"

    # Move old DB to pgdataold
    pgdataold="${pgdata}-old"
    rm -rf "$pgdataold"
    mv "$pgdata" "$pgdataold" || exit 1

    # Create configuration file for upgrade process
    HBA_CONF_BACKUP="$pgdataold/pg_hba.conf.postgresql-setup.`date +%s`"
    HBA_CONF_BACKUP_EXISTS=0

    if [ ! -f $HBA_CONF_BACKUP ]; then
        mv "$pgdataold/pg_hba.conf" "$HBA_CONF_BACKUP"
        HBA_CONF_BACKUP_EXISTS=1

        # For fluent upgrade 'postgres' user should be able to connect
        # to any database without password.  Temporarily, no other type
        # of connection is needed.
        echo "local all postgres ident" > "$pgdataold/pg_hba.conf"
    fi

    echo -n $"Upgrading database: "

    # Create empty new-format database
    if perform_initdb; then
        # Do the upgrade
        $SU -l postgres -c "$PGENGINE/pg_upgrade \
                        '--old-bindir=$PREVPGENGINE' \
                        '--new-bindir=$PGENGINE' \
                        '--old-datadir=$pgdataold' \
                        '--new-datadir=$pgdata' \
                        --link \
                        '--old-port=$PGPORT' '--new-port=$PGPORT' \
                        --user=postgres \
                        $PGSETUP_PGUPGRADE_OPTIONS" \
                                >> "$PGUPLOG" 2>&1 < /dev/null
        if [ $? -ne 0 ]; then
            # pg_upgrade failed
            script_result=1
        fi
    else
        # initdb failed
        script_result=1
    fi

    # Move back the backed-up pg_hba.conf regardless of the script_result.
    if [ x$HBA_CONF_BACKUP_EXISTS = x1 ]; then
        mv -f "$HBA_CONF_BACKUP" "$pgdataold/pg_hba.conf"
    fi

    if [ $script_result -eq 0 ]; then
        echo $"OK"
        echo
        echo $"The configuration files were replaced by default configuration."
        echo $"The previous configuration and data are stored in folder"
        echo $pgdataold.
    else
        # Clean up after failure
        rm -rf "$pgdata"
        mv "$pgdataold" "$pgdata"
        echo $"failed"
    fi
    echo
    echo $"See $PGUPLOG for details."
}


handle_sysconfig()
{
    local mode="$1"
    local service="$2"
    local sysconfig_file="$SYSCONFIG_DIR/$service"

    test -r "$sysconfig_file" || {
        warn "system config file '$sysconfig_file' not found or unreadable"
        return 1
    }

    unset PGPORT PGDATA
    . "$sysconfig_file"
    sysconfig_pgdata="$PGDATA"
    sysconfig_pgport="$PGPORT"
    unset PGPORT PGDATA

    test -n "$sysconfig_pgdata" && debug "sysconfig pgdata: '$sysconfig_pgdata'"
    test -n "$sysconfig_pgport" && debug "sysconfig pgport: $sysconfig_pgport"
}


# This is mostly for backward compatibility with version postgresql-setup from
# postgresql package <= 9.3.4-7 as this type of configuration is not adviced
# anymore.  But user still may override the /etc/sysconfig/* settings with
# Environment= statement in service file.   Note that this parsing technique
# fails for PGDATA pathnames containing spaces, but there's not much we can do
# about it given systemctl's output format.

handle_service_file()
{
    local mode="$1"
    local service="$2"

    local systemd_env="$(systemctl show -p Environment "${service}.service")" \
        || { return; }

    for env_var in `echo "$systemd_env" | sed 's/^Environment=//'`; do
        # If one variable name is defined multiple times the last definition wins.
        case "$env_var" in
            PGDATA=*)
                unit_pgdata="${env_var##PGDATA=}"
                debug "unit's datadir: '$unit_pgdata'"
                ;;
            PGPORT=*)
                unit_pgport="${env_var##PGPORT=}"
                debug "unit's pgport: $unit_pgport"
                ;;
        esac
    done
}


handle_pgconf()
{
    local mode="$1"
    local datadir="$2"
    local conffile="$datadir/postgresql.conf"

    test "$mode" = initdb && return 0

    debug "postgresql.conf: $conffile"

    test -r "$conffile" || {
        error "config file $conffile is not readable or does not exist"
        return 1
    }

    local sp='[[:space:]]'
    local sed_expr="s/^$sp*port$sp*=$sp\([0-9]\+\).*/\1/p"

    rv=0
    conf_pgport=`sed -n "$sed_expr" $conffile | tail -1` || rv=1
    test -n "$conf_pgport" && debug "postgresql.conf pgport: $conf_pgport"
    return $rv
}


# <Compat>
# Alow users to use the old style arguments like
# 'postgresql-setup initdb $SERVICE_NAME'.
case "$1" in initdb|upgrade)
    action="--$1"
    shift

    warn "using obsoleted argument syntax, try --help"
    old_long_args="help,usage,version,debug"
    oldargs=`getopt -o "" -l "$old_long_args" -n "old-options" -- "$@"` \
        || die "can't parse old arguments"
    eval set -- "$oldargs"
    additional_opts=
    while true; do
        case "$1" in
            --version|--help|--usage|--debug)
                additional_opts="$additional_opts $1"
                shift
                ;;
            --)
                shift
                break
                ;;
        esac
    done

    service=postgresql
    if test -n "$1"; then
        service=$1
        shift
    fi

    set -- $additional_opts "$action" --unit "$service" "$@"
    warn "arguments transformed to: ${0##*/} $@"
esac
# </Compat>


# postgresql-setup arguments are parsed into those variables
option_mode=none
option_service=postgresql
option_port=
option_debug=0

# Content of /etc/sysconfig/$option_service fills those:
sysconfig_pgdata=
sysconfig_pgport=

# Configuration from (/etc/systemd/system/$option_service.service) fills those
# variables (this is here for compat, users should user rather sysconfig).
unit_pgdata=
unit_pgport=

# Configuration from postgresql.conf:
conf_pgport=

# Key variables.  Try to fill them postgresql.conf, sysconfig or unit file
# configuration (the later mentioned has more priority).
pgdata=default
pgport=default


short_opts=""
long_opts="\
initdb,upgrade,\
unit:,service:,port:,\
debug,\
version,help,usage"

args=`getopt -o "$short_opts" -l "$long_opts" -n "postgresql-setup" -- "$@"` \
    || die "can't parse arguments"
eval set -- "$args"
parse_fail=0
while true; do
    case "$1" in
        --initdb|--upgrade)
            if test "$option_mode" != none; then
                error "bad argument $1, mode already specified: --$option_mode"
                parse_fail=1
            else
                option_mode=${1##--}
            fi
            shift
            ;;

        --unit|--service)
            option_service=$2
            shift 2
            ;;

        --port)
            option_port=$2
            shift 2
            ;;

        --debug)
            option_debug=1
            shift
            ;;

        --help|--usage)
            echo "$USAGE_STRING"
            exit 0
            ;;

        --version)
            print_version
            exit 0
            ;;

        --)
            shift
            break
            ;;

        *)
            die "author's fault: option $1 not handled"
            break
            ;;
    esac
done

test $parse_fail -ne 0 && die "can't parse arguments"

test "$option_mode" = none \
    && die "no mode specified, use --initdb or --upgrade, or --help"

[[ "$option_port" =~ ^[0-9]*$ ]] \
    || die $"port set to '$option_port', must be integer number"

test -n "$option_port" && pgport=$option_port

debug "mode used: $option_mode"
debug "service name: $option_service"
debug "port: $pgport"

handle_sysconfig    "$option_mode" "$option_service"
handle_service_file "$option_mode" "$option_service"

test -n "$sysconfig_pgdata" && pgdata="$sysconfig_pgdata"
test -n "$unit_pgdata" && pgdata="$unit_pgdata"

test "$pgdata" = default && die "no datadir specified"
[[ "$pgdata" =~ ^/.* ]] \
    || die $"the PostgreSQL datadir not absolute path: '$pgdata', try --debug"

handle_pgconf "$option_mode" "$pgdata" || die "can not parse postgresql.conf"

test -n "$conf_pgport" && pgport="$conf_pgport"
test -n "$sysconfig_pgport" && pgport="$sysconfig_pgport"
test -n "$unit_pgport" && pgport="$unit_pgport"

if test $option_mode = initdb -a "$pgport" = default; then
    test $option_service == postgresql \
        && pgport=5432 \
        || die $"for initdb $option_service, the --port must be specified"
fi

test "$pgport" = default \
    && die $"\
port is not set by postgresql.conf, '$SYSCONFIG_DIR/$option_service' \
nor by --port"

# These variables are read by underlying utilites, rather export them.
export PGDATA=$pgdata
export PGPORT=$pgport

script_result=0

# See how we were called.
case "$option_mode" in
    initdb)
        initdb
        ;;
    upgrade)
        upgrade
        ;;
    *)
        echo >&2 "$USAGE_STRING"
        exit 2
esac

exit $script_result