Skip to content

Commit 8bd306a

Browse files
authored
Merge pull request #21 from wheelos/auto_service
feat: add autostart service
2 parents e1e8413 + 274eb09 commit 8bd306a

File tree

3 files changed

+225
-65
lines changed

3 files changed

+225
-65
lines changed

docker/scripts/dev_into.sh

Lines changed: 82 additions & 55 deletions
Original file line numberDiff line numberDiff line change
@@ -16,88 +16,95 @@
1616
# limitations under the License.
1717
###############################################################################
1818

19-
set -euo pipefail # Added for robustness
19+
set -euo pipefail
2020

2121
# --- Constants ---
2222
DEV_CONTAINER_PREFIX='apollo_dev_'
23-
DEV_INSIDE="in-dev-docker" # Consistent with start_container.sh
23+
DEV_INSIDE="in-dev-docker"
2424

25-
# --- Global Variables (will be set by parse_arguments) ---
26-
# DOCKER_USER will be the username used for 'docker exec -u'
27-
# Default to host's USER, can be overridden by --user
25+
# --- Global Variables ---
2826
DOCKER_USER="${USER}"
29-
# DEV_CONTAINER will be the name of the container to connect to
30-
# Default to apollo_dev_host_user, can be overridden by --user or -n
31-
DEV_CONTAINER="${DEV_CONTAINER_PREFIX}${USER}" # Initialize with default
32-
33-
# Placeholder for user-specified container name via -n
27+
DEV_CONTAINER="${DEV_CONTAINER_PREFIX}${USER}"
3428
USER_EXPLICIT_CONTAINER_NAME=""
29+
#
30+
# === [MODIFICATION 1]: Add a new global variable for the command ===
31+
#
32+
NON_INTERACTIVE_CMD=""
3533

3634
# --- Helper Functions ---
3735
function show_usage() {
3836
cat <<EOF
3937
Usage: $0 [options]
4038
Connects to a running Apollo development Docker container.
4139
40+
MODES:
41+
Interactive (default): If no command is provided, starts an interactive bash session.
42+
Non-Interactive: If a command is provided, executes it inside the container and exits.
43+
4244
OPTIONS:
43-
-h, --help Display this help and exit.
44-
-n, --name <container_name> Specify the *full* name of the docker container to connect to.
45-
Overrides default naming based on user.
46-
--user <username> Specify the username to log into inside the container.
47-
This also influences the container name if -n is not used.
48-
(Default: current host user for both container name and login user).
45+
-h, --help Display this help and exit.
46+
-n, --name <container_name> Specify the *full* name of the container to connect to.
47+
-c, --command <"command"> Specify a command to execute in non-interactive mode.
48+
--user <username> Specify the username for the container session.
49+
Influences default container name if -n is not used.
4950
EOF
5051
}
5152

5253
function parse_arguments() {
5354
local opt_n_value=""
5455
local opt_user_value=""
5556

57+
#
58+
# === [MODIFICATION 2]: Update argument parsing logic ===
59+
# Using a while loop to process options, allowing a command to be passed at the end.
60+
# The previous logic would fail if options and commands were mixed.
61+
#
5662
while [[ $# -gt 0 ]]; do
57-
local opt="$1"
58-
shift
59-
case "${opt}" in
63+
case "$1" in
6064
-h | --help)
6165
show_usage
6266
exit 0
6367
;;
6468
-n | --name)
65-
opt_n_value="$1"
6669
shift
67-
if [[ -z "${opt_n_value}" ]]; then
68-
echo "Error: Missing argument for -n/--name." >&2
69-
exit 1
70+
if [[ -z "${1-}" ]]; then
71+
echo "Error: Missing argument for -n/--name." >&2; exit 1
7072
fi
71-
USER_EXPLICIT_CONTAINER_NAME="${opt_n_value}" # <-- Here's the change: no prefixing
73+
USER_EXPLICIT_CONTAINER_NAME="$1"
74+
shift
7275
;;
7376
--user)
74-
opt_user_value="$1"
7577
shift
76-
if [[ -z "${opt_user_value}" ]]; then
77-
echo "Error: Missing argument for --user." >&2
78-
exit 1
78+
if [[ -z "${1-}" ]]; then
79+
echo "Error: Missing argument for --user." >&2; exit 1
7980
fi
80-
DOCKER_USER="${opt_user_value}"
81+
DOCKER_USER="$1"
82+
shift
83+
;;
84+
-c | --command)
85+
shift
86+
if [[ -z "${1-}" ]]; then
87+
echo "Error: Missing argument for -c/--command." >&2; exit 1
88+
fi
89+
NON_INTERACTIVE_CMD="$1"
90+
shift
8191
;;
8292
*)
83-
echo "Error: Unknown option: ${opt}" >&2
84-
show_usage
85-
exit 1
93+
# If an unknown option is found, assume it's the start of the command
94+
NON_INTERACTIVE_CMD="$*"
95+
break # Exit the loop, the rest of the arguments are the command
8696
;;
8797
esac
8898
done
8999

