acquire-core-x/app/build.sh
hyeongwoo 12812a6c33 Wave L: 외부 연동·전문처리 (ISO8583 와이어·소켓 왕복·클리어링 파일, 패리티→~89%)
외부 카드망 연동(전문처리)을 실제 프로토콜로 구현 — 마이그레이션 최난이도 계층
(소켓·바이너리 전문·프레이밍·상태처리)이 실제로 동작.

■ ISO 8583 와이어 코덱 (common/acq_iso8583.c)
- 1차 비트맵(64bit) + DE 인코딩/파싱(PAN LLVAR·금액·STAN·RRN·응답코드·MID/TID 등).
- iso_build/iso_parse 왕복 무손실 검증(비트맵 7220000000C08000 정확).

■ 외부 카드망 시뮬레이터 (clients/acq_extsw.c, :9500)
- TCP 서버: 길이접두 프레이밍 + ISO 전문. 0200 수신→승인판정→0210 응답 조립
  (DE38 승인번호·DE39 응답코드). 금액임계/STAN으로 일부 거절(05).

■ 전문 왕복 드라이버 (clients/acq_netdrv.c)
- 실매입건→0200 조립→소켓 전송→0210 수신·파싱→mg_netlog 적재(RTT·응답코드).
- 실측: 200건 왕복 승인198/거절2/오류0, 0200→0210, 평균 RTT 0.3ms.

■ 클리어링 파일 교환 (clients/acq_clrfile.c)
- cl_presentment→고정폭 정산파일(/app/out/CLR_BC_YYYYMMDD.dat) HD/DT/TR+체크섬 생성,
  cl_clearing_file 등록. 실제 SFTP 배치 파일교환 대역. 샘플 파일 커밋.

■ 스키마·부팅·화면
- 74-netio.sql: mg_netlog(왕복로그)·cl_clearing_file(파일 레지스트리).
- entrypoint.sh: acq_extsw 부팅 자동기동(:9500).
- build.sh: 클라이언트에 libacqcommon.a(ISO 코덱) 링크.
- 시스템현황에 "외부망 전문 왕복"·"클리어링 파일 교환" 패널.

패리티 재채점 ≈89/100. 남은 ~11%는 상용회선·HSM·다중센터 DR 등 배포 인프라.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-23 14:59:36 +09:00

223 lines
8.3 KiB
Bash

