#!/usr/bin/rc
# =====================================================================
# csa-commit: commit changes to DS-layer files.
#
# Copyright (c) 2002,2025 Carlo Strozzi
#
# This program is free software; you can redistribute it and/or modify
# it under the terms of the GNU General Public License as published by
# the Free Software Foundation; version 2 dated June, 1991.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
#
# =====================================================================
#
# Notes:
#
# This program is a separate script because it needs to be run in
# a subshell anyway, as it changes the signal handlers. Including
# it inside a @{} block directly in the csaCommit() library function
# would only make the environment of every program more cluttered, with
# no real performance advantages.
#
# Like in every CSA script that is run either in a subshell or in a
# separate "rc" script that expect CSA functions to be already defined
# in the environment, only the "csaExit [code]" function must be used
# to return to caller and not csaExit.{ok,fault}. Otherwise unexpected
# things may occur, as exit functions other than plain csaExit can
# be overridden with different code in different execution contexts.
# Furthermore, csaExit* will always be called twice: a first time by the
# subprogram and a second time by the caller. While this is acceptable
# for plain csaExit, it may be problematic with csaExit.* functions.
#
# =====================================================================

# mainlib must have already been loaded. If it is not, then we can not
# even rely on csaExit().

~ $CSA_ID () && exit 1

CSA_PGM = ($CSA_PGM csa-commit)		# Override caller in messages.

csaSubshell				# Mandatory !!

~ $CSA_DEBUG 0 || csaDebug $CSA_PGM $*

csa1 = /dev/null				# File to be committed.
csa2 = ()					# Commit CMD.
csa3 = ()					# Repair CMD.
csa4 = ()					# Audit list.
csa5 = ()					# Commit list.
csa6 = -mcsaCommit				# Default commit message.

# Be repeatable and atomical (cp+mv).

for (csa1 in $CSA_COMMIT) {

    csa5 = ``($cr$nl){echo $csa1}
    ~ $csa2 () && csa2 = swu-cpmv

    csa2=($csa2 $csa5(1) $csa5(2)^.$CSA_PID.mv.tmp $csa5(2))

    ~ $csa5(2) $CSA_NOAUDIT || csa4=($csa4 $csa5(2))
}

if (csaTrue $CSA_AUDIT && !~ $csa4 ()) {
   # If we are on the web
   if (csaIsWeb) {
      csa6 = -m$REMOTE_ADDR
      ~ $CSA_AUTH_USER () || csa6 = ($csa6 -w$CSA_AUTH_USER)
   }
   csa2=($csa2 '&&' $CSA_CMD_CI $csa6 -l $csa4)
}

# Add any extra commands to the commit-list.
if (!~ $csa2 () && !~ $CSA_ONCOMMIT ()) {
   csa2 = ($csa2 '&& {' $CSA_ONCOMMIT '}')
}  else csa2 = ($csa2 $CSA_ONCOMMIT)

~ $csa2 () && csaExit 0			# Nothing to do.

# Build the repair command, making sure it can be safely eval'ed.
#csa2 = `{echo $csa2 |
#	 sed 's/''/''''/g;s/^/''/;s/\([ '$tab']\+\)/''\1''/g;s/$/''/'}
csa2 = `{echo $csa2 |
	 sed 's/''/''''/g
         s/^/''/
         s/\([ '$tab']\+\)/''\1''/g
         s/$/''/
         # Expose a few shell special characters.
	 # However, for them to work they will have to
	 # be surrounded by spaces in $CSA_ONCOMMIT,
	 # that is, command > file will work, while
	 # command>file will not, and the same for pipes.
         s/''\([][<>|&{}]\)/\1/g
         s/\([][<>|&{}]\)''/\1/g'}
csa3 = $csa2

# Locking csa-db-repair is mandatory, as it may be being
# run+removed by another program (see above).

csaLock $CSA_CKPT_ROOT/csa-db-repair || csaExit 1

if (~ $CSA_DEBUG 1) {
   echo $CSA_COMMIT >[1=2]
   echo $csa2 >[1=2]
   echo $csa3 >[1=2]
}

# Try and ensure database consistency unless SIGKILL'ed.
fn sighup sigint sigterm {
   echo $csa3 > $CSA_CKPT_ROOT/csa-db-repair
}

# Perform the commit.
eval $csa2 || {

   # Half-completed commits must set the data storage temporarily offline.
   echo $csa2 > $CSA_CKPT_ROOT/csa-db-repair

   csaPrintMsg 0003 $^csa2

   # Tell the caller not to clean-up on exit or csa-db-repair may
   # reference temporary files that will no longer be available when
   # csa-db-repair will be run by the recovery process.

   csaExit 1
}

csaExit 0						# Mandatory!

# =====================================================================
# End of program.
# =====================================================================