100+
90101
# --- Determine the final DEV_CONTAINER name ---
91-
# Priority: -n (explicit full name) > --user (implied prefixed name) > host USER (default prefixed name)
92102
if [[ -n "${USER_EXPLICIT_CONTAINER_NAME}" ]]; then
93103
DEV_CONTAINER="${USER_EXPLICIT_CONTAINER_NAME}"
94-
elif [[ -n "${opt_user_value}" ]]; then
95-
# If --user was provided, but -n was not, use --user's value for container name suffix
96-
DEV_CONTAINER="${DEV_CONTAINER_PREFIX}${opt_user_value}"
97-
else
98-
# Fallback to host's USER for container name suffix (default)
99-
DEV_CONTAINER="${DEV_CONTAINER_PREFIX}${USER}"
100-
fi
104+
elif [[ "${DOCKER_USER}" != "${USER}" ]]; then
105+
# If --user was provided AND it's different from the host user, use it for the container name
106+
DEV_CONTAINER="${DEV_CONTAINER_PREFIX}${DOCKER_USER}"
107+
fi # Otherwise, the default DEV_CONTAINER="${DEV_CONTAINER_PREFIX}${USER}" is used
101108
}
102109

103110
function restart_stopped_container() {
@@ -120,23 +127,43 @@ function restart_stopped_container() {
120127

121128
# --- Main Script Execution ---
122129

123-
xhost +local:root 1>/dev/null 2>&1 || { echo "Warning: xhost command failed. Display may not work." >&2; }
124-
125130
parse_arguments "$@"
126131

127132
restart_stopped_container # Ensures the target container is running
128133

129-
echo "Connecting to container '${DEV_CONTAINER}' as user '${DOCKER_USER}'..."
130-
131-
# Execute bash inside the container
132-
docker exec \
133-
-u "${DOCKER_USER}" \
134-
-e HISTFILE=/apollo/.dev_bash_hist \
135-
-it "${DEV_CONTAINER}" \
136-
/bin/bash
137-
138-
# Cleanup xhost (execute only if docker exec was successful, or cleanup anyway)
139-
# The `|| true` ensures the cleanup command runs even if docker exec fails.
140-
xhost -local:root 1>/dev/null 2>&1 || true
141134

142-
echo "Disconnected from container."
135+
#
136+
# === [MODIFICATION 3]: Add the core logic to switch between modes ===
137+
#
138+
if [[ -n "${NON_INTERACTIVE_CMD}" ]]; then
139+
# === NON-INTERACTIVE MODE ===
140+
echo "Executing command in container '${DEV_CONTAINER}':"
141+
echo " ${NON_INTERACTIVE_CMD}"
142+
143+
# Use 'docker exec' without '-it'. The command is passed as arguments to /bin/bash -c
144+
docker exec \
145+
-u "${DOCKER_USER}" \
146+
"${DEV_CONTAINER}" \
147+
/bin/bash -c "${NON_INTERACTIVE_CMD}"
148+
149+
echo "Command execution finished."
150+
151+
else
152+
# === INTERACTIVE MODE ===
153+
echo "Connecting to container '${DEV_CONTAINER}' as user '${DOCKER_USER}'..."
154+
155+
# Add display access for GUI applications
156+
xhost +local:root &>/dev/null || true
157+
158+
# Use 'docker exec' with '-it' for an interactive terminal.
159+
docker exec \
160+
-u "${DOCKER_USER}" \
161+
-e HISTFILE=/apollo/.dev_bash_hist \
162+
-it \
163+
"${DEV_CONTAINER}" \
164+
/bin/bash
165+
166+
# Clean up display access after session ends
167+
xhost -local:root &>/dev/null || true
168+
echo "Disconnected from container."
169+
fi

docker/setup_host/config_system.sh

Lines changed: 113 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ set -euo pipefail
1717
# --- Global Variables ---
1818
# Determine the absolute path to Apollo root directory.
1919
# Assumes this script is located at <APOLLO_ROOT_DIR>/docker/setup_host/config_system.sh
20-
APOLLO_ROOT_DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )/../.." && pwd )"
20+
APOLLO_ROOT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")/../.." && pwd)"
2121
# use relative path for multiple instances
2222
CORE_DUMP_DIR="data/core"
2323
CORE_DUMP_CONF_FILE="/etc/sysctl.d/99-core-dump.conf"
@@ -26,6 +26,13 @@ UVCVIDEO_CONF_FILE="/etc/modprobe.d/uvcvideo.conf"
2626
UDEV_RULES_SRC_DIR="${APOLLO_ROOT_DIR}/docker/setup_host/etc/udev/rules.d"
2727
UDEV_RULES_DEST_DIR="/etc/udev/rules.d"
2828

