+* Key terms
-developer/shell/subu is what I am using to run a sub user. I haven't been maintaining any of the rest of it.
+masu == master user
+subu == sub user
+* Description
+The scripts here can be used to give a master user the ability to administer sub users. The master user can add sub-users, delete them, and adminnister the sub users files.
-# `subu` — Sub-User Shell Launcher
+Sub users are containers built over the user system.
-## Overview
+User processes all have pids, and contained permissions.
-`subu` is a shell script used to launch an interactive login shell under a **sub-user** account.
-It provides an isolated session under a consistent naming scheme, while correctly configuring GUI forwarding and persistent services.
+User's have virtual memory.
-Sub-users are named using the convention:
+User files have owners, and ownership permissions, and also group permissions (sharing permissions) via unix and ACLs.
-```
-Thomas-<subuser>
-```
+IP policy routine rules support network isolation by uid.
-This naming scheme is required because Linux user names must be globally unique — the kernel has no built-in notion of subusers (yet!).
+X windows has sharing facilities and security policies for the display.
----
+Most I/O devices already give virtual presentations of themselves to users. I.e. give each user full functionality while keeping the users separate. Those which do not have always been a pain when writing containers, no exception here.
-## Syntax
-```bash
-subu <subuser>
-```
+* Key to this approach are the tools:
-Where `<subuser>` is the symbolic sub-identity (e.g., `incommon`, `dev`, `tester`).
+machinectl
+bindfs
+wg and iproute2
----
+* Experience
-## Behavior
+I've been using this approach for some years, though much of it is ad hoc. Occasionally I wrote scripts but many were then deprecated. Slowly the full picture is emerging in in this git repo.
-- Launches a shell as user `Thomas-<subuser>`
-- Detects whether the current session is under **Wayland** or **X11**:
- - Under Wayland: uses `xhost +SI:localuser:Thomas-<subuser>`
- - Under X11: extracts the appropriate `.Xauthority` cookie to `$HOME/subu/<subuser>/.Xauthority`
-- Sets `DISPLAY` and `XAUTHORITY` for graphical applications
-- Enables lingering via `loginctl enable-linger` so sub-user services persist
-- Runs the user’s shell (as listed in `/etc/passwd`) in **login mode** (`-l`)
+It would be great to get together with another person who would like to experiment with this. Having two or more users would hasten the maturity of the scripts.
-The shell launched will source `.bash_profile`, which should in turn source `.bashrc`:
+* Also included, scripts for putting the master users and their subus on a remotely mounted encrypted drive. This adds the conveience of being able to walk up to linux boxes and provide one's user data, or to remotely mount a user on a box.
-```bash
-# Inside ~/.bash_profile
-if [ -f "$HOME/.bashrc" ]; then
- . "$HOME/.bashrc"
-fi
-```
+To do:
-This ensures that aliases, prompts, and other interactive settings are applied.
+Initializing, adding, and deleting subu. Adding and removing subu can become so common that there is a data management problem. At one point I had a python module with a constant 'table' of data. I plan to use sqlite for managing this problem and to introduce a new set of scripts. I will probably jump to C to support suid root and begin the integration of subu into Linux.
----
+Static local mounts of masu have not been developed, because I do not use them personally. It would require a simplification of the current remote mount scripts.
-## Subuser: `incommon`
-
-The subuser named `incommon` is a shared environment used across all sub-users.
-It is referenced in the PATH:
-
-```
-/home/Thomas/subu_data/incommon/executable
-```
-
-This allows tools and scripts to be shared without duplication.
-
----
-
-## Example
-
-```bash
-subu incommon
-```
-
-This spawns a login shell as user `Thomas-incommon`, with GUI forwarding, shared executable access, and persistent services via systemd.
-
----
-
-## Notes
-
-- Each `<subuser>` must have a corresponding home directory:
- ```
- /home/Thomas/subu_data/<subuser>
- ```
-- Ensure that the sub-user exists in `/etc/passwd` with a valid shell.
-- Sub-users can run GUI applications (e.g., Emacs, Firefox) with correct X access.
-- Sessions are sandboxed per sub-user, ideal for role-based separation, testing, or containment.
-
----
--- /dev/null
+-- Schema for the subu server
+--
+
+-- List Tables
+-- SQLite does not support PSQL style types
+--
+-- CREATE TYPE List AS (
+-- id SERIAL, -- Integer ID
+-- name TEXT NOT NULL -- Name of the list entry
+-- );
+--
+-- so though these all have the same `List` form, they are declared independently
+--
+ CREATE TABLE db_property_list (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL UNIQUE
+ );
+
+ CREATE TABLE db_event_list (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL UNIQUE
+ );
+
+ CREATE TABLE shell_list (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL UNIQUE
+ );
+
+ CREATE TABLE system_resource_list (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL UNIQUE
+ );
+
+ CREATE TABLE user_type_list (
+ id INTEGER PRIMARY KEY AUTOINCREMENT,
+ name TEXT NOT NULL UNIQUE
+ );
+
+-- Data Tables
+--
+ CREATE TABLE db_property (
+ id INTEGER PRIMARY KEY,
+ property_id INTEGER NOT NULL REFERENCES db_property_list(id),
+ type TEXT NOT NULL,
+ value TEXT,
+ created_at DATETIME DEFAULT CURRENT_TIMESTAMP
+ );
+
+ CREATE TABLE db_event (
+ id INTEGER PRIMARY KEY,
+ event_time DATETIME DEFAULT CURRENT_TIMESTAMP,
+ event_id INTEGER NOT NULL REFERENCES db_event_list(id),
+ user_id INTEGER REFERENCES user(id)
+ );
+
+ CREATE TABLE user (
+ id INTEGER PRIMARY KEY,
+ login_gid INTEGER NOT NULL UNIQUE,
+ name TEXT NOT NULL UNIQUE,
+ home_directory TEXT NOT NULL,
+ shell INTEGER NOT NULL REFERENCES shell_list(id),
+ parent_id INTEGER REFERENCES user(id),
+ user_type_id INTEGER NOT NULL REFERENCES user_type_list(id),
+ status TEXT DEFAULT 'active'
+ );
+
+ CREATE TABLE share (
+ id INTEGER PRIMARY KEY,
+ user_id INTEGER NOT NULL REFERENCES user(id),
+ other_user_id INTEGER NOT NULL REFERENCES user(id),
+ permissions TEXT NOT NULL
+ );
+
+ CREATE TABLE system_resource (
+ id INTEGER PRIMARY KEY,
+ user_id INTEGER NOT NULL REFERENCES user(id),
+ resource_id INTEGER NOT NULL REFERENCES system_resource_list(id),
+ granted_by INTEGER REFERENCES user(id)
+ );
+++ /dev/null
-#!/bin/bash
-set -x
-
-subu="$1"
-if [ -z "$subu" ]; then
- echo "No subuser name supplied"
- exit 1
-fi
-
-# actual subu user name and subu home directory
-subu_user="Thomas-$subu"
-subu_dir="/home/Thomas/subu_data/$subu"
-
-# detect current display and authority
-display_val="${DISPLAY:-:0}"
-authority_val="${XAUTHORITY:-$HOME/.Xauthority}"
-
-# construct guest's authority path (destination)
-subu_Xauthority_path="$HOME/subu/$subu/.Xauthority"
-
-if [ "$XDG_SESSION_TYPE" = "wayland" ]; then
- # Wayland/XWayland uses a temporary XAUTHORITY file (already in $XAUTHORITY)
- echo "Wayland session detected"
- xhost +SI:localuser:"$subu_user"
-else
- # X11 session – extract the proper cookie
- echo "X11 session detected"
- mkdir -p "$(dirname "$subu_Xauthority_path")"
- touch "$subu_Xauthority_path"
- xauth extract "$subu_Xauthority_path" "$display_val"
-fi
-
-# allow guest processes to outlive login
-sudo loginctl enable-linger "$subu_user"
-
-# Launch the subuser shell with DISPLAY and XAUTHORITY
-sudo machinectl shell "$subu_user"@ /bin/bash -c "
- export DISPLAY='$display_val';
- export XAUTHORITY='$subu_Xauthority_path';
- umask 077
- exec \"\$SHELL\" -l
-"
--- /dev/null
+
+These are currently not used. Eventually the main subu commands will be C programs and setuid root so that master users (and only master users) can seamlessly manipulate sub users.
+
+I started on this, then decided to let the scripts stabilize first.
+
--- /dev/null
+#!/bin/bash
+# give_audio.sh — run as master user "Thomas"
+# Usage: ./give_audio.sh <USER>
+# Example: ./give_audio.sh Thomas-US # give card to subuser
+# ./give_audio.sh Thomas # reclaim for master
+
+set -euo pipefail
+
+target="${1-}"
+if [[ -z "$target" ]]; then
+ echo "❌ usage: $0 <USER>"; exit 2
+fi
+
+master="Thomas"
+
+# don't use sudo -v as it dumps the password into the emacs shell
+sudo echo >& /dev/null
+
+run() { echo "+ $*"; eval "$*"; }
+
+# --- sanity checks ---
+if ! id "$target" &>/dev/null; then
+ echo "❌ user not found: $target"; exit 1
+fi
+if [[ "$(id -un)" != "$master" ]]; then
+ echo "❌ must be run as master user '$master'"; exit 1
+fi
+
+# Gather all subusers (Thomas-*)
+mapfile -t subusers < <(getent passwd | awk -F: '$1 ~ /^'"$master"'-/ {print $1}' | sort)
+
+stop_master_audio() {
+ run "systemctl --user stop pipewire pipewire-pulse wireplumber || true"
+}
+
+start_master_audio() {
+ # start services (not only sockets) to avoid lazy-activation races
+ run "systemctl --user start pipewire.service pipewire-pulse.service wireplumber.service"
+}
+
+stop_subu_audio() {
+ local u="$1"
+ run "sudo machinectl shell ${u}@ /bin/bash -lc 'systemctl --user stop pipewire pipewire-pulse wireplumber || true'"
+}
+
+start_subu_audio() {
+ local u="$1"
+ # Keep subuser from trying to bind to logind (not the active seat)
+ run "sudo machinectl shell ${u}@ /bin/bash -lc 'export WIREPLUMBER_DISABLE_PLUGINS=logind; systemctl --user import-environment WIREPLUMBER_DISABLE_PLUGINS; systemctl --user start pipewire.service pipewire-pulse.service wireplumber.service'"
+}
+
+# --- stop everyone first (to release ALSA cleanly) ---
+stop_master_audio
+for u in "${subusers[@]}"; do
+ stop_subu_audio "$u"
+done
+
+# Small settle time so ALSA reservation clears
+sleep 0.5
+
+# --- start only the target ---
+if [[ "$target" == "$master" ]]; then
+ start_master_audio
+else
+ # ensure linger for target so user services can run
+ run "sudo loginctl enable-linger '$target' || true"
+ start_subu_audio "$target"
+fi
+
+# --- quick verification (best-effort) ---
+if [[ "$target" == "$master" ]]; then
+ # Show default sink name (may require pipewire-pulse to be fully up)
+ run "pactl info | sed -n 's/^Default Sink: /Default Sink: /p'"
+else
+ run "sudo machinectl shell ${target}@ /bin/bash -lc 'pactl info | sed -n \\\"s/^Default Sink: /Default Sink: /p\\\"'"
+fi
--- /dev/null
+#!/bin/bash
+# launch_subu.sh — Start a subuser shell (console or GUI-aware, with systemd user session)
+
+set -euo pipefail
+umask 0077
+
+subu="$1"
+if [ -z "$subu" ]; then
+ echo "❌ No subuser name supplied"
+ exit 1
+fi
+
+subu_user="Thomas-$subu"
+if ! id "$subu_user" &>/dev/null; then
+ echo "❌ User $subu_user does not exist"
+ exit 1
+fi
+
+# Check required commands
+error_flag=0
+for cmd in machinectl xauth xhost dbus-run-session; do
+ if ! command -v "$cmd" &>/dev/null; then
+ echo "❌ $cmd not found"
+ error_flag=1
+ fi
+done
+if [ "$error_flag" -eq 1 ]; then
+ exit 1
+fi
+
+# don't use sudo -v, because it will echo the password into the emacs shell
+sudo echo >& /dev/null
+
+
+# Something broke when I turned this off. What was it. Will have to turn it off again and
+# test.
+#
+# Enable lingering so user services can persist
+sudo loginctl enable-linger "$subu_user"
+
+# Decide how to set the use_xauth and use_xhost flags.
+#
+# As of the time of this writing, on my machines, Wayland insists on
+# xauth, while my X11 is refuses to use it, thus it needs xhost control.
+# So this is how I determine how to set the flags here.
+#
+
+# bash will evaluate this variables inside a quoted if even when the
+# gate is falase, so everything needs to be initialized, whether used
+# or not.
+subu_Xauthority_path=""
+use_xauth=0
+use_xhost=0
+if [[ -n "${WAYLAND_DISPLAY:-}" ]]; then
+ has_display=true
+ XDG_SESSION_TYPE="wayland"
+ subu_Xauthority_path="$HOME/subu/$subu/.Xauthority"
+ use_xauth=1
+ use_xhost=0
+ echo "🌀 Wayland session - Using xauth for access control"
+
+elif [[ -n "${DISPLAY:-}" ]]; then
+ has_display=true
+ XDG_SESSION_TYPE="x11"
+ use_xauth=0
+ use_xhost=1
+ echo "🧱 X11 session - Using xhost for access control"
+
+else
+ has_display=false
+ XDG_SESSION_TYPE="tty"
+ use_xauth=0
+ use_xhost=0
+ echo "🖳 Console session (no X detected)"
+fi
+
+if [[ "$use_xhost" -eq 1 ]]; then
+ xhost +SI:localuser:"$subu_user"
+fi
+if [[ "$use_xauth" -eq 1 ]]; then
+ mkdir -p "$(dirname "$subu_Xauthority_path")"
+ touch "$subu_Xauthority_path"
+ xauth extract "$subu_Xauthority_path" "$DISPLAY"
+fi
+
+if $has_display; then
+
+
+ sudo machinectl shell "$subu_user"@ /bin/bash -c "
+
+ # --- session env from parent ---
+ export DISPLAY=\"${DISPLAY:-${WAYLAND_DISPLAY}}\";
+ export XDG_RUNTIME_DIR='/run/user/$(id -u "$subu_user")';
+ export XDG_SESSION_TYPE=\"$XDG_SESSION_TYPE\";
+ export XDG_SESSION_CLASS=\"user\";
+ export XDG_DATA_DIRS=\"/usr/share/gnome:/usr/local/share/:/usr/share/\";
+ export USE_XAUTH=$use_xauth
+
+ # Only set XAUTHORITY when we actually prepared it (Wayland/xauth case)
+ if [[ \"\$USE_XAUTH\" -eq 1 ]]; then
+ export XAUTHORITY=\"$subu_Xauthority_path\"
+ fi
+
+ if command -v /usr/bin/gnome-keyring-daemon &>/dev/null; then
+ eval \$(/usr/bin/gnome-keyring-daemon --start)
+ export GNOME_KEYRING_CONTROL GNOME_KEYRING_PID
+ fi
+
+ # WirePlumber: ignore logind (subuser isn't the active seat)
+ systemctl --user set-environment WIREPLUMBER_DISABLE_PLUGINS=logind
+ systemctl --user import-environment DISPLAY XAUTHORITY WAYLAND_DISPLAY XDG_RUNTIME_DIR XDG_SESSION_TYPE
+
+ # Bring up audio (sockets first, then services)
+ systemctl --user enable --now pipewire.socket pipewire-pulse.socket >/dev/null 2>&1 || true
+ systemctl --user restart wireplumber pipewire pipewire-pulse
+
+ exec dbus-run-session -- bash -l
+ "
+
+else
+
+ # Console mode with DBus session (give it audio too)
+ sudo machinectl shell "$subu_user"@ /bin/bash -c "
+ export XDG_RUNTIME_DIR='/run/user/$(id -u "$subu_user")}';
+
+ systemctl --user set-environment WIREPLUMBER_DISABLE_PLUGINS=logind
+ systemctl --user import-environment XDG_RUNTIME_DIR
+ systemctl --user enable --now pipewire.socket pipewire-pulse.socket >/dev/null 2>&1 || true
+ systemctl --user restart wireplumber pipewire pipewire-pulse
+
+ exec dbus-run-session -- bash -l
+ "
+fi
+
+
--- /dev/null
+# As root:
+# loginctl terminate-user Thomas-US
+loginctl terminate-user $1
--- /dev/null
+
+When the master users are not remote mounted, but rather have static home directories on the local machine, these scripts are not needed.
+
+masu == master user
+subu == sub user
+
+These 'mount' scripts are for keeping master users on a remote, optionally encrypted, device.
+
+I have one example running, so there might be generalization issues with these scripts.
+
+The order and type of the command arguments are the first part of each
+command name. The command description follows. For example:
+
+`map_name__mounted_masu_list.sh`
+
+is given a `map_name` and provides a list of the masu found at the mount point with that name. (Note, that /dev/mapper/<map_name> is mounted at /mnt/<map_name>.)
+
+
+For mounting a remote device.
+
+The remote device has at the top level master user home directories. Each master user home directory has a sub directory called subu_data. Each directory in subu_data is owned by a sub-user, and is the home directory for that sub-user. Each master user also has a `subu` sub-directory. The contents of the subu directory will parallel that of the subu_data directory, but differ in that the contained files will be owned by the master user.
+
+1. the remote device is mounted under /mnt/map_name
+
+ `device_mapname__open_mount.sh` <device> <mapname>`
+
+ `mapname` is the name that appears under /dev/mapper, it is also used
+ as the /mnt/<name> mount point.
+
+ if <device> is not to be opened with cryptsetup, instead mount it to the mount pt directly.
+
+2. Each master user is mount --bind from the /mnt/<map_name> to /home
+
+ masu__to_home.sh <masu>
+
+ There is currently no script to map bind all the masu in one call.
+
+3. master users have subu home directories under /home/<masu>/subu_data. These
+ are owned by the subu users. Map the /home/<masu>/subu_data directories to
+ `/home/subu`n using
+
+ masu__map_own_all.sh <user>
+
+ The `/home/subu` directories are bindfs mapped to be be owned by the master
+ user, so that the master user may administer them.
+
+
+
+* bindfs problem
+
+ On my Fedora install once it was bound by root, it stayed there. On
+ Debian it keeps losing the mounts, for sure on a logout or login,
+ and maybe other times also --- the machinectl logins and logouts I
+ bet that is what is doing it. Hence very time I go to use a mount
+ in subu it is not there or filled with the dreaded question mark
+ garbage, has to be umount then remounted.
+
+ --------------------------------------------------------------------------------
+ You're exactly right in your diagnosis: Debian's behavior under
+ systemd with machinectl is unmounting user bind mounts on
+ login/logout cycles—especially when using enable-linger, systemd
+ --user, or machinectl shell.
+
+ This differs from Fedora, which preserves those mounts more consistently unless explicitly torn down. Let’s first address why it’s happening, then provide a robust, recoverable fix.
+ 🔍 Why This Happens
+
+ When machinectl or loginctl manages a user session:
+
+ systemd creates a private mount namespace per user.
+
+ Any mounts (especially bind mounts) not made within that
+ namespace or not managed by systemd itself (i.e. not via .mount
+ units) can be:
+
+ Torn down on logout,
+
+ Hidden due to namespace isolation,
+
+ Disconnected if user lingering is toggled.
+
+ If your bindfs mount is outside the user’s namespace (i.e. mounted by root), but then accessed inside a user-managed session, systemd may unmount it or lose visibility.
+ ✅ Recommended Fix
+
+ There are two paths:
+ A. Use a systemd .mount unit (most stable)
+
+ Create a file /etc/systemd/system/home-Thomas-subu-<subuname>.mount:
+
+ [Unit]
+ Description=Bind mount subu user home
+
+ [Mount]
+ What=/home/Thomas/subu_data/<subuname>
+ Where=/home/Thomas/subu/<subuname>
+ Type=none
+ Options=bind
+
+ [Install]
+ WantedBy=multi-user.target
+
+ Enable and start it:
+
+ sudo systemctl daemon-reexec
+ sudo systemctl enable --now home-Thomas-subu-<subuname>.mount
+
+ Repeat for each subu.
+
+ This ensures systemd manages the bind mount in its native way and it won’t get torn down.
+ B. Remount automatically on login via ~/.bash_profile or a systemd --user unit
+
+ You can modify your script to:
+
+ Skip sudo if already root
+
+ Be idempotent
+
+ Automatically remount on session start
+
+ Then in ~/.bash_profile or ~/.config/systemd/user/subu-mount.service:
+
+ [Unit]
+ Description=Re-mount subu directory for Thomas
+
+ [Service]
+ Type=oneshot
+ ExecStart=/home/Thomas/executable/subu/user_open_subu.sh Thomas US
+
+ [Install]
+ WantedBy=default.target
+
+ Enable with:
+
+ systemctl --user daemon-reexec
+ systemctl --user enable --now subu-mount.service
+
+ This guarantees it will come up as part of your login, even after a reboot.
+ 🔄 Temporary Workaround
+
+ If you want a quick way to check all subu are mounted and remount them if not, write a watchdog script:
+
+ #!/bin/bash
+ for subu in $(ls /home/Thomas/subu_data); do
+ mountpoint="/home/Thomas/subu/$subu"
+ if ! mountpoint -q "$mountpoint"; then
+ /home/Thomas/executable/subu/user_open_subu.sh Thomas "$subu"
+ fi
+ done
+
+ Would you like me to auto-generate the .mount or .service files for each subu? We can script the system-wide setup cleanly.
--- /dev/null
+#!/bin/bash
+
+if [[ $EUID -ne 0 ]]; then
+ echo "❌ This script must be run as root." >&2
+ exit 1
+fi
+
+# Function to unlock and mount the device
+device_mount() {
+ local device_node=$1 # e.g., /dev/sdb1
+ local device_name=$2 # e.g., Zathustra
+ local mount_point="/mnt/$device_name"
+
+ # Check if cryptsetup is installed
+ if ! command -v cryptsetup &> /dev/null; then
+ echo "Error: cryptsetup is not installed!"
+ return 1
+ fi
+
+ # Check if the device is already mounted
+ if mount | grep "on $mount_point" > /dev/null; then
+ echo "Device $device_name is already mounted at $mount_point."
+ return 0
+ fi
+
+ # Make sure the mount point exists
+ mkdir -p "$mount_point"
+
+ # Unlock the encrypted device
+ sudo cryptsetup luksOpen "$device_node" "$device_name-crypt"
+
+ # Mount the unlocked device
+ sudo mount "/dev/mapper/$device_name-crypt" "$mount_point"
+
+ echo "$device_name mounted at $mount_point"
+}
+
+# Run the function with the device node and device name as arguments
+device_mount "$1" "$2"
--- /dev/null
+#!/bin/bash
+
+if [[ $EUID -ne 0 ]]; then
+ echo "❌ This script must be run as root." >&2
+ exit 1
+fi
+
+umount /mnt/"$1"
+cryptsetup close "$1"
--- /dev/null
+#!/bin/bash
+
+# Function to list users in the /mnt/<device>/user_data directory
+device_user_list() {
+ local device=$1
+ local user_data_dir="/mnt/$device/user_data"
+
+ if [ ! -d "$user_data_dir" ]; then
+ echo "Error: $user_data_dir does not exist!"
+ return 1
+ fi
+
+ # List all user directories in the user_data directory
+ find "$user_data_dir" -maxdepth 1 -mindepth 1 -type d -exec basename {} \;
+}
+
+# Run the function with the device name as an argument
+device_user_list "$1"
--- /dev/null
+#!/bin/bash
+
+# Check if the correct number of arguments is passed
+if [ $# -ne 1 ]; then
+ echo "Usage: $0 <username>"
+ exit 1
+fi
+
+user=$1
+
+# Get the list of sub-users by calling the user_list_subu_home.sh script
+subu_list=$(./user_list_subu_homedir.sh "$user")
+
+# Check if we received any sub-users
+if [ -z "$subu_list" ]; then
+ echo "No sub-users found for $user."
+ exit 1
+fi
+
+# Loop through the sub-users and call user_open_subu.sh for each
+for subu in $subu_list; do
+ echo "Opening sub-user: $subu"
+ ./masu_subu__map_own.sh "$user" "$subu"
+done
--- /dev/null
+#!/bin/bash
+
+# Function to list sub-users in /home/<user>/subu_data
+subu_home_dir_list() {
+ local user=$1
+ local subu_home_dir="/home/$user/subu_data"
+
+ if [ ! -d "/home/$user" ]; then
+ echo "Error: /home/$user does not exist!"
+ return 1
+ fi
+
+ if [ ! -d "$subu_home_dir" ]; then
+ echo "Error: $subu_home_dir does not exist!"
+ return 1
+ fi
+
+ # List all sub-users in the subu directory
+ find "$subu_home_dir" -maxdepth 1 -mindepth 1 -type d -exec basename {} \;
+}
+
+# Run the function with the user as an argument
+subu_home_dir_list "$1"
--- /dev/null
+#!/bin/bash
+
+# Function to list sub-users in /home/<user>/subu
+subu_list() {
+ local user=$1
+ local subu_dir="/home/$user/subu"
+
+ if [ ! -d "/home/$user" ]; then
+ echo "Error: /home/$user does not exist!"
+ return 1
+ fi
+
+ if [ ! -d "$subu_dir" ]; then
+ echo "Error: $subu_dir does not exist!"
+ return 1
+ fi
+
+ # List all sub-users in the subu directory
+ find "$subu_dir" -maxdepth 1 -mindepth 1 -type d -exec basename {} \;
+}
+
+# Run the function with the user as an argument
+subu_list "$1"
--- /dev/null
+#!/bin/bash
+
+# Function to bind mount a user's data to /home/<user>
+device_user_bind() {
+ local device=$1
+ local user=$2
+ local user_data_dir="/mnt/$device/user_data/$user"
+ local home_dir="/home/$user"
+
+ if [ ! -d "$user_data_dir" ]; then
+ echo "Error: $user_data_dir does not exist!"
+ return 1
+ fi
+
+ # Create the home directory if it doesn't exist
+ mkdir -p "$home_dir"
+
+ # Mount --bind the user data to the home directory
+ sudo mount --bind "$user_data_dir" "$home_dir"
+ echo "Mounted $user_data_dir -> $home_dir"
+}
+
+# Run the function with the device name and user as arguments
+device_user_bind "$1" "$2"
--- /dev/null
+#!/bin/bash
+
+# Function to bind mount with UID/GID mapping
+subu_bind() {
+ local user=$1
+ local subu=$2
+
+ # Check if bindfs is installed
+ if ! command -v bindfs &> /dev/null; then
+ echo "Error: bindfs is not installed!"
+ return 1
+ fi
+
+ # Get the username and group name for the main user
+ master_user_name=$user
+ master_group=$user
+
+ # Get the username and group name for the sub-user
+ subu_user_name="${user}-${subu}"
+ subu_group="${user}-${subu}"
+
+ # Check if the user and sub-user exist
+ if ! id "$master_user_name" &>/dev/null; then
+ echo "Error: User '$master_user_name' not found!"
+ return 1
+ fi
+ if ! id "$subu_user_name" &>/dev/null; then
+ echo "Error: Sub-user '${master_user_name}-${subu}' not found!"
+ return 1
+ fi
+
+ # Directories to be bind-mounted
+ subu_data_path="/home/$user/subu_data/$subu"
+ subu_mount_point_path="/home/$user/subu/$subu"
+
+ # Check if sub-user directory exists
+ if [ ! -d "$subu_data_path" ]; then
+ echo "Error: Sub-user directory '$subu_data_path' does not exist!"
+ return 1
+ fi
+
+ # Create the mount point if it doesn't exist
+ mkdir -p "$subu_mount_point_path"
+
+ # Perform the bind mount using bindfs with UID/GID mapping
+ sudo bindfs\
+ --map="$subu_user_name/$master_user_name:@$subu_group/@$master_group" \
+ "$subu_data_path" \
+ "$subu_mount_point_path"
+
+ # Verify if the mount was successful
+ if [ $? -eq 0 ]; then
+ echo "Successfully bind-mounted $subu_data_path to $subu_mount_point_path with UID/GID mapping."
+ else
+ echo "Error: Failed to bind-mount $subu_data_path to $subu_mount_point_path, might already exist."
+ fi
+}
+
+# Call the function with user and subu as arguments
+subu_bind "$1" "$2"
--- /dev/null
+#!/bin/bash
+
+# Function to lookup the UID of the user and sub-user combination
+get_subu_uid() {
+ local user=$1
+ local subu=$2
+
+ # Concatenate user and sub-user name (no space around the = sign)
+ local subu_user="${user}-${subu}"
+
+ # Lookup the UID for the sub-user (user-subuser) combination
+ subu_uid=$(id -u "$subu_user" 2>/dev/null)
+
+ # If found, return only the UID, otherwise return nothing
+ if [ -n "$subu_uid" ]; then
+ echo "$subu_uid"
+ fi
+}
+
+# Call the function with user and subu as arguments
+get_subu_uid "$1" "$2"
--- /dev/null
+#!/bin/bash
+
+# Function to list available devices under /mnt, excluding /mnt itself
+mount_pt_list() {
+ # List all directories in /mnt that are potentially available for mounting
+ find /mnt -mindepth 1 -maxdepth 1 -type d -exec basename {} \;
+}
+
+# Call the function to display available devices
+mount_pt_list
+++ /dev/null
--- Schema for the subu server
---
-
--- List Tables
--- SQLite does not support PSQL style types
---
--- CREATE TYPE List AS (
--- id SERIAL, -- Integer ID
--- name TEXT NOT NULL -- Name of the list entry
--- );
---
--- so though these all have the same `List` form, they are declared independently
---
- CREATE TABLE db_property_list (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- name TEXT NOT NULL UNIQUE
- );
-
- CREATE TABLE db_event_list (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- name TEXT NOT NULL UNIQUE
- );
-
- CREATE TABLE shell_list (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- name TEXT NOT NULL UNIQUE
- );
-
- CREATE TABLE system_resource_list (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- name TEXT NOT NULL UNIQUE
- );
-
- CREATE TABLE user_type_list (
- id INTEGER PRIMARY KEY AUTOINCREMENT,
- name TEXT NOT NULL UNIQUE
- );
-
--- Data Tables
---
- CREATE TABLE db_property (
- id INTEGER PRIMARY KEY,
- property_id INTEGER NOT NULL REFERENCES db_property_list(id),
- type TEXT NOT NULL,
- value TEXT,
- created_at DATETIME DEFAULT CURRENT_TIMESTAMP
- );
-
- CREATE TABLE db_event (
- id INTEGER PRIMARY KEY,
- event_time DATETIME DEFAULT CURRENT_TIMESTAMP,
- event_id INTEGER NOT NULL REFERENCES db_event_list(id),
- user_id INTEGER REFERENCES user(id)
- );
-
- CREATE TABLE user (
- id INTEGER PRIMARY KEY,
- login_gid INTEGER NOT NULL UNIQUE,
- name TEXT NOT NULL UNIQUE,
- home_directory TEXT NOT NULL,
- shell INTEGER NOT NULL REFERENCES shell_list(id),
- parent_id INTEGER REFERENCES user(id),
- user_type_id INTEGER NOT NULL REFERENCES user_type_list(id),
- status TEXT DEFAULT 'active'
- );
-
- CREATE TABLE share (
- id INTEGER PRIMARY KEY,
- user_id INTEGER NOT NULL REFERENCES user(id),
- other_user_id INTEGER NOT NULL REFERENCES user(id),
- permissions TEXT NOT NULL
- );
-
- CREATE TABLE system_resource (
- id INTEGER PRIMARY KEY,
- user_id INTEGER NOT NULL REFERENCES user(id),
- resource_id INTEGER NOT NULL REFERENCES system_resource_list(id),
- granted_by INTEGER REFERENCES user(id)
- );
+++ /dev/null
-#!/bin/env /bin/bash
-# set -x
-
-subu="$1"
-if [ -z "$subu" ]; then
- echo "No subuser name supplied"
- exit 1
-fi
-subu_user="Thomas-$subu"
-
-# share the X display
-#
- export DISPLAY=:0
- export XAUTHORITY=${XAUTHORITY:-$(ls -1 /run/user/$(id -u)/.mutter-Xwaylandauth.* 2>/dev/null | head -n 1)}
- if [ -z "$XAUTHORITY" ]; then
- export XAUTHORITY="$HOME/.Xauthority"
- fi
- xauth -f "$XAUTHORITY" generate "$DISPLAY" . trusted
- xauth extract - "$DISPLAY" | sudo -u "$subu_user" \
- XAUTHORITY="/home/$subu_user/.Xauthority" \
- xauth merge -
-
-# Login the subu
-#
- sudo loginctl enable-linger "$subu_user"
- sudo machinectl shell "$subu_user"@ /bin/bash -c "
- export DISPLAY=:0;
- export XAUTHORITY=/home/$subu_user/.Xauthority;
- bash -i
- "
-