import hashlib import json import os import shutil import stat import subprocess import tempfile BASE = '/www/backup/txiaw-mysql-fix-20260831' STAGE = BASE + '/stage' WEB = '/www/wwwroot/www.txiaw.com' NGINX = '/www/server/panel/vhost/nginx' BIN = '/www/server/nginx/sbin/nginx' PHP = '/www/server/php/56/bin/php' def run(args): subprocess.run(args, check=True, timeout=20) def atomic_copy(src, dst, metadata=None): info = metadata or os.stat(src) fd, tmp = tempfile.mkstemp(prefix='.txiaw-repair-', suffix='.tmp', dir=os.path.dirname(dst)) try: with os.fdopen(fd, 'wb') as target, open(src, 'rb') as source: shutil.copyfileobj(source, target) target.flush() os.fsync(target.fileno()) os.chmod(tmp, stat.S_IMODE(info.st_mode)) os.chown(tmp, info.st_uid, info.st_gid) os.replace(tmp, dst) finally: if os.path.exists(tmp): os.unlink(tmp) manifest = json.load(open(STAGE + '/baseline.json')) for item in manifest: data = open(item['remote'], 'rb').read() if hashlib.sha256(data).hexdigest() != item['sha256']: raise RuntimeError('Source changed since inspection: ' + item['remote']) shutil.copy2(item['remote'], BASE + '/originals/' + item['backup']) os.chmod(BASE + '/originals/' + item['backup'], 0o600) for name in ['HomeRequestProtection.php', 'index.php', 'test_home_guard.php']: run([PHP, '-l', STAGE + '/' + name]) run([PHP, STAGE + '/test_home_guard.php']) changes = [ ('HomeRequestProtection.php', WEB + '/Lib/HomeRequestProtection.php', None), ('index.php', WEB + '/index.php', 'original-index.php'), ('txiaw-home-protection-http.inc', NGINX + '/txiaw-home-protection-http.inc', None), ('txiaw-home-protection-server.inc', NGINX + '/txiaw-home-protection-server.inc', None), ('www.txiaw.com.conf', NGINX + '/www.txiaw.com.conf', 'original-www.txiaw.com.conf'), ] for source, target, backup in changes: if backup is None and os.path.exists(target): raise RuntimeError('Refusing to overwrite unexpected file: ' + target) installed = [] metadata = {} try: for source, target, backup in changes: metadata[target] = os.stat(target) if backup else None if not backup: os.chmod(STAGE + '/' + source, 0o644) atomic_copy(STAGE + '/' + source, target, metadata[target]) installed.append((target, backup)) run([BIN, '-t']) run([BIN, '-s', 'reload']) except Exception: for target, backup in reversed(installed): if backup: atomic_copy(BASE + '/originals/' + backup, target, metadata[target]) else: os.unlink(target) run([BIN, '-t']) raise json.dump({'files': [p for p, _ in installed], 'status': 'installed'}, open(BASE + '/guard-installed.json', 'w'), indent=2) print('GUARD_DEPLOYED; PHP guard passed; nginx configuration passed and gracefully reloaded.', flush=True)