diff --git a/wsl-distribution.conf b/wsl-distribution.conf index 8361f26..3c8e1e5 100644 --- a/wsl-distribution.conf +++ b/wsl-distribution.conf @@ -20,4 +20,4 @@ defaultName = DEFAULT_NAME [shortcut] enabled = true -icon = /usr/share/pixmaps/fedora-logo.ico +icon = DEFAULT_ICON_FULL_PATH diff --git a/wsl-oobe.sh b/wsl-oobe.sh index ace2e51..5cb4a69 100644 --- a/wsl-oobe.sh +++ b/wsl-oobe.sh @@ -2,7 +2,8 @@ # The Fedora WSL out of box experience script. # -# This command runs the first time the user opens an interactive shell. +# This command runs the first time the user opens an interactive shell if +# `cloud-init` is not enabled. # # A non-zero exit code indicates to WSL that setup failed. @@ -10,19 +11,75 @@ set -ueo pipefail DEFAULT_USER_ID=1000 -echo 'Please create a default user account. The username does not need to match your Windows username.' -echo 'For more information visit: https://aka.ms/wslusers' +function create_user() { + local rc=0 + + while true; do + read -r -p "Create a default Unix user account: " username + + # Create the user. + rc=$( + set +e + /usr/sbin/useradd -m -G wheel --uid "$DEFAULT_USER_ID" "$username" > /dev/null + echo $? + ) + + case $rc in + # 3: invalid argument to option + # 19: Bad login name (since Fedora 42) + 3 | 19) + echo "Invalid username. A valid username must start with a letter or underscore, and can contain letters, digits, underscores, dots, dashes and a dollar sign at the end." + continue + ;; + # 9: username or group name already in use + 9) + echo "User \"$username\" already exists" + continue + ;; + 0) + break + ;; + *) + echo "Unexpected error code from useradd: $rc" + break + ;; + esac + done +} + +# When `cloud-init` is enabled it might take care of user creation and other bits, depending on its +# configuration contained within the WSL image; or the WSL configuration as provided by the host. +if systemctl is-enabled cloud-init.service > /dev/null ; then + echo 'cloud-init is enabled, skipping user account creation. Waiting for cloud-init to finish.' + + # We need to run cloud-init in a sub-shell that disables errexit so we can inspect its error code + # Without the script exiting. + (set +e cloud-init status --wait > /dev/null 2>&1) + + cloud_status=$? + + # We only exit unsuccesfully on a cloud-init exit status of 1. This means an unrecoverable error, + # and the system might not be usable. Any other exit status (0 for success, or 2 for warning) can + # be ignored and happens commonly, for example when there is a default configuration but the fallback + # data source was used. + if [ "${cloud_status}" -eq 1 ]; then + echo 'cloud-init failed unrecoverably. Failed to provision system.' + cloud-init status --long + exit 1 + fi + + exit 0 +fi if getent passwd $DEFAULT_USER_ID > /dev/null ; then echo 'User account already exists, skipping creation' exit 0 fi -# Prompt from the username -read -r -p 'Enter new UNIX username: ' username +echo 'Please create a default user account. The username does not need to match your Windows username.' +echo 'For more information visit: https://aka.ms/wslusers' -# Create the user -/usr/sbin/useradd -m -G wheel --uid $DEFAULT_USER_ID "$username" +create_user cat > /etc/sudoers.d/wsluser << EOF # Ensure the WSL initial user can use sudo without a password. @@ -33,13 +90,5 @@ cat > /etc/sudoers.d/wsluser << EOF $username ALL=(ALL) NOPASSWD: ALL EOF -# Set the default user; necessary when this script is manually run in versions -# of WSL prior to 2.4. -cat >> /etc/wsl.conf << EOF - -[user] -default = "$username" -EOF - echo 'Your user has been created, is included in the wheel group, and can use sudo without a password.' echo "To set a password for your user, run 'sudo passwd $username'" diff --git a/wsl-setup b/wsl-setup new file mode 100755 index 0000000..698ca18 --- /dev/null +++ b/wsl-setup @@ -0,0 +1,96 @@ +#!/usr/bin/bash +# +# Setup the environment for the new WSL configuration +# +# Copyright (C) 2025 +# SPDX-License-Identifier: MIT +# +# Authors: +# Troy Dawson +# + +############### +# Variables +############### +DISTRIBUTION_TEMPLATE_FILE="/usr/share/wsl-setup/wsl-distribution.conf.template" +DISTRIBUTION_CONF_FILE="/usr/lib/wsl-distribution.conf" +DISTRIBUTION_CONF_LINK="/etc/wsl-distribution.conf" +. /etc/os-release +DEFAULT_NAME="${NAME// /}-${VERSION_ID%.*}" +ICON_PATH="/usr/share/pixmaps/fedora-logo.ico" + +############### +# Show help +############### +usage() { + echo "Usage `basename $0` " >&2 + echo >&2 + echo "Setup the environment for the new WSL configuration" >&2 + echo >&2 + echo "Options:" >&2 + echo " --name, -n" >&2 + echo " Set the distro name" >&2 + echo " Default: ${DEFAULT_NAME}" >&2 + echo " --icon, -i" >&2 + echo " Full pathname to the icon" >&2 + echo " Default: ${ICON_PATH}" >&2 + echo " --help, -h" >&2 + echo " Show this options menu" >&2 + echo >&2 + popd &>/dev/null + exit 1 +} + + +############### +# Get our arguments +############### +while [[ "$#" -ge 1 ]] +do +key="$1" +case $key in +--name | -n ) + if ! [ "${2}" == "" ] ; then + DEFAULT_NAME="${2}" + shift + else + echo + echo "ERROR: Name is not provided" + echo + usage + exit 2 + fi +;; +--icon | -i ) + if ! [ "${2}" == "" ] ; then + ICON_PATH="${2}" + shift + else + echo + echo "ERROR: Icon full path is not provided" + echo + usage + exit 2 + fi +;; +--help | -h ) + usage + exit 0 +;; +* ) + echo + echo "ERROR BAD OPTION: $key" + echo + usage + exit 2 +;; +esac +shift +done + +############### +# Do the work +############### + +cp -f ${DISTRIBUTION_TEMPLATE_FILE} ${DISTRIBUTION_CONF_FILE} +sed -i -e "s|DEFAULT_NAME|${DEFAULT_NAME}|" -e "s|DEFAULT_ICON_FULL_PATH|${ICON_PATH}|" ${DISTRIBUTION_CONF_FILE} diff --git a/wsl-setup-tmpfiles.conf b/wsl-setup-tmpfiles.conf index 23ba7b5..a9c3d1f 100644 --- a/wsl-setup-tmpfiles.conf +++ b/wsl-setup-tmpfiles.conf @@ -1 +1 @@ -L+ /tmp/.X11-unix - - - - /mnt/wslg/.X11-unix +L /tmp/.X11-unix - - - - /mnt/wslg/.X11-unix diff --git a/wsl-setup.spec b/wsl-setup.spec index ba05619..4c637a0 100644 --- a/wsl-setup.spec +++ b/wsl-setup.spec @@ -1,5 +1,5 @@ Name: wsl-setup -Version: 1.0.0 +Version: 1.0.1 Release: %autorelease Summary: Windows Subsystem for Linux setup script and configuration License: MIT @@ -13,12 +13,16 @@ Source4: wsl-oobe.sh Source5: firstboot-override.conf Source6: wsl-setup-tmpfiles.conf Source7: wsl-setup-user-tmpfiles.conf +Source8: wsl-setup BuildRequires: systemd-rpm-macros # Needed for the distribution icon Requires: system-logos +# Utilities used by the OOBE script +Requires: shadow-utils +Requires: systemd %description Provides WSL specific configuration files and first-time setup script. @@ -33,8 +37,9 @@ Provides WSL specific configuration files and first-time setup script. %install install -pm 0644 %{SOURCE1} LICENSE install -Dpm0644 -t %{buildroot}%{_sysconfdir}/ %{SOURCE2} -install -Dpm0644 -t %{buildroot}%{_prefix}/lib/ %{SOURCE3} +install -Dpm0644 -T %{SOURCE3} %{buildroot}%{_datarootdir}/wsl-setup/wsl-distribution.conf.template install -Dpm0755 -T %{SOURCE4} %{buildroot}%{_libexecdir}/wsl/oobe.sh +install -Dpm0755 -T %{SOURCE8} %{buildroot}%{_bindir}/wsl-setup ln -s ..%{_prefix}/lib/wsl-distribution.conf %{buildroot}%{_sysconfdir}/wsl-distribution.conf # WSL provides a socket for x11, but we need to ensure its linked to in /tmp. @@ -55,16 +60,17 @@ install -Dpm0644 %{SOURCE5} %{buildroot}%{_unitdir}/systemd-firstboot.service.d/ %post -# generate the "auto" naming -. %{_sysconfdir}/os-release -DYNAMIC_NAME="${NAME// /}-${VERSION_ID%.*}" -sed -i "s,DEFAULT_NAME,${DYNAMIC_NAME}," %{_sysconfdir}/wsl-distribution.conf +# generate the "auto" naming, using the defaults +%{_bindir}/wsl-setup %files +%{_bindir}/wsl-setup %config(noreplace) %{_sysconfdir}/wsl.conf -%{_prefix}/lib/wsl-distribution.conf +%ghost %attr(644, root, root) %{_prefix}/lib/wsl-distribution.conf %{_sysconfdir}/wsl-distribution.conf +%dir %{_datarootdir}/wsl-setup/ +%{_datarootdir}/wsl-setup/wsl-distribution.conf.template %{_libexecdir}/wsl/oobe.sh %{_tmpfilesdir}/%{name}.conf %{_user_tmpfilesdir}/%{name}.conf