#!/bin/sh
#
# coding - Enter the SliTaz AI coding jail as yourself, then run opencode.
# The jail is created by root with: tazdev gen-jail
#
# This script runs as root through sudo, so it takes no argument and reads
# no user config, only /etc/slitaz/tazdev.conf. The sudoers rule is:
#
#     %coding ALL=(root) NOPASSWD: /usr/bin/coding
#
# (c) 2026 SliTaz GNU/Linux - GNU GPL v3
#

JAIL_ROOT="/home/slitaz/coding"
JAIL_LOG="/var/log/coding.log"
[ -f /etc/slitaz/tazdev.conf ] && . /etc/slitaz/tazdev.conf
jail="$JAIL_ROOT"

die() {
	echo "coding: $@" >&2
	exit 1
}

# Who used the jail and when, read by admins with: tazdev jail-log
log() {
	echo "$(date '+%Y-%m-%d %H:%M:%S') $user $1" >> $JAIL_LOG
}

# Become root, sudo tells us who we really are
if [ "$(id -u)" != 0 ]; then
	sudo -n -l "$0" >/dev/null 2>&1 ||
		die "you may not enter the coding jail, ask an admin"
	exec sudo "$0"
fi

user="$SUDO_USER"
[ -n "$user" ] || die "run me as a normal user, not as root"
case "$user" in
	*[!a-z0-9_.-]*) die "invalid user name: $user" ;;
esac
uid=$(id -u $user)
gid=$(id -g $user)
group=$(id -gn $user)
[ "$uid" -ge 1000 ] || die "system accounts do not code in the jail"

[ -x "$jail/usr/bin/opencode" ] ||
	die "no coding jail in $jail, ask an admin for: tazdev gen-jail"

# Mount once for everybody and keep it mounted: other devs may be inside.
# With hidepid=2 each dev only sees their own processes in the jail.
(
	flock 9
	mountpoint -q $jail/proc && exit 0
	mount -t proc -o nosuid,nodev,noexec,hidepid=2 proc $jail/proc
	mount -t sysfs -o ro,nosuid,nodev,noexec sysfs $jail/sys
	mount -t tmpfs -o nosuid,mode=0755 none $jail/dev
	mknod -m 666 $jail/dev/null    c 1 3
	mknod -m 666 $jail/dev/zero    c 1 5
	mknod -m 666 $jail/dev/tty     c 5 0
	mknod -m 666 $jail/dev/ptmx    c 5 2
	mknod -m 444 $jail/dev/random  c 1 8
	mknod -m 444 $jail/dev/urandom c 1 9
	ln -s /proc/self/fd   $jail/dev/fd
	ln -s /proc/self/fd/0 $jail/dev/stdin
	ln -s /proc/self/fd/1 $jail/dev/stdout
	ln -s /proc/self/fd/2 $jail/dev/stderr
	mkdir $jail/dev/pts $jail/dev/shm
	mount -t devpts -o gid=4,mode=620 none $jail/dev/pts
	mount -t tmpfs -o nosuid,nodev none $jail/dev/shm
) 9> /var/lock/coding.lock

# Same uid and gid as on the host, with a locked password
grep -q "^$user:" $jail/etc/passwd ||
	echo "$user:x:$uid:$gid:$user:/home/$user:/bin/bash" >> $jail/etc/passwd
grep -q "^$user:" $jail/etc/shadow ||
	echo "$user:!:0:0:99999:7:::" >> $jail/etc/shadow
grep -q "^[^:]*:[^:]*:$gid:" $jail/etc/group ||
	echo "$group:x:$gid:" >> $jail/etc/group

# First visit: a private home with the default opencode config
if [ ! -d "$jail/home/$user" ]; then
	mkdir -p $jail/home/$user
	cp -a $jail/etc/skel/. $jail/home/$user
	# The opencode wrapper avoids the background server, but if another
	# subcommand starts it, it listens on a fixed port: one port per dev
	echo "{ \"port\": $((20000 + uid)) }" \
		> $jail/home/$user/.config/opencode/service.json
	chmod 0600 $jail/home/$user/.config/opencode/service.json
	chown -R $uid:$gid $jail/home/$user
	chmod 0700 $jail/home/$user
fi

cat $jail/etc/motd
log enter
# A lost ssh connection is logged as a hangup, not a leave
trap 'log hangup; exit' HUP TERM
# Drop root in the jail, --nnp: nothing can give it back (setuid, caps)
chroot $jail /bin/busybox setpriv --nnp /bin/busybox su -l $user
log leave