29+
# Source path of the service file within the project
30+
AUTOSERVICE_SRC_FILE="${APOLLO_ROOT_DIR}/docker/setup_host/etc/systemd/system/autostart.service"
31+
# Destination path for systemd services
32+
AUTOSERVICE_DEST_FILE="/etc/systemd/system/autostart.service"
33+
# User who will run the autonomous driving stack. SUDO_USER is the user who invoked sudo.
34+
WHL_HOST_USER="${SUDO_USER:-$(whoami)}"
35+
2936
# --- Color Definitions for Output ---
3037
BOLD='\033[1m'
3138
RED='\033[0;31m'
@@ -57,8 +64,8 @@ check_host_setup_pre_conditions() {
5764

5865
# 1. Check for root privileges
5966
if [ "$(id -u)" -ne 0 ]; then
60-
error "This script must be run with root privileges (sudo)."
61-
return 1
67+
error "This script must be run with root privileges (sudo)."
68+
return 1
6269
fi
6370

6471
# 2. Check if APOLLO_ROOT_DIR exists
@@ -87,6 +94,14 @@ check_host_setup_pre_conditions() {
8794
fi
8895
info "Udev rules source directory detected."
8996

97+
# 5. Check for existence of autostart service file
98+
if [ ! -f "${AUTOSERVICE_SRC_FILE}" ]; then
99+
error "Autostart service file not found at '${AUTOSERVICE_SRC_FILE}'."
100+
error "Please ensure the service definition file is present."
101+
return 1
102+
fi
103+
info "Autostart service source file detected."
104+
90105
success "All host setup pre-conditions met."
91106
return 0
92107
}
@@ -96,8 +111,8 @@ setup_core_dump() {
96111
info "Setting up core dump format..."
97112

98113
# Check if core dump configuration is already applied
99-
if [ -f "${CORE_DUMP_CONF_FILE}" ] && grep -q "kernel.core_pattern = ${CORE_DUMP_DIR}/core_%e.%p" "${CORE_DUMP_CONF_FILE}" && \
100-
[[ "$(sudo sysctl -n kernel.core_pattern)" == "${CORE_DUMP_DIR}/core_%e.%p" ]]; then
114+
if [ -f "${CORE_DUMP_CONF_FILE}" ] && grep -q "kernel.core_pattern = ${CORE_DUMP_DIR}/core_%e.%p" "${CORE_DUMP_CONF_FILE}" &&
115+
[[ "$(sudo sysctl -n kernel.core_pattern)" == "${CORE_DUMP_DIR}/core_%e.%p" ]]; then
101116
info "Core dump configuration already detected and active. Skipping."
102117
return 0
103118
fi
@@ -113,10 +128,13 @@ setup_core_dump() {
113128
fi
114129

115130
# Ensure the directory has appropriate permissions for core dumps
116-
sudo chmod 0777 "${CORE_DUMP_DIR}" || { error "Failed to set permissions for core dump directory: ${CORE_DUMP_DIR}."; return 1; }
131+
sudo chmod 0777 "${CORE_DUMP_DIR}" || {
132+
error "Failed to set permissions for core dump directory: ${CORE_DUMP_DIR}."
133+
return 1
134+
}
117135

118136
info "Writing core dump configuration to ${CORE_DUMP_CONF_FILE}..."
119-
sudo tee "${CORE_DUMP_CONF_FILE}" > /dev/null <<EOF
137+
sudo tee "${CORE_DUMP_CONF_FILE}" > /dev/null << EOF
120138
kernel.core_pattern = ${CORE_DUMP_DIR}/core_%e.%p
121139
EOF
122140
if [ $? -ne 0 ]; then
@@ -152,7 +170,10 @@ setup_bazel_cache_dir() {
152170
fi
153171

154172
# Ensure permissions are appropriate for a shared cache
155-
sudo chmod a+rwx "${BAZEL_CACHE_DIR}" || { error "Failed to set permissions for Bazel cache directory."; return 1; }
173+
sudo chmod a+rwx "${BAZEL_CACHE_DIR}" || {
174+
error "Failed to set permissions for Bazel cache directory."
175+
return 1
176+
}
156177

157178
success "Bazel cache directory configured."
158179
return 0
@@ -263,7 +284,7 @@ configure_uvcvideo_module() {
263284
if [ -f "${UVCVIDEO_CONF_FILE}" ] && grep -q "options uvcvideo clock=realtime" "${UVCVIDEO_CONF_FILE}"; then
264285
info "uvcvideo clock configuration already detected. Skipping write."
265286
else
266-
sudo tee "${UVCVIDEO_CONF_FILE}" > /dev/null <<EOF
287+
sudo tee "${UVCVIDEO_CONF_FILE}" > /dev/null << EOF
267288
options uvcvideo clock=realtime
268289
EOF
269290
if [ $? -ne 0 ]; then
@@ -323,6 +344,82 @@ add_user_to_docker_group() {
323344
success "User '${user}' added to the Docker group. Please log out and back in for changes to take effect."
324345
}
325346

347+
# Installs and enables the autostart systemd service.
348+
install_autostart_service() {
349+
info "Installing and configuring the autostart systemd service..."
350+
351+
# 1: Hard Fail on Root Installation (Security Best Practice) ---
352+
# Running the main AD stack as root is a major security risk.
353+
# We now block this by default and require an explicit, intentional override.
354+
if [[ "${WHL_HOST_USER}" == "root" ]]; then
355+
error "Running the autonomous driving stack as 'root' is not allowed for security reasons."
356+
error "To override this critical check, set the environment variable ALLOW_ROOT_INSTALL=yes and re-run."
357+
if [[ "${ALLOW_ROOT_INSTALL:-no}" != "yes" ]]; then
358+
return 1 # Hard fail, aborting the installation.
359+
else
360+
info "ALLOW_ROOT_INSTALL=yes detected. Proceeding with installation as root. THIS IS NOT RECOMMENDED."
361+
fi
362+
fi
363+
364+
# 2: Input Validation for Username (Prevent Command Injection) ---
365+
# Ensure the username consists only of safe, alphanumeric characters.
366+
if ! [[ "${WHL_HOST_USER}" =~ ^[a-zA-Z0-9_][a-zA-Z0-9_-]*$ ]]; then
367+
error "Invalid username detected: '${WHL_HOST_USER}'. Usernames must be alphanumeric (with _ or -)."
368+
error "Aborting installation to prevent potential command injection."
369+
return 1
370+
fi
371+
info "Target user '${WHL_HOST_USER}' validated."
372+
373+
# 3: Input Validation for Path (Prevent Command Injection) ---
374+
# Ensure the path is a valid absolute path and does not contain characters
375+
# that could break the sed command or be interpreted by the shell.
376+
if ! [[ "${APOLLO_ROOT_DIR}" =~ ^/[a-zA-Z0-9_/.-]+$ ]]; then
377+
error "Invalid APOLLO_ROOT_DIR detected: '${APOLLO_ROOT_DIR}'."
378+
error "Path must be absolute and contain only safe characters (alphanumeric, /, _, ., -)."
379+
error "Aborting installation to prevent potential command injection."
380+
return 1
381+
fi
382+
info "Apollo root directory '${APOLLO_ROOT_DIR}' validated."
383+
384+
info "Copying '${AUTOSERVICE_SRC_FILE}' to '${AUTOSERVICE_DEST_FILE}'..."
385+
if ! cp "${AUTOSERVICE_SRC_FILE}" "${AUTOSERVICE_DEST_FILE}"; then
386+
error "Failed to copy service file. Check permissions."
387+
return 1
388+
fi
389+
390+
# 4: Safer Replacement Method ---
391+
# By using a different delimiter for sed (like '#'), we make the replacement
392+
# robust even if the path contains the default '/' delimiter.
393+
# The validation above already mitigates this, but this is a defense-in-depth measure.
394+
info "Customizing service file with user='${WHL_HOST_USER}' and path='${APOLLO_ROOT_DIR}'..."
395+
if ! sed -i "s#__USER__#${WHL_HOST_USER}#g" "${AUTOSERVICE_DEST_FILE}"; then
396+
error "Failed to replace user placeholder in service file."
397+
return 1
398+
fi
399+
if ! sed -i "s#__APOLLO_ROOT_DIR__#${APOLLO_ROOT_DIR}#g" "${AUTOSERVICE_DEST_FILE}"; then
400+
error "Failed to replace path placeholder in service file."
401+
return 1
402+
fi
403+
404+
# Reload the systemd daemon
405+
info "Reloading systemd daemon..."
406+
if ! systemctl daemon-reload; then
407+
error "Failed to reload systemd daemon. Run 'journalctl -xe' for details."
408+
return 1
409+
fi
410+
411+
# Enable the service
412+
info "Enabling 'autostart.service' to run on boot..."
413+
if ! systemctl enable autostart.service; then
414+
error "Failed to enable autostart.service."
415+
return 1
416+
fi
417+
418+
success "Autostart service installed and enabled successfully."
419+
info "The autonomous driving system will now start automatically on the next boot."
420+
return 0
421+
}
422+
326423
# --- Main Host Setup Orchestration Function ---
327424
# This is the primary entry point for setting up the host machine.
328425
setup_host_machine() {
@@ -370,6 +467,12 @@ setup_host_machine() {
370467
return 1
371468
fi
372469

470+
# 8. Install and enable the autostart service
471+
if ! install_autostart_service; then
472+
error "Failed to install the autostart service. Host setup is incomplete."
473+
return 1
474+
fi
475+
373476
success "Host machine setup completed successfully!"
374477
return 0 # Final success
375478
}
@@ -382,7 +485,7 @@ main() {
382485
# Any other argument will result in an error message.
383486
if [ "$#" -eq 0 ] || [ "$1" == "install" ]; then
384487
if [ "$#" -eq 0 ]; then
385-
info "No argument provided. Defaulting to 'install' mode."
488+
info "No argument provided. Defaulting to 'install' mode."
386489
fi
387490
setup_host_machine
388491
else

0 commit comments

Comments
 (0)