Initial build setup for x86_64 Stormux.

This commit is contained in:
Storm Dragon
2025-11-19 03:26:25 -05:00
parent fffe426d29
commit 3c5490ea24
91 changed files with 2266 additions and 0 deletions
+7
View File
@@ -0,0 +1,7 @@
#!/usr/bin/env bash
play -qnV0 synth 3 pluck D3 pluck A3 pluck D4 pluck F4 pluck A4 delay 0 .1 .2 .3 .4 remix - chorus 0.9 0.9 38 0.75 0.3 0.5 -t &
read -rsp "$*"$'\n' password
echo "$password"
exit 0
+12
View File
@@ -0,0 +1,12 @@
#
# This file is parsed by pam_env module
#
# Syntax: simple "KEY=VAL" pairs on separate lines
#
# Accessibility variables
export ACCESSIBILITY_ENABLED=1
export GTK_MODULES=gail:atk-bridge
export GNOME_ACCESSIBILITY=1
export QT_ACCESSIBILITY=1
export QT_LINUX_ACCESSIBILITY_ALWAYS_ON=1
export SAL_USE_VCLPLUGIN=gtk3
+6
View File
@@ -0,0 +1,6 @@
# Static information about the filesystems.
# See fstab(5) for details.
# <file system> <dir> <type> <options> <dump> <pass>
# Live ISO - root filesystem is handled by archiso, only tmpfs mounts needed
tmpfs /var/log tmpfs defaults,noatime,nosuid,nodev,noexec,mode=0755,size=128M 0 0
+1
View File
@@ -0,0 +1 @@
stormux
+5
View File
@@ -0,0 +1,5 @@
# Static table lookup for hostnames.
# See hosts(5) for details.
127.0.0.1 localhost
::1 localhost
127.0.1.1 stormux.localdomain stormux
@@ -0,0 +1,3 @@
HOOKS=(base udev microcode modconf kms memdisk archiso archiso_loop_mnt archiso_pxe_common archiso_pxe_nbd archiso_pxe_http archiso_pxe_nfs block filesystems keyboard)
COMPRESSION="xz"
COMPRESSION_OPTIONS=(-9e)
@@ -0,0 +1,8 @@
# mkinitcpio preset file for the 'linux' package on archiso
PRESETS=('archiso')
ALL_kver='/boot/vmlinuz-linux'
archiso_config='/etc/mkinitcpio.conf.d/archiso.conf'
archiso_image="/boot/initramfs-linux.img"
+10
View File
@@ -0,0 +1,10 @@
Welcome to Stormux, powered by Arch Linux ARM
Stormux Website: https://stormux.org
Arch Linux ARM Forum: https://archlinuxarm.org/forum
Stormux IRC: #stormux on irc.stormux.org
Arch Linux ARM IRC: #archlinuxarm on irc.libera.chat
Thank you Stormux supporters! https://ko-fi.com/stormux/leaderboard
+104
View File
@@ -0,0 +1,104 @@
#
# /etc/pacman.conf
#
# See the pacman.conf(5) manpage for option and repository directives
#
# GENERAL OPTIONS
#
[options]
# The following paths are commented out with their default values listed.
# If you wish to use different paths, uncomment and update the paths.
#RootDir = /
#DBPath = /var/lib/pacman/
#CacheDir = /var/cache/pacman/pkg/
#LogFile = /var/log/pacman.log
#GPGDir = /etc/pacman.d/gnupg/
#HookDir = /etc/pacman.d/hooks/
HoldPkg = pacman glibc
#XferCommand = /usr/bin/curl -L -C - -f -o %o %u
#XferCommand = /usr/bin/wget --passive-ftp -c -O %o %u
#CleanMethod = KeepInstalled
Architecture = auto
# Pacman won't upgrade packages listed in IgnorePkg and members of IgnoreGroup
#IgnorePkg =
#IgnoreGroup =
#NoUpgrade =
#NoExtract =
# Misc options
#UseSyslog
#Color
#NoProgressBar
# We cannot check disk space from within a chroot environment
#CheckSpace
#VerbosePkgLists
ParallelDownloads = 5
#DownloadUser = alpm
#DisableSandbox
# By default, pacman accepts packages signed by keys that its local keyring
# trusts (see pacman-key and its man page), as well as unsigned packages.
SigLevel = Required DatabaseOptional
LocalFileSigLevel = Optional
#RemoteFileSigLevel = Required
# NOTE: You must run `pacman-key --init` before first using pacman; the local
# keyring can then be populated with the keys of all official Arch Linux
# packagers with `pacman-key --populate archlinux`.
#
# REPOSITORIES
# - can be defined here or included from another file
# - pacman will search repositories in the order defined here
# - local/custom mirrors can be added here or in separate files
# - repositories listed first will take precedence when packages
# have identical names, regardless of version number
# - URLs will have $repo replaced by the name of the current repo
# - URLs will have $arch replaced by the name of the architecture
#
# Repository entries are of the format:
# [repo-name]
# Server = ServerName
# Include = IncludePath
#
# The header [repo-name] is crucial - it must be present and
# uncommented to enable the repo.
#
# The testing repositories are disabled by default. To enable, uncomment the
# repo name header and Include lines. You can add preferred servers immediately
# after the header, and they will be used before the default mirrors.
#[core-testing]
#Include = /etc/pacman.d/mirrorlist
[stormux]
SigLevel = Required DatabaseOptional
Server = https://packages.stormux.org/$arch
[core]
Include = /etc/pacman.d/mirrorlist
#[extra-testing]
#Include = /etc/pacman.d/mirrorlist
[extra]
Include = /etc/pacman.d/mirrorlist
# If you want to run 32 bit applications on your x86_64 system,
# enable the multilib repositories as required here.
#[multilib-testing]
#Include = /etc/pacman.d/mirrorlist
#[multilib]
#Include = /etc/pacman.d/mirrorlist
# An example of a custom package repository. See the pacman manpage for
# tips on creating your own repositories.
#[custom]
#SigLevel = Optional TrustAll
#Server = file:///home/custompkgs
@@ -0,0 +1,10 @@
[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = *
[Action]
Description = Adding Stormux repository key...
When = PreTransaction
Exec = /bin/sh -c 'if [ ! -f /etc/pacman.d/.stormux-key-added ]; then pacman-key --add /usr/share/stormux/stormux_repo.pub && pacman-key --lsign-key 52ADA49000F1FF0456F8AEEFB4CDE1CD56EF8E82 && touch /etc/pacman.d/.stormux-key-added; fi'
@@ -0,0 +1,16 @@
# remove from airootfs!
# Stormux system setup hook - creates user, configures services
[Trigger]
Operation = Install
Operation = Upgrade
Type = Package
Target = filesystem
[Action]
Description = Setting up Stormux user and system configuration...
When = PostTransaction
Depends = shadow
Depends = systemd
Depends = sudo
Exec = /usr/local/bin/stormux-setup.sh
+63
View File
@@ -0,0 +1,63 @@
#!/usr/bin/env bash
if [[ "$(tty)" != "/dev/tty1" ]]; then
return
fi
if [[ -x /opt/configure-stormux/configure-stormux.sh ]]; then
return
fi
if ! [[ -x /usr/local/bin/configure-stormux ]]; then
return
fi
# Volume calibration FIRST
echo "Setting up audio volume..."
volume=50
wait=0
# Wait for pipewire to become available
while [[ $wait -lt 30 ]]; do
if pgrep pipewire &> /dev/null ; then
break
else
sleep 1
((wait++))
fi
done
# Volume calibration - this should be the VERY FIRST interactive part
while [[ $volume -le 130 ]]; do
clear
pactl set-sink-volume @DEFAULT_SINK@ "${volume}%" 2>/dev/null
spd-say "If this is loud enough, press enter."
if read -t4 ; then
echo "Volume set to ${volume}%"
break
else
((volume+=5))
fi
done
# For audible sudo prompts:
unset sudoFlags
if [[ -x /etc/audibleprompt.sh ]]; then
export SUDO_ASKPASS=/etc/audibleprompt.sh
export sudoFlags=("-A")
fi
clear
cat << "EOF"
Hello, and welcome to Stormux!
Let's get you set up. After you press enter, you will be prompted for the sudo password.
When that happens, type the word stormux and press enter.
You will not receive any speech feedback for this process.
That is completely normal, and speech will return after you have typed the password.
Once again, the password is stormux in all lower case letters.
Please press enter to continue.
EOF
read -r
sudo "${sudoFlags[@]}" configure-stormux
@@ -0,0 +1,4 @@
# Raspberry Pi Information
alias pi-temp='/usr/bin/vcgencmd measure_temp'
alias pi-version='cat /sys/firmware/devicetree/base/model;echo'
alias pi-ip='ip a | grep -v "127.0.0.1" | grep -E -o "([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}"'
@@ -0,0 +1,21 @@
memuse() {
ps axo rss,comm,pid \
| awk '{ proc_list[$2] += $1; } END \
{ for (proc in proc_list) { printf("%d\t%s\n", proc_list[proc],proc); }}' \
| sort -n | tail -n 10 | sort -rn \
| awk '{$1/=1024;printf "%.0fMB\t",$1}{print $2}'
}
pdf()
{
if [[ $# -ne 1 ]]; then
echo 'Usage: pdf <file>' >&2
else
local dir=$(mktemp -d -p /tmp pdf_conversion.XXXXXX)
local outFile="${1##*/}"
local outFile="${outFile%.*}"
pdftohtml -noframes -i -s "$1" "${dir}/${outFile}.html"
w3m -s "${dir}/${outFile}.html"
fi
}
+24
View File
@@ -0,0 +1,24 @@
#
# ~/.bashrc
#
# If not running interactively, don't do anything
[[ $- != *i* ]] && return
#Change directories without using cd
shopt -s autocd
# Keep bash history in screen
export HISTFILE="${HISTFILE}${WINDOW:+.${WINDOW}}"
# load Aliases and functions
[[ -f ".bash_aliases" ]] && source .bash_aliases
[[ -f ".bash_functions" ]] && source .bash_functions
#Invironment variables
PS1='[\u@\h \W] \$ '
export DIALOGOPTS='--no-lines --visit-items'
GPG_TTY=$(tty)
export GPG_TTY
# Don't put commands prefixed with space, or duplicate commands in history
export HISTCONTROL=ignoreboth
+6
View File
@@ -0,0 +1,6 @@
# Reload changes with control+x followed by control+r
set echo-control-characters off
# History searching with up and down arrows.
"\e[A": history-search-backward
"\e[B": history-search-forward
+18
View File
@@ -0,0 +1,18 @@
vbell off
bell_msg ""
hardstatus off
startup_message off
defscrollback 4096
bind ! select 10
bind @ select 11
bind \# select 12
bind $ select 13
bind % select 14
bind ^ select 15
bind & select 16
bind * select 17
bind ( select 18
bind ) select 19
bind b eval "writebuf" 'exec !!! xclip -selection "clipboard" -i /tmp/screen-exchange'
#termcapinfo xterm|xterms|xs|rxvt ti@:te@
termcapinfo xterm* ti@:te@
+1
View File
@@ -0,0 +1 @@
%wheel ALL=(ALL:ALL) ALL
@@ -0,0 +1 @@
kernel.printk = 3 3 3 3
@@ -0,0 +1,3 @@
[Journal]
Storage=volatile
SystemMaxUse=20M
@@ -0,0 +1,2 @@
[Resolve]
DNSSEC=no
@@ -0,0 +1,5 @@
[Unit]
# Ensure audio and speech-dispatcher are available before starting Fenrir
# Wait for user audio initialization to complete
After=sound.target stormux-audio-setup.service speech-dispatcherd.service user@1000.service
Wants=stormux-audio-setup.service speech-dispatcherd.service user@1000.service
@@ -0,0 +1 @@
/usr/lib/systemd/system/fenrirscreenreader.service
@@ -0,0 +1,3 @@
[Service]
ExecStart=
ExecStart=-/sbin/agetty -o '-p -f -- \\u' --noclear --autologin root - $TERM
@@ -0,0 +1,3 @@
[Service]
ExecStart=
ExecStart=-/sbin/agetty -o '-p -f -- \\u' --noclear --autologin stormux - $TERM
@@ -0,0 +1,16 @@
[Unit]
Description=Setup RAM logging and restore logs from disk
DefaultDependencies=false
After=local-fs.target
Before=sysinit.target
RequiresMountsFor=/var
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStartPre=/bin/mkdir -p /var/log.hdd
ExecStartPre=/bin/sh -c 'if [ -d /var/log.hdd ] && [ "$(ls -A /var/log.hdd 2>/dev/null)" ]; then cp -au /var/log.hdd/* /var/log/ 2>/dev/null || true; fi'
ExecStart=/bin/true
[Install]
WantedBy=sysinit.target
@@ -0,0 +1,14 @@
[Unit]
Description=Sync logs to disk before shutdown
DefaultDependencies=false
Before=shutdown.target reboot.target halt.target
Conflicts=shutdown.target reboot.target halt.target
[Service]
Type=oneshot
RemainAfterExit=true
ExecStart=/bin/sh -c 'rsync -aXv --delete --exclude="*.hdd" /var/log/ /var/log.hdd/ 2>/dev/null || true'
TimeoutStopSec=30
[Install]
WantedBy=shutdown.target reboot.target halt.target
@@ -0,0 +1,8 @@
[Unit]
Description=Sync logs from RAM to disk
After=local-fs.target
[Service]
Type=oneshot
ExecStartPre=/bin/mkdir -p /var/log.hdd
ExecStart=/bin/sh -c 'rsync -aXv --delete --exclude="*.hdd" /var/log/ /var/log.hdd/ 2>/dev/null || true'
@@ -0,0 +1,11 @@
[Unit]
Description=Sync logs from RAM to disk every hour
Requires=log-to-ram-sync.service
[Timer]
OnBootSec=15min
OnUnitActiveSec=1h
Persistent=true
[Install]
WantedBy=timers.target
@@ -0,0 +1 @@
/usr/lib/systemd/system/NetworkManager.service
@@ -0,0 +1 @@
/usr/lib/systemd/system/brltty.path
@@ -0,0 +1 @@
../stormux-repo-init.service
@@ -0,0 +1,13 @@
[Unit]
Description=Setup Pipewire for Live Environment
After=sound.target
Before=fenrirscreenreader.service speech-dispatcherd.service
DefaultDependencies=no
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/setup-pipewire-live.sh
[Install]
WantedBy=multi-user.target
@@ -0,0 +1 @@
../stormux-audio-setup.service
@@ -0,0 +1,17 @@
[Unit]
Description=Setup audio for Stormux live environment
# Wait for audio hardware to be ready
Wants=systemd-udev-settle.service
After=systemd-udev-settle.service sound.target
[Service]
Type=oneshot
RemainAfterExit=yes
# Enable linger so pipewire can run without login
ExecStartPre=/bin/mkdir -p /var/lib/systemd/linger
ExecStartPre=/bin/touch /var/lib/systemd/linger/stormux
# Unmute and set volume
ExecStart=/usr/local/bin/livecd-sound
[Install]
WantedBy=sound.target
@@ -0,0 +1,13 @@
[Unit]
Description=Initialize Stormux repository key
After=pacman-init.service
Requires=pacman-init.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/bin/pacman-key --add /usr/share/stormux/stormux_repo.pub
ExecStart=/usr/bin/pacman-key --lsign-key 52ADA49000F1FF0456F8AEEFB4CDE1CD56EF8E82
[Install]
WantedBy=multi-user.target
@@ -0,0 +1,16 @@
[Unit]
Description=Stormux screen reader and speech service
After=stormux-audio-setup.service sound.target
Wants=stormux-audio-setup.service
Before=getty@tty1.service
[Service]
Type=oneshot
RemainAfterExit=yes
# Give pipewire a moment to initialize
ExecStartPre=/bin/sleep 2
# Start Fenrir screen reader
ExecStart=/bin/systemctl start fenrirscreenreader.service
[Install]
WantedBy=multi-user.target
@@ -0,0 +1 @@
/dev/null
@@ -0,0 +1,11 @@
[Unit]
Description=Update system time from worldtimeapi.org
After=network-online.target
Wants=network-online.target
[Service]
Type=oneshot
ExecStart=/usr/local/bin/sync_time.sh
[Install]
WantedBy=multi-user.target
+3
View File
@@ -0,0 +1,3 @@
# This is the fallback vconsole configuration provided by systemd.
KEYMAP=us_alt
@@ -0,0 +1,31 @@
-- Prioritize analog and USB audio devices over HDMI
-- This ensures real sound cards are selected by default
rule = {
matches = {
{
{ "device.form-factor", "equals", "internal" },
},
{
{ "device.form-factor", "equals", "speaker" },
},
{
{ "device.form-factor", "equals", "headphone" },
},
{
{ "device.bus", "equals", "usb" },
},
{
{ "node.name", "matches", "alsa_output.pci-*analog*" },
},
{
{ "node.name", "matches", "alsa_output.usb-*" },
},
},
apply_properties = {
["priority.driver"] = 1000,
["priority.session"] = 1000,
},
}
table.insert(alsa_monitor.rules, rule)
@@ -0,0 +1,22 @@
-- Deprioritize HDMI audio devices
-- This ensures analog/USB audio devices are preferred over HDMI
rule = {
matches = {
{
{ "device.name", "matches", "hdmi*" },
},
{
{ "device.name", "matches", "HDMI*" },
},
{
{ "node.name", "matches", "alsa_output.pci-*hdmi*" },
},
},
apply_properties = {
["priority.driver"] = 100,
["priority.session"] = 100,
},
}
table.insert(alsa_monitor.rules, rule)
@@ -0,0 +1,4 @@
# Raspberry Pi Information
alias pi-temp='/usr/bin/vcgencmd measure_temp'
alias pi-version='cat /sys/firmware/devicetree/base/model;echo'
alias pi-ip='ip a | grep -v "127.0.0.1" | grep -E -o "([0-9]{1,3}\.){3}[0-9]{1,3}/[0-9]{1,2}"'
@@ -0,0 +1,21 @@
memuse() {
ps axo rss,comm,pid \
| awk '{ proc_list[$2] += $1; } END \
{ for (proc in proc_list) { printf("%d\t%s\n", proc_list[proc],proc); }}' \
| sort -n | tail -n 10 | sort -rn \
| awk '{$1/=1024;printf "%.0fMB\t",$1}{print $2}'
}
pdf()
{
if [[ $# -ne 1 ]]; then
echo 'Usage: pdf <file>' >&2
else
local dir=$(mktemp -d -p /tmp pdf_conversion.XXXXXX)
local outFile="${1##*/}"
local outFile="${outFile%.*}"
pdftohtml -noframes -i -s "$1" "${dir}/${outFile}.html"
w3m -s "${dir}/${outFile}.html"
fi
}
+24
View File
@@ -0,0 +1,24 @@
#
# ~/.bashrc
#
# If not running interactively, don't do anything
[[ $- != *i* ]] && return
#Change directories without using cd
shopt -s autocd
# Keep bash history in screen
export HISTFILE="${HISTFILE}${WINDOW:+.${WINDOW}}"
# load Aliases and functions
[[ -f ".bash_aliases" ]] && source .bash_aliases
[[ -f ".bash_functions" ]] && source .bash_functions
#Invironment variables
PS1='[\u@\h \W] \$ '
export DIALOGOPTS='--no-lines --visit-items'
GPG_TTY=$(tty)
export GPG_TTY
# Don't put commands prefixed with space, or duplicate commands in history
export HISTCONTROL=ignoreboth
@@ -0,0 +1,24 @@
# Fenrir console audio support
# Adds secondary socket for console applications like Fenrir
pulse.properties = {
# the addresses this server listens on
server.address = [
"unix:native"
"unix:/tmp/pulse.sock" # console access socket
]
}
# client/stream specific properties
pulse.rules = [
{
# speech dispatcher asks for too small latency and then underruns.
matches = [ { application.name = "~speech-dispatcher*" } ]
actions = {
update-props = {
pulse.min.req = 1024/48000 # 21ms
pulse.min.quantum = 1024/48000 # 21ms
}
}
}
]
@@ -0,0 +1 @@
../pipewire-init-sound.service
@@ -0,0 +1,12 @@
[Unit]
Description=Initialize Pipewire Audio with Sound Test
After=pipewire.service pipewire-pulse.service wireplumber.service
Wants=pipewire.service pipewire-pulse.service wireplumber.service
[Service]
Type=oneshot
RemainAfterExit=yes
ExecStart=/usr/local/bin/init-pipewire-sound.sh
[Install]
WantedBy=default.target
@@ -0,0 +1,24 @@
# Fenrir console audio support
# Prevents bluetooth disconnection when switching TTY
monitor.bluez.properties = {
# Disable logind integration to prevent bluetooth device suspension on TTY switch
bluez5.enable-sbc-xq = true
bluez5.enable-msbc = true
bluez5.enable-hw-volume = true
}
monitor.bluez.rules = [
{
matches = [
{
device.name = "~bluez_card.*"
}
]
actions = {
update-props = {
session.suspend-timeout-seconds = 0
}
}
}
]
@@ -0,0 +1,35 @@
# Fenrir console audio support
# Prevents audio device suspension when switching to TTY console
monitor.alsa.rules = [
{
matches = [
{
device.name = "~alsa_card.*"
}
]
actions = {
update-props = {
api.alsa.use-acp = true
api.acp.auto-profile = false
api.acp.auto-port = false
session.suspend-timeout-seconds = 0
}
}
}
{
matches = [
{
node.name = "~alsa_input.*"
}
{
node.name = "~alsa_output.*"
}
]
actions = {
update-props = {
session.suspend-timeout-seconds = 0
}
}
}
]
+6
View File
@@ -0,0 +1,6 @@
# Reload changes with control+x followed by control+r
set echo-control-characters off
# History searching with up and down arrows.
"\e[A": history-search-backward
"\e[B": history-search-forward
+18
View File
@@ -0,0 +1,18 @@
vbell off
bell_msg ""
hardstatus off
startup_message off
defscrollback 4096
bind ! select 10
bind @ select 11
bind \# select 12
bind $ select 13
bind % select 14
bind ^ select 15
bind & select 16
bind * select 17
bind ( select 18
bind ) select 19
bind b eval "writebuf" 'exec !!! xclip -selection "clipboard" -i /tmp/screen-exchange'
#termcapinfo xterm|xterms|xs|rxvt ti@:te@
termcapinfo xterm* ti@:te@
@@ -0,0 +1,36 @@
# This file is part of PulseAudio.
#
# PulseAudio is free software; you can redistribute it and/or modify
# it under the terms of the GNU Lesser General Public License as published by
# the Free Software Foundation; either version 2 of the License, or
# (at your option) any later version.
#
# PulseAudio 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 Lesser General Public License
# along with PulseAudio; if not, see <http://www.gnu.org/licenses/>.
## Configuration file for PulseAudio clients. See pulse-client.conf(5) for
## more information. Default values are commented out. Use either ; or # for
## commenting.
; default-sink =
; default-source =
default-server = unix:/tmp/pulse.sock
; default-dbus-server =
autospawn = no
; autospawn = yes
; daemon-binary = /usr/bin/pulseaudio
; extra-arguments = --log-target=syslog
; cookie-file =
; enable-shm = yes
; shm-size-bytes = 0 # setting this 0 will use the system-default, usually 64 MiB
; auto-connect-localhost = no
; auto-connect-display = no
+103
View File
@@ -0,0 +1,103 @@
#!/usr/bin/env bash
# Renames a user
# Required arguments: old user name, new user name
set -e
question() {
echo
echo "$@"
read -r answer
answer="${answer:0:1}"
answer="${answer^}"
}
if [[ $# -ne 2 ]]; then
echo "Usage: $0 old-user new-user"
exit 1
fi
oldUser="$1"
newUser="$2"
# Make sure old user exists
if ! id "$oldUser" >/dev/null 2>&1; then
echo "The user $oldUser does not exist."
exit 1
fi
# Make sure old user is not logged in
if pgrep -u "$oldUser" >/dev/null; then
echo "The user $oldUser is currently logged in or has running processes. Cannot continue."
question "Would you like to forcibly log out $oldUser? (Y/N)"
if [[ "$answer" != "Y" ]]; then
exit 1
fi
systemctl stop display-manager
loginctl terminate-user "$oldUser"
sleep 2
if pgrep -u "$oldUser" >/dev/null; then
echo "The user $oldUser still has running processes after termination. Cannot continue."
exit 1
fi
fi
# Make sure new user does not exist
if id "$newUser" >/dev/null 2>&1; then
echo "The user name $newUser is already taken."
exit 1
fi
# Make sure the new name is acceptable
if ! [[ "$newUser" =~ ^[a-z_][a-z0-9_-]{0,31}$ ]]; then
echo "$newUser is not an acceptable user name."
exit 1
fi
# Passed all safety checks, proceed with the rename
echo "Renaming $oldUser to $newUser..."
groups=$(id -nG "$oldUser")
groups="${groups// /,}"
usermod -G "$groups" -m -d "/home/$newUser" -l "$newUser" "$oldUser"
# Update nodm.conf if it exists
if [[ -e /etc/nodm.conf ]]; then
echo "Updating /etc/nodm.conf..."
sed -i -e "s#^NODM_USER=.*#NODM_USER='$newUser'#" -e "s#^NODM_XSESSION=.*#NODM_XSESSION='/home/$newUser/.xinitrc'#" /etc/nodm.conf
fi
# Copy over crontab if it exists
if crontab -u "$oldUser" -l >/tmp/${oldUser}_crontab.bak 2>/dev/null; then
echo "Copying over crontab..."
if crontab -u "$newUser" "/tmp/${oldUser}_crontab.bak"; then
rm "/tmp/${oldUser}_crontab.bak"
else
echo "Warning: failed to restore crontab for $newUser."
rm "/tmp/${oldUser}_crontab.bak"
fi
fi
# Update linger
if [[ -e "/var/lib/systemd/linger/$oldUser" ]]; then
echo "Enabling linger..."
mv "/var/lib/systemd/linger/$oldUser" "/var/lib/systemd/linger/$newUser"
fi
# Optionally update files in new home directory
echo
echo "Would you like to search for references to $oldUser and update them to $newUser in files located in /home/$newUser?"
question "This can take a while. (Y/N)"
if [[ "$answer" == "Y" ]]; then
echo "Updating files..."
find "/home/$newUser" -type f -exec grep -Iq . "{}" \; -and -exec sed -i "s|$oldUser|$newUser|g" "{}" \;
fi
echo
echo "Rename complete. The user, $newUser, is now available."
question "Would you like to reboot?"
if [[ "${answer}" == "Y" ]]; then
reboot
fi
exit 0
+1
View File
@@ -0,0 +1 @@
../../../pi4/files/etc/skel
+141
View File
@@ -0,0 +1,141 @@
#!/usr/bin/env bash
# For audible sudo prompts:
unset sudoFlags
if [[ -x /etc/audibleprompt.sh ]]; then
export SUDO_ASKPASS=/etc/audibleprompt.sh
export sudoFlags=("-A")
fi
trap cleanup EXIT
cleanup() {
popd &> /dev/null
if ! [[ -x /opt/configure-stormux/configure-stormux.sh ]]; then
echo "Initial setup is not complete."
echo "To continue setup, please run:"
echo "sudo configure-stormux"
fi
}
if [[ -x /opt/configure-stormux/configure-stormux.sh ]]; then
pushd /opt/configure-stormux
./configure-stormux.sh
exit 0
fi
# Volume calibration is now handled in stormux_first_boot.sh
export DIALOGOPTS='--insecure --no-lines --visit-items'
set_timezone() {
# Get the list of timezones
mapfile -t regions < <(timedatectl --no-pager list-timezones | cut -d '/' -f1 | sort -u)
# Use the same text twice here and just hide the tag field.
region=$(dialog --backtitle "Please select your Region" \
--no-tags \
--menu "Use up and down arrows or page-up and page-down to navigate the list, and press 'Enter' to make your selection." 0 0 0 \
$(for i in ${regions[@]} ; do echo "$i";echo "$i";done) --stdout)
mapfile -t cities < <(timedatectl --no-pager list-timezones | grep "$region" | cut -d '/' -f2 | sort -u)
# Use the same text twice here and just hide the tag field.
city=$(dialog --backtitle "Please select a city near you" \
--no-tags \
--menu "Use up and down arrow or page-up and page-down to navigate the list." 0 0 10 \
$(for i in ${cities[@]} ; do echo "$i";echo "$i";done) --stdout)
# Set the timezone
if [[ -f /etc/localtime ]]; then
rm /etc/localtime
fi
ln -sf /usr/share/zoneinfo/${region}/${city} /etc/localtime
timedatectl set-ntp true
}
# Offer to switch fenrir layout.
echo "Would you like to switch Fenrir to laptop layout?"
echo "Press y for yes or n for no followed by enter."
read -r continue
continue="${continue::1}"
if [[ "${continue,}" == "y" ]];then
sed -i 's/=desktop/=laptop/' /etc/fenrirscreenreader/settings/settings.conf
clear
systemctl restart fenrirscreenreader.service
fi
# Check for possible resize
diskSource="$(df --output='source' / | tail -1)"
diskSize="$(df -h --output='size' / | tail -1 | tr -cd '[:digit:].')"
diskSize=${diskSize%.*}
if [[ $diskSize -le 7 ]]; then
echo "$diskSource is only $diskSize gigs, which means it probably needs to be resized. Would you like to do this now?"
echo "Press y for yes or n for no followed by enter."
read -r continue
continue="${continue::1}"
if [[ "${continue,}" == "y" ]];then
# Extract base device name (handles mmcblk0p2, nvme0n1p2, sda2, etc.)
if [[ "$diskSource" =~ (.*[0-9]+)p[0-9]+$ ]]; then
# Handle mmcblk0p2, nvme0n1p2 style
diskDevice="${BASH_REMATCH[1]}"
else
# Handle sda2, sdb3 style
diskDevice="$(echo "$diskSource" | sed 's/[0-9]*$//')"
fi
echo "Yes" | sudo "${sudoFlags[@]}" parted ---pretend-input-tty "$diskDevice" resizepart 2 100%
sudo "${sudoFlags[@]}" resize2fs -f "$diskSource"
fi
fi
if ! ping -c1 stormux.org &> /dev/null ; then
echo "No internet connection detected. Press enter to open NetworkManager."
read -r continue
echo "setting set focus#highlight=True" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
nmtui-connect
echo "setting set focus#highlight=False" | socat - UNIX-CLIENT:/tmp/fenrirscreenreader-deamon.sock
fi
# Check for internet connectivity
if ping -qc1 -W 1 stormux.org &> /dev/null; then
echo "Updating the clock to prevent certificate errors..."
# Get current date and time
date_time=$(curl -s http://worldtimeapi.org/api/ip | grep -oP '(?<="datetime":")[^"]*')
echo "Current date and time: $date_time"
# set date and time
date -s "$date_time"
echo "If your Pi does not have a CMOS battery and is powered off regularly, it may take a while for the time to be set correctly after boot."
echo "Stormux provides a service to sync the time after internet connection is established."
read -rp "Would you like the time to sync as soon as the Pi connects to the internet? " answer
answer="${answer:0:1}"
if [[ "${answer,,}" == "y" ]]; then
systemctl enable time_sync_at_boot.service
else
echo "Time sync at boot skipped."
echo "If you change your mind later, simply type:"
echo "sudo systemctl enable time_sync_at_boot.service"
fi
set_timezone
else
echo "Please connect to the internet and run ${0##*/} again."
exit 1
fi
echo "Installing configure-stormux..."
git -C /opt clone -q https://git.stormux.org/storm/configure-stormux || exit 1
echo
echo "Initial setup is complete."
echo
echo "The default passwords are stormux for the stormux user"
echo "and root for the root user. It is highly recommended to change them."
echo "To change the password for stormux, run:"
echo "passwd"
echo "To change the password for root, run:"
echo "sudo passwd"
echo
echo "For more configuration options, run configure-stormux,"
echo "or you may configure your system manually."
echo
echo "Thank you for choosing Stormux."
exit 0
+21
View File
@@ -0,0 +1,21 @@
#!/usr/bin/env bash
# Initialize pipewire audio by attempting to play sound until it succeeds
# This ensures pipewire is fully ready before fenrir starts
maxAttempts=30
attempt=0
while [[ $attempt -lt $maxAttempts ]]; do
# Try to play a silent sound using sox
if play -qnV0 synth .1 whi norm -100 2>/dev/null; then
# Success! Pipewire is working
exit 0
fi
# Wait a moment before trying again
sleep 1
((attempt++))
done
# Even if we failed, exit successfully so we don't block boot
exit 0
+24
View File
@@ -0,0 +1,24 @@
#!/usr/bin/env bash
# Configure audio for Stormux live environment
# Ensures pipewire is running and audio is properly configured
# Start pipewire for the stormux user if not already running
if id stormux &>/dev/null; then
# Enable linger for stormux user
loginctl enable-linger stormux 2>/dev/null || true
# Start pipewire user services
stormux_uid="$(id -u stormux)"
sudo -u stormux XDG_RUNTIME_DIR="/run/user/${stormux_uid}" systemctl --user enable --now pipewire.service pipewire-pulse.service wireplumber.service 2>/dev/null || true
# Wait for pipewire to initialize
sleep 2
fi
# Unmute and set reasonable volumes
amixer -q set Master 70% unmute 2>/dev/null || true
amixer -q set PCM 70% unmute 2>/dev/null || true
amixer -q set Speaker 70% unmute 2>/dev/null || true
amixer -q set Headphone 70% unmute 2>/dev/null || true
exit 0
+22
View File
@@ -0,0 +1,22 @@
#!/usr/bin/env bash
# Setup Pipewire for the live environment
# This ensures pipewire is running before Fenrir starts
# Note: Do not use set -e - we want this to succeed even if commands fail
# Enable user linger for the stormux user (allows user services to run without login)
if [ -d /var/lib/systemd/linger ]; then
touch /var/lib/systemd/linger/stormux
fi
# Start pipewire for the stormux user
if id stormux &>/dev/null; then
# Use machinectl to start user services in the user's session
machinectl shell stormux@ /usr/bin/systemctl --user enable pipewire.service pipewire-pulse.service wireplumber.service 2>/dev/null || true
machinectl shell stormux@ /usr/bin/systemctl --user start pipewire.service pipewire-pulse.service wireplumber.service 2>/dev/null || true
fi
# Wait a moment for pipewire to initialize
sleep 2
exit 0
+73
View File
@@ -0,0 +1,73 @@
#!/usr/bin/env bash
#
# Customize the airootfs after package installation
# This script runs in the chroot environment after packages are installed
set -e -u
# Copy custom skel files, overwriting package defaults
if [ -d /etc/skel.custom ]; then
echo "Installing custom skel files..."
cp -f /etc/skel.custom/.* /etc/skel/ 2>/dev/null || true
rm -rf /etc/skel.custom
fi
# Set the distribution name
echo 'Stormux \r (\l)' > /etc/issue
echo >> /etc/issue
# Create realtime group if it doesn't exist
if ! getent group realtime > /dev/null 2>&1; then
echo "Creating realtime group..."
groupadd -r realtime
fi
# Create stormux user with groups matching Pi build
echo "Creating stormux user..."
useradd -m -g users -G wheel,realtime,audio,video,network,brlapi -s /bin/bash stormux
# Set passwords
echo -e "root\nroot" | passwd "root"
echo -e "stormux\nstormux" | passwd "stormux"
# Configure git for stormux user
sudo -iu stormux bash -c 'git config --global init.defaultBranch master'
# Create user directories
sudo -iu stormux xdg-user-dirs-update
# Enable linger so that sound will start at login
mkdir -p /var/lib/systemd/linger
touch /var/lib/systemd/linger/stormux
# Enable pipewire globally
# Pipewire configuration for fenrir is pre-installed in airootfs
systemctl --global enable pipewire.service pipewire-pulse.service
# Configure sudo for wheel group
echo '%wheel ALL=(ALL) ALL' > /etc/sudoers.d/wheel
# Set hostname
echo stormux > /etc/hostname
# Disable systemd-networkd in favor of NetworkManager
systemctl disable systemd-networkd.service systemd-networkd.socket 2>/dev/null || true
# Enable services
services=(
brltty.path
fenrirscreenreader.service
NetworkManager.service
stormux-audio-setup.service
)
for service in "${services[@]}"; do
echo "Enabling $service..."
if systemctl enable "$service"; then
echo " $service: OK"
else
echo " $service: FAILED"
fi
done
echo "Airootfs customization complete"
+4
View File
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
date_time=$(curl -s http://worldtimeapi.org/api/ip | grep -oP '(?<="datetime":")[^"]*')
date -s "$date_time"
@@ -0,0 +1,29 @@
-----BEGIN PGP PUBLIC KEY BLOCK-----
mQINBGhTtIcBEADTCqY/+das1n52HTTlp3Uf5+ER5r0fNA2HgJq4GzEeFBdqdgSF
y05P9B94d4JWtWvt0ckjsNfku19O9IquKiYP5x9/sYT8RGeRUrTlN9qlCrytTsdM
a8ij9esGQOXDeyRJQTs5igbFWQ26MrJpOkmO8xMXGmfD29yOppRLzVS8Jed0mokk
aEajui3QSWcefDnFMBYNaMF04iuWgVxv/7102adh3u3+V632+fQQhFtu+x92iosU
GtWlGGe3UkEhWJIb6wf/VCYIYQAjxTQBLul2WByXqwCXmobzc7DJbte3FSEPvPis
nJiUmT0fsIIC9c1UbTJt5Gqb6o/oI61tEJEeXieBViXFE7HwBI7FUxaIXoozgiYA
BxaLHQT0Qv9k1SXbBm82xNydjqFHIAolYSp5s1wRIhErlOaG9w3+Tb8zFSekPsbg
cgS/b3PpWmnigXBJQ94Ee+6KoPMVrwEKIu/IHexRUVAm+66Xs55ccgMY+MUupIjX
Bdn9jCYB8NwpC3UNWoSv2oQ7F2hVbXZYC/dx7gKmyvzfrw5MLOX7yEoSGlJJp2iK
fLLP8nw+x7LnDFEnAjPjtmeH7e+JANH1scP/7YMKDszO1THSK3hBAK1aeq0aEFs8
9AO++xZrNbCrF2MnqcGaXs/753k6kf8zyrW6t7A1opxjPHIz3WrQ8SbXXwARAQAB
tExTdG9ybXV4IChGb3Igc2lnbmluZyBzdG9ybXV4IHJlcG9zaXRvcnkgcGFja2Fn
ZXMpIDxzdG9ybV9kcmFnb25Ac3Rvcm11eC5vcmc+iQJOBBMBCgA4FiEEUq2kkADx
/wRW+K7vtM3hzVbvjoIFAmhTtIcCGwMFCwkIBwMFFQoJCAsFFgIDAQACHgECF4AA
CgkQtM3hzVbvjoLiLQ/9HJzlwr2JNGdR/LaM1Y6VuhASGDZsUsi5XLJ7qKUKN/eA
kq0LMO7TzHO++otsEolodMr8fTkthQTPqSnbLZsW9Y3wK9U2vajryblRSEBaY6VB
1ds0pAg5SXz7O2eI2Ne05dkYB176G72KcjrOiSanKjI9sqh6pdTHcQM3XbRxxEVn
or7fKbLs/eveFWRPW3EAwuVnWI1aw3Xw9BQ3TE1eEDPvBh2GVKZ6qskbzdwfZtUB
kxAa30M+ftlNDaV7d6mNfREefEJfZjYFrY2XiqTK3+w1Q1ZaugK0Bbd/YdAoGMdt
6gbd2h/IZtxduWW/HhvWX2wtKqn4fNxAyYcY3aWm8kuq0pXlyEZ96gZas6a2lu+f
+yLvIh3UtC1IimFMDMQnKCTbWgdhd0HMnG9ULnoOroXVGltnBQwlO0hrHSVD+qmy
NVPUxkzS3ZhqSmoW2G8hSvXpMjt/w5fhC0FJhFb/uM/7LqJWSl5aAkUHupN3t/nG
9o7chBQzzXwbpiS6DQuIt6ouyPpwNjeELFPNBjZDf0auVWj6ZZ5+B/bmyHXQ4wFS
bYKZdO9VeXhBxRUMfGpIKvIw/Lnc+1Toan1RTWoUoRH+HuK2D/LAuvsam36gtLrQ
XV3//7Mh4oQ0Zg4REG3SEfG9i3rav8wMtRmaJaSbwmMVpC6mT/uiEzCSgQhO/0c=
=HIM9
-----END PGP PUBLIC KEY BLOCK-----
+79
View File
@@ -0,0 +1,79 @@
state.ALSA {
control.1 {
iface MIXER
name 'PCM Playback Volume'
value -197
comment {
access 'read write'
type INTEGER
count 1
range '-10239 - 400'
dbmin -9999999
dbmax 400
dbvalue.0 -197
}
}
control.2 {
iface MIXER
name 'PCM Playback Switch'
value true
comment {
access 'read write'
type BOOLEAN
count 1
}
}
control.3 {
iface MIXER
name 'PCM Playback Route'
value 1
comment {
access 'read write'
type INTEGER
count 1
range '0 - 2'
}
}
control.4 {
iface PCM
name 'IEC958 Playback Default'
value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
comment {
access 'read write'
type IEC958
count 1
}
}
control.5 {
iface PCM
name 'IEC958 Playback Con Mask'
value '0200000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
comment {
access read
type IEC958
count 1
}
}
control.6 {
iface PCM
name 'IEC958 Playback PCM Stream'
value '0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
comment {
access 'read write inactive'
type IEC958
count 1
}
}
}
state.vc4hdmi {
control.1 {
iface PCM
name ELD
value '100008006a10000100000000000000000469fd22415355532056533232380917070000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000'
comment {
access 'read volatile'
type BYTES
count 128
}
}
}