#!/bin/bash
##
## build.sh - REUSABLE build + Enduro/X config generator for acquire-core-x.
##
## Discovers every module under app/src/<mod>/, ecpg-precompiles its .pgc, builds
## - libacqcommon.a (from app/src/common/*.c) [once]
## - lib<mod>dbio.a (from app/src/<mod>/dbio/*.pgc) [per module]
## - <mod>_svr (from app/src/<mod>/<mod>_svr.pgc) [per module]
## - <mod> batches (from app/src/<mod>/batch/*.pgc) [per module]
## - standalone clients (from app/src/clients/*.c)
## then GENERATES app/conf/ndrxconfig.xml with one <server> entry per <mod>_svr.
##
## Adding a new module later = drop app/src/<mod>/{<mod>_svr.pgc,dbio,batch} and
## re-run build.sh. No hand-editing of config or this script is required: modules,
## servers, dbio libs, batches, and srvids are all DISCOVERED.
##
set -euo pipefail
# App root = this script's own directory (normally /app in the container, but
# relocatable so an isolated copy — e.g. CI building a throwaway /b — works too).
APP="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
SRC=$APP/src
BIN=$APP/bin
LIB=$APP/lib
OBJ=$APP/obj
UBF=$APP/ubftab
CONF=$APP/conf
EXINC=/usr/local/include
PGINC="$(pg_config --includedir)"
PGLIB="$(pg_config --libdir)"
mkdir -p "$BIN" "$LIB" "$OBJ"
# Includes for every compile (CFLAGS is honored by buildserver/buildclient and
# by our direct gcc calls). Add every module's dbio AND svc dir so headers resolve
# (svc/*.pgc copybooks include the module's dbio + common headers).
CFLAGS="-I$UBF -I$EXINC -I$PGINC -I$SRC/common"
for d in "$SRC"/*/dbio; do [ -d "$d" ] && CFLAGS="$CFLAGS -I$d"; done
for d in "$SRC"/*/svc; do [ -d "$d" ] && CFLAGS="$CFLAGS -I$d"; done
export CFLAGS
PGLINK="-L$PGLIB -lecpg -lpq"
# ecpg needs the same header search path as gcc (its preprocessor opens the
# #include'd copybooks to find EXEC SQL host-var decls). Mirror CFLAGS' -I set.
ECPG_INC="-I$UBF -I$SRC/common"
for d in "$SRC"/*/dbio; do [ -d "$d" ] && ECPG_INC="$ECPG_INC -I$d"; done
for d in "$SRC"/*/svc; do [ -d "$d" ] && ECPG_INC="$ECPG_INC -I$d"; done
log() { echo "[build] $*"; }
# ---------------------------------------------------------------------------
# discover modules = dirs app/src/<mod>/ that contain <mod>_svr.pgc
# ---------------------------------------------------------------------------
MODS=()
for d in "$SRC"/*/; do
m="$(basename "$d")"
[ -f "$d/${m}_svr.pgc" ] && MODS+=("$m")
done
IFS=$'\n' MODS=($(printf '%s\n' "${MODS[@]}" | sort)); unset IFS
log "modules discovered: ${MODS[*]}"
# ---------------------------------------------------------------------------
# 0. UBF field header
# ---------------------------------------------------------------------------
log "mkfldhdr acq.fd"
( cd "$UBF" && FIELDTBLS=acq.fd FLDTBLDIR="$UBF" mkfldhdr acq.fd >/dev/null )
# ecpg precompile helper: $1 = .pgc path -> echoes generated .c path
ecpg_gen() {
local pgc="$1" base out
base="$(basename "${pgc%.pgc}")"
out="$OBJ/${base}.c"
ecpg $ECPG_INC -I"$(dirname "$pgc")" -o "$out" "$pgc"
echo "$out"
}
# ---------------------------------------------------------------------------
# 1. common lib -> libacqcommon.a
# ---------------------------------------------------------------------------
log "libacqcommon.a"
COMMON_OBJS=()
for c in "$SRC"/common/*.c; do
o="$OBJ/$(basename "${c%.c}").o"
gcc -c $CFLAGS "$c" -o "$o"
COMMON_OBJS+=("$o")
done
ar rcs "$LIB/libacqcommon.a" "${COMMON_OBJS[@]}"
NSVR=0; NDBIO=0; NBATCH=0
# ---------------------------------------------------------------------------
# 2..4 per module: dbio lib, server, batches
# ---------------------------------------------------------------------------
NSVC=0
for m in "${MODS[@]}"; do
MDIR="$SRC/$m"
DBIO_LIB=""
SVC_LIB=""
# -- dbio static lib (optional) --
if compgen -G "$MDIR/dbio/*.pgc" >/dev/null; then
log "lib${m}dbio.a"
DB_OBJS=()
for pgc in "$MDIR"/dbio/*.pgc; do
c="$(ecpg_gen "$pgc")"
o="${c%.c}.o"
gcc -c $CFLAGS "$c" -o "$o"
DB_OBJS+=("$o")
done
ar rcs "$LIB/lib${m}dbio.a" "${DB_OBJS[@]}"
DBIO_LIB="$LIB/lib${m}dbio.a"
NDBIO=$((NDBIO+1))
fi
# -- per-service static lib (NEW legacy layout, optional) --
# A module may keep its services INLINE in <mod>_svr.pgc (old style) OR split
# them into svc/*.pgc, one service per file (new style). Both are supported;
# when svc/*.pgc exist they are archived into lib<mod>svc.a and linked in.
if compgen -G "$MDIR/svc/*.pgc" >/dev/null; then
log "lib${m}svc.a"
SVC_OBJS=()
for pgc in "$MDIR"/svc/*.pgc; do
c="$(ecpg_gen "$pgc")"
o="${c%.c}.o"
gcc -c $CFLAGS "$c" -o "$o"
SVC_OBJS+=("$o")
done
ar rcs "$LIB/lib${m}svc.a" "${SVC_OBJS[@]}"
SVC_LIB="$LIB/lib${m}svc.a"
NSVC=$((NSVC+1))
fi
# -- module server <mod>_svr (links svc lib + dbio lib + common) --
log "${m}_svr"
SVR_C="$(ecpg_gen "$MDIR/${m}_svr.pgc")"
buildserver -o "$BIN/${m}_svr" -f "$SVR_C" \
-a "$SVC_LIB $DBIO_LIB $LIB/libacqcommon.a $PGLINK"
NSVR=$((NSVR+1))
# -- batches (optional): each *.pgc -> its own client executable --
if compgen -G "$MDIR/batch/*.pgc" >/dev/null; then
for pgc in "$MDIR"/batch/*.pgc; do
name="$(basename "${pgc%.pgc}")"
log "batch $name"
BC="$(ecpg_gen "$pgc")"
buildclient -o "$BIN/$name" -f "$BC" \
-a "$DBIO_LIB $LIB/libacqcommon.a $PGLINK"
NBATCH=$((NBATCH+1))
done
fi
done
# ---------------------------------------------------------------------------
# 5. standalone clients (plain C, no EXEC SQL)
# ---------------------------------------------------------------------------
NCLI=0
for c in "$SRC"/clients/*.c; do
name="$(basename "${c%.c}")"
log "client $name"
# libacqcommon.a 링크: ISO 8583 코덱(acq_iso8583) 등 공통 C 함수 사용 가능.
buildclient -o "$BIN/$name" -f "$c" -a "$LIB/libacqcommon.a -L$PGLIB -lpq"
NCLI=$((NCLI+1))
done
# ---------------------------------------------------------------------------
# 6. GENERATE ndrxconfig.xml (tmsrv fixed at srvid 40; module servers 100 step 10)
# ---------------------------------------------------------------------------
log "generating ndrxconfig.xml for servers: ${MODS[*]/%/_svr}"
gen_servers() {
local srvid=100
for m in "${MODS[@]}"; do
cat <<EOF
<server name="${m}_svr">
<srvid>${srvid}</srvid>
<!-- 모듈 내부 tpcall 체인(예: SETTLE→ST_MDR)이 같은 서버로 되돌아오므로
복수 카피 필수. 카피가 1이면 호출자가 블록된 채 자기 요청을 못 꺼내
자기교착(TPETIME)이 난다. 모듈내 최대 체인 깊이 3 + 동시성 여유 = 4. -->
<min>4</min>
<max>4</max>
<sysopt>-e \${NDRX_APPHOME}/log/${m}_svr.log -r</sysopt>
</server>
EOF
srvid=$((srvid+10))
done
}
cat > "$CONF/ndrxconfig.xml" <<EOF
<?xml version="1.0" ?>
<!-- GENERATED by app/build.sh - do not hand-edit. One <server> per discovered
<mod>_svr module server; each advertises its module's services at tpsvrinit. -->
<endurox>
<appconfig>
<sanity>1</sanity>
<brrefresh>5</brrefresh>
<restart_min>1</restart_min>
<restart_step>1</restart_step>
<restart_max>5</restart_max>
<restart_to_check>20</restart_to_check>
<gather_pq_stats>Y</gather_pq_stats>
</appconfig>
<defaults>
<min>1</min>
<max>1</max>
<autokill>1</autokill>
<start_max>20</start_max>
<pingtime>100</pingtime>
<ping_max>800</ping_max>
<end_max>30</end_max>
<killtime>1</killtime>
</defaults>
<servers>
<!-- XA transaction manager for RM1 (ECPG/PostgreSQL). -->
<server name="tmsrv">
<srvid>40</srvid>
<min>1</min>
<max>1</max>
<sysopt>-e \${NDRX_APPHOME}/log/tmsrv-rm1.log -r -- -t3 -l\${NDRX_APPHOME}/tmlogs/rm1 -m20</sysopt>
</server>
$(gen_servers)
</servers>
</endurox>
EOF
log "DONE: modules=${#MODS[@]} servers=$NSVR svc_libs=$NSVC dbio_libs=$NDBIO batches=$NBATCH clients=$NCLI"
echo "[build] server binaries: ${MODS[*]/%/_svr}"