|
| 1 | +#!/bin/bash |
| 2 | +#------------------------------------------------------------------------------------------------------------------------- |
| 3 | +# Copyright (c) Microsoft Corporation. All rights reserved. |
| 4 | +# Licensed under the MIT License. See https://github.com/devcontainers/features/blob/main/LICENSE for license information. |
| 5 | +#------------------------------------------------------------------------------------------------------------------------- |
| 6 | +# |
| 7 | +# Helper script for common feature setup tasks, including user selection logic. |
| 8 | +# Maintainer: The Dev Container spec maintainers |
| 9 | + |
| 10 | +# Determine the appropriate non-root user |
| 11 | +# Usage: determine_user_from_input USERNAME [FALLBACK_USER] |
| 12 | +# |
| 13 | +# This function resolves the USERNAME variable based on the input value: |
| 14 | +# - If USERNAME is "auto" or "automatic", it will detect an existing non-root user |
| 15 | +# - If USERNAME is "none" or doesn't exist, it will fall back to root |
| 16 | +# - Otherwise, it validates the specified USERNAME exists |
| 17 | +# |
| 18 | +# Arguments: |
| 19 | +# USERNAME - The username input (typically from feature configuration) |
| 20 | +# FALLBACK_USER - Optional fallback user when no user is found in automatic mode (defaults to "root") |
| 21 | +# |
| 22 | +# Returns: |
| 23 | +# The resolved username is printed to stdout |
| 24 | +# |
| 25 | +# Examples: |
| 26 | +# USERNAME=$(determine_user_from_input "automatic") |
| 27 | +# USERNAME=$(determine_user_from_input "vscode") |
| 28 | +# USERNAME=$(determine_user_from_input "auto" "vscode") |
| 29 | +# |
| 30 | +determine_user_from_input() { |
| 31 | + local input_username="${1:-automatic}" |
| 32 | + local fallback_user="${2:-root}" |
| 33 | + local resolved_username="" |
| 34 | + |
| 35 | + if [ "${input_username}" = "auto" ] || [ "${input_username}" = "automatic" ]; then |
| 36 | + # Automatic mode: try to detect an existing non-root user |
| 37 | + |
| 38 | + # First, check if _REMOTE_USER is set and is not root |
| 39 | + if [ -n "${_REMOTE_USER:-}" ] && [ "${_REMOTE_USER}" != "root" ]; then |
| 40 | + # Verify the user exists before using it |
| 41 | + if id -u "${_REMOTE_USER}" > /dev/null 2>&1; then |
| 42 | + resolved_username="${_REMOTE_USER}" |
| 43 | + else |
| 44 | + # _REMOTE_USER doesn't exist, fall through to normal detection |
| 45 | + resolved_username="" |
| 46 | + fi |
| 47 | + fi |
| 48 | + |
| 49 | + # If we didn't resolve via _REMOTE_USER, try to find a non-root user |
| 50 | + if [ -z "${resolved_username}" ]; then |
| 51 | + # Try to find a non-root user from a list of common usernames |
| 52 | + # The list includes: devcontainer, vscode, node, codespace, and the user with UID 1000 |
| 53 | + local possible_users=("devcontainer" "vscode" "node" "codespace" "$(awk -v val=1000 -F ":" '$3==val{print $1}' /etc/passwd 2>/dev/null || echo '')") |
| 54 | + |
| 55 | + for current_user in "${possible_users[@]}"; do |
| 56 | + # Skip empty entries |
| 57 | + if [ -z "${current_user}" ]; then |
| 58 | + continue |
| 59 | + fi |
| 60 | + |
| 61 | + # Check if user exists |
| 62 | + if id -u "${current_user}" > /dev/null 2>&1; then |
| 63 | + resolved_username="${current_user}" |
| 64 | + break |
| 65 | + fi |
| 66 | + done |
| 67 | + |
| 68 | + # If no user found, use the fallback |
| 69 | + if [ -z "${resolved_username}" ]; then |
| 70 | + resolved_username="${fallback_user}" |
| 71 | + fi |
| 72 | + fi |
| 73 | + elif [ "${input_username}" = "none" ]; then |
| 74 | + # Explicit "none" means use root |
| 75 | + resolved_username="root" |
| 76 | + else |
| 77 | + # Specific username provided - validate it exists |
| 78 | + if id -u "${input_username}" > /dev/null 2>&1; then |
| 79 | + resolved_username="${input_username}" |
| 80 | + else |
| 81 | + # User doesn't exist, fall back to root |
| 82 | + resolved_username="root" |
| 83 | + fi |
| 84 | + fi |
| 85 | + |
| 86 | + echo "${resolved_username}" |
| 87 | +} |
0 commit comments