kych-dbconfig (5453B)
1 #!/bin/bash 2 # This file is part of KyCH. 3 # Copyright (C) 2026 Taler Systems SA 4 # 5 # KyCH is free software; you can redistribute it and/or modify it under the 6 # terms of the GNU Affero General Public License as published by the Free 7 # Software Foundation; either version 3, or (at your option) any later version. 8 # 9 # KyCH is distributed in the hope that it will be useful, but WITHOUT ANY 10 # WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR 11 # A PARTICULAR PURPOSE. See the GNU Affero General Public License for more 12 # details. 13 # 14 # You should have received a copy of the GNU Affero General Public License 15 # along with KyCH; see the file COPYING. If not, see 16 # <http://www.gnu.org/licenses/> 17 # 18 # 19 # Create the PostgreSQL role and database that kych.conf names and load the 20 # schema into it. This is the one-time setup step; afterwards the schema is 21 # kept up to date by kych-dbinit, which the Debian package runs on every 22 # upgrade. 23 # 24 # Error checking on 25 set -eu 26 27 RESET_DB=0 28 SKIP_DBINIT=0 29 DBUSER="kych" 30 CFGFILE="/etc/kych/kych.conf" 31 32 while getopts 'c:hrsu:' OPTION; do 33 case "$OPTION" in 34 c) 35 CFGFILE="$OPTARG" 36 ;; 37 h) 38 echo 'Supported options:' 39 echo " -c FILENAME -- use configuration FILENAME (default: $CFGFILE)" 40 echo " -h -- print this help text" 41 echo " -r -- reset database (dangerous, destroys all data)" 42 echo " -s -- skip database initialization" 43 echo " -u USER -- kych to be run by USER (default: $DBUSER)" 44 exit 0 45 ;; 46 r) 47 RESET_DB="1" 48 ;; 49 s) 50 SKIP_DBINIT="1" 51 ;; 52 u) 53 DBUSER="$OPTARG" 54 ;; 55 ?) 56 echo "Unrecognized command line option" 1>&2 57 exit 1 58 ;; 59 esac 60 done 61 62 if [ "$(id -u)" -ne 0 ]; then 63 echo "This script must be run as root" 1>&2 64 exit 1 65 fi 66 67 if ! id postgres >/dev/null 2>&1; then 68 echo "Could not find 'postgres' user. Please install PostgreSQL first" 1>&2 69 exit 1 70 fi 71 72 if ! id "$DBUSER" >/dev/null 2>&1; then 73 echo "Could not find '$DBUSER' user. Please set it up first" 1>&2 74 exit 1 75 fi 76 77 if ! command -v kych-dbinit >/dev/null 2>&1; then 78 echo "Required 'kych-dbinit' not found. Please fix your installation." 1>&2 79 exit 1 80 fi 81 82 # kych-dbinit understands the full configuration format, including @inline@ 83 # includes and $-expansion, so it is asked where the database is rather than 84 # this script guessing at it. This sets PGDATABASE, PGHOST and PGPORT, which 85 # matters on a host whose PostgreSQL is not on the default port: everything 86 # below then acts on the same server the daemon will use. 87 eval "$(kych-dbinit -c "$CFGFILE" --print-connection)" 88 89 if [ -z "${PGDATABASE:-}" ]; then 90 echo "Could not determine the database from $CFGFILE" 1>&2 91 exit 1 92 fi 93 DBNAME="$PGDATABASE" 94 95 # A database on another host is not ours to create. 96 case "$PGHOST" in 97 /* | localhost | 127.0.0.1 | ::1) ;; 98 *) 99 echo "DATABASE in $CFGFILE names the server '$PGHOST', which is not this" 1>&2 100 echo "host. Create the role and the database there by hand, then load the" 1>&2 101 echo "schema with 'kych-dbinit -c $CFGFILE'." 1>&2 102 exit 1 103 ;; 104 esac 105 106 # The administrative work is done over the local socket as the postgres user, 107 # which is what peer authentication there expects; only the port is taken from 108 # the configuration. Asking the postgres superuser to authenticate over TCP 109 # instead would stop to prompt for a password that nobody is there to type, 110 # which is also why -w is passed to psql throughout. "sudo -i" resets the 111 # environment, so PGPORT is passed explicitly. 112 as_postgres() { 113 sudo -i -u postgres env PGPORT="$PGPORT" PGCONNECT_TIMEOUT=10 "$@" 114 } 115 116 # Up front, so that a server which is not running is reported as that and not 117 # as a series of puzzling failures further down. 118 if ! as_postgres psql -w -tAc 'SELECT 1' postgres >/dev/null 2>&1; then 119 echo "Cannot reach PostgreSQL on port $PGPORT as the postgres user over the" 1>&2 120 echo "local socket. Check that the server is running and that $CFGFILE" 1>&2 121 echo "names its port." 1>&2 122 exit 1 123 fi 124 125 # The daemon connects over the local socket and authenticates as the 126 # operating-system user it runs as, which is what peer authentication in a 127 # stock Debian PostgreSQL expects: the role must have the same name. 128 if as_postgres psql -w -tAc \ 129 "SELECT 1 FROM pg_roles WHERE rolname = '$DBUSER'" postgres | grep -q 1; then 130 echo "Database user '$DBUSER' already exists, continuing anyway." 1>&2 131 else 132 echo "Setting up database user '$DBUSER'." 1>&2 133 if ! as_postgres createuser "$DBUSER"; then 134 echo "Failed to create database user '$DBUSER'" 1>&2 135 exit 1 136 fi 137 fi 138 139 DO_CREATE=1 140 if as_postgres psql -w -lqtA -F: | cut -d: -f1 | grep -qx "$DBNAME"; then 141 if [ 1 = "$RESET_DB" ]; then 142 echo "Deleting existing database '$DBNAME'." 1>&2 143 if ! as_postgres dropdb "$DBNAME"; then 144 echo "Failed to delete existing database '$DBNAME'" 1>&2 145 exit 1 146 fi 147 else 148 echo "Database '$DBNAME' already exists, continuing anyway." 1>&2 149 DO_CREATE=0 150 fi 151 fi 152 153 if [ 1 = "$DO_CREATE" ]; then 154 echo "Creating database '$DBNAME'." 1>&2 155 if ! as_postgres createdb -O "$DBUSER" "$DBNAME"; then 156 echo "Failed to create database '$DBNAME'" 1>&2 157 exit 1 158 fi 159 fi 160 161 if [ 0 = "$SKIP_DBINIT" ]; then 162 echo "Initializing database '$DBNAME'." 1>&2 163 if ! sudo -u "$DBUSER" kych-dbinit -c "$CFGFILE"; then 164 echo "Failed to initialize database schema" 1>&2 165 exit 1 166 fi 167 fi 168 169 echo "Database configuration finished." 1>&2 170 171 exit 0