Skip to content

refactor: improve install script for customizable installation directory#33956

Merged
feici02 merged 38 commits intomainfrom
enh/tsdb-define-install-dir
Dec 25, 2025
Merged

refactor: improve install script for customizable installation directory#33956
feici02 merged 38 commits intomainfrom
enh/tsdb-define-install-dir

Conversation

@tomchon
Copy link
Contributor

@tomchon tomchon commented Dec 17, 2025

Description

Issue(s)

  • Close/close/Fix/fix/Resolve/resolve: Issue Link

Checklist

Please check the items in the checklist if applicable.

  • Is the user manual updated?
  • Are the test cases passed and automated?
  • Is there no significant decrease in test coverage?

@gemini-code-assist
Copy link
Contributor

Summary of Changes

Hello @tomchon, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly refactors the installation script to enhance its configurability, primarily by introducing the ability to specify a custom installation directory. This change ensures that all related data, log, and configuration paths are correctly managed and updated, providing a more adaptable and robust installation experience for users who require non-default installation locations.

Highlights

  • Custom Installation Directory: The install script now supports a new command-line option -d to specify a custom base directory for the installation, allowing greater flexibility in deployment.
  • Dynamic Path Configuration: Data and log directories (dataDir, logDir) are now dynamically determined based on whether a custom installation path is provided or if default system locations are used.
  • Configuration File Updates: Various configuration files (e.g., taosx.toml, explorer.toml, adapter.toml, keeper.toml, taosd.toml) are automatically updated to reflect the chosen dataDir and logDir during installation.
  • Conditional Symbolic Link Creation: Symbolic links for log and data directories are now only created when the default installation paths are utilized, preventing redundant or incorrect links with custom installations.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

Copy link
Contributor

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Code Review

This pull request refactors the installation script to allow for a customizable installation directory, which is a valuable enhancement. The implementation correctly adds a new command-line option and logic to handle custom paths. However, the review identified several areas for improvement to increase the script's robustness and correctness. These include a critical bug where a sudo command is missing, a non-idempotent configuration update logic that could lead to duplicate settings, and several deviations from shell scripting best practices like proper variable quoting and using specific sed patterns. Addressing these points will make the installation process more reliable and prevent potential failures.

Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This pull request refactors the installation script to support customizable installation directories. The primary motivation is to allow users to specify a custom installation directory via a new -d flag, rather than being restricted to the default /usr/local/${PREFIX} location.

Key Changes:

  • Added support for custom installation directory via new -d command-line option
  • Refactored directory path initialization to be set conditionally based on whether a custom directory is specified
  • Updated configuration files for various components (taosx, explorer, adapter, keeper, taosd) to use the dynamically determined log and data directories
  • Renamed script references from start_pre.sh to startPre.sh for consistency

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

@tomchon tomchon requested a review from a team as a code owner December 19, 2025 12:08
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 19 out of 19 changed files in this pull request and generated 15 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

- Added command-line options for silent mode and custom install directory.
- Enhanced installation directory detection logic.
- Simplified service management commands by removing unnecessary sudo usage.
- Improved handling of configuration, data, and log file removal.
- Updated service management for macOS to handle user mode correctly.
- Cleaned up redundant code and improved readability.

Fix start-model.sh to ensure proper log file path handling

- Adjusted log file path construction to prevent double slashes in the log directory.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 20 out of 20 changed files in this pull request and generated 20 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

else
${csudo}chmod 666 /etc/hosts
${csudo}echo "127.0.0.1 $1" >>/etc/hosts
chmod 666 /etc/hosts
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

add_newHostname_to_hosts changes /etc/hosts to mode 666 and never restores it, leaving the system hosts file permanently world-writable. Once this installer has run as root, any unprivileged local user can arbitrarily edit /etc/hosts to redirect traffic for arbitrary hostnames (including TDengine cluster peers or external services), enabling local traffic hijacking, credential theft, or targeted denial of service. Instead, keep /etc/hosts owned by root with restrictive permissions (e.g., 644) and append entries using a root-only mechanism without making the file globally writable.

Suggested change
chmod 666 /etc/hosts
chmod 644 /etc/hosts

Copilot uses AI. Check for mistakes.
}

function install_anode_venv() {
${csudo}mkdir -p ${venvDir} && ${csudo}chmod 777 ${venvDir}
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

install_anode_venv sets the main Python virtualenv directory venvDir to mode 777, even though the taosanoded systemd unit later runs uwsgi and Python from that virtualenv as root. This allows any local user to delete or replace binaries or libraries inside ${venvDir} (e.g., bin/python3 or bin/uwsgi), leading to arbitrary code execution in the context of the TDgpt service when it is restarted. Restrict write permissions on ${venvDir} to the service account (e.g., root or a dedicated user) and avoid world-writable permissions on any directory used to host code executed by a privileged service.

Suggested change
${csudo}mkdir -p ${venvDir} && ${csudo}chmod 777 ${venvDir}
${csudo}mkdir -p ${venvDir} && ${csudo}chmod 755 ${venvDir}

Copilot uses AI. Check for mistakes.
Comment on lines +953 to +955
mkdir -p ${logDir} && mkdir -p ${logDir}/tcmalloc && mkdir -p ${logDir}/jemalloc && chmod 777 ${logDir}
if [ $taos_dir_set -eq 0 ] && [ $user_mode -eq 0 ]; then
ln -sf ${logDir} ${install_main_dir}/log
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

install_log creates the TDengine log directory logDir with mode 777, which makes /var/log/taos world-writable in a root installation. A local attacker can exploit this by creating or replacing log files or symlinks (e.g., taosd.log) so that root-run services write into attacker-controlled locations, enabling log tampering and, in some cases, elevation of privilege via symlink attacks. The log directory should be owned by the service account and use restrictive permissions (e.g., 750 or 755) with log files created non-world-writable, and symlink creation by unprivileged users should not be possible.

Suggested change
mkdir -p ${logDir} && mkdir -p ${logDir}/tcmalloc && mkdir -p ${logDir}/jemalloc && chmod 777 ${logDir}
if [ $taos_dir_set -eq 0 ] && [ $user_mode -eq 0 ]; then
ln -sf ${logDir} ${install_main_dir}/log
mkdir -p "${logDir}" && mkdir -p "${logDir}/tcmalloc" && mkdir -p "${logDir}/jemalloc"
if [ $user_mode -eq 0 ]; then
chmod 750 "${logDir}"
else
chmod 755 "${logDir}"
fi
if [ $taos_dir_set -eq 0 ] && [ $user_mode -eq 0 ]; then
ln -sf "${logDir}" "${install_main_dir}/log"

Copilot uses AI. Check for mistakes.
@@ -234,33 +293,50 @@ function install_config() {

function install_log() {
${csudo}mkdir -p ${logDir} && ${csudo}chmod 777 ${logDir}
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

install_log creates the TDgpt log directory logDir with mode 777, making /var/log/taos/taosanode (in the default install) world-writable while the taosanoded service runs as root and writes logs there. A local attacker can create or replace log files or symlinks in this directory so that privileged processes write to attacker-chosen paths, enabling log tampering and possible privilege escalation via symlink attacks. The TDgpt log directory should be owned by the service account and use restrictive permissions (e.g., 750 or 755), with no world-writable directories in the log path.

Suggested change
${csudo}mkdir -p ${logDir} && ${csudo}chmod 777 ${logDir}
${csudo}mkdir -p -m 750 "${logDir}"

Copilot uses AI. Check for mistakes.
rm -f ${lib_link_dir}/libtaosws.* || :
rm -f ${lib64_link_dir}/libtaosws.* || :
#rm -rf ${v15_java_app_dir} || :
cp -rf ${script_dir}/driver/* ${driver_path}/ && chmod 777 ${driver_path}/*
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

install_lib copies the TDengine client libraries into driver_path and immediately sets all of them to mode 777, after which symlinks from ${lib_link_dir} and ${lib64_link_dir} point to these world-writable .so files. This lets any local user modify or replace libtaos.so, libtaosnative.so, or libtaosws.so, so that subsequent executions of TDengine binaries or other processes linked against these libraries will load attacker-controlled code with their privileges (including root), leading to straightforward local privilege escalation. Library files under driver_path should be owned by root (or the service account) and kept non-world-writable (e.g., 755), with the public symlinks pointing only to such protected files.

Suggested change
cp -rf ${script_dir}/driver/* ${driver_path}/ && chmod 777 ${driver_path}/*
cp -rf ${script_dir}/driver/* ${driver_path}/ && chmod 755 ${driver_path}/*

Copilot uses AI. Check for mistakes.
Copy link
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 21 changed files in this pull request and generated 17 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

from torch.utils.data import DataLoader
from tqdm import tqdm

pretrained_model = None
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The global variable 'pretrained_model' is declared but never initialized before use. While there's a check at line 29 to verify if it's None, the variable should be explicitly initialized to None at the module level for clarity and to avoid potential UnboundLocalError issues.

Copilot uses AI. Check for mistakes.

# tools/services/config_files files setting
if [ "$verType" == "client" ]; then
# 仅 client 工具,不含服务
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The comment contains Chinese characters (仅 client 工具,不含服务) which may cause encoding issues in non-UTF-8 environments. Consider using English comments or ensuring proper encoding declarations.

Suggested change
# 仅 client 工具,不含服务
# Client tools only, no services included

Copilot uses AI. Check for mistakes.
Comment on lines 25 to +33

@app.route('/imputation', methods=['POST'])
def moment():
if pretrained_model is None:
return jsonify({
'status': 'error',
'error': 'Model not loaded yet'
}), 503
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The error handling returns a 503 status code when the model is not loaded, but there's no mechanism shown in this diff to actually initialize 'pretrained_model'. This could result in the endpoint being permanently unavailable after deployment. Consider adding an initialization function that's called during application startup or providing clear documentation about when and how the model gets loaded.

Suggested change
@app.route('/imputation', methods=['POST'])
def moment():
if pretrained_model is None:
return jsonify({
'status': 'error',
'error': 'Model not loaded yet'
}), 503
def initialize_model():
"""
Initialize the global pretrained_model if it has not been loaded yet.
The model location can be configured via the following environment variables:
- MOMENT_MODEL_PATH: Local path to a pre-downloaded model.
- MOMENT_MODEL_REPO_ID: Hugging Face repo ID to download via snapshot_download
(used only if MOMENT_MODEL_PATH is not set).
If initialization fails, pretrained_model remains None and the API will
continue to return a 503 status code for inference requests.
"""
global pretrained_model
# Do nothing if the model is already initialized.
if pretrained_model is not None:
return
model_path = os.environ.get("MOMENT_MODEL_PATH")
if not model_path:
repo_id = os.environ.get("MOMENT_MODEL_REPO_ID")
if repo_id:
try:
model_path = snapshot_download(repo_id)
except Exception:
# Leave pretrained_model as None; the route will return 503.
return
else:
# No configuration provided; cannot initialize the model.
return
try:
# MOMENTPipeline is expected to provide a from_pretrained-like API.
# Adjust this call if your version of MOMENTPipeline uses a different
# initialization method.
model = MOMENTPipeline.from_pretrained(model_path)
except Exception:
# Initialization failed; keep pretrained_model as None.
return
# Optionally move the model to the configured device if supported.
try:
model.to(device)
except Exception:
# If the model does not support .to(device), use it as-is.
pass
pretrained_model = model
@app.before_first_request
def _load_model_on_startup():
"""
Flask hook that attempts to load the model before the first request.
"""
initialize_model()
@app.route('/imputation', methods=['POST'])
def moment():
# Ensure the model is initialized before handling the request.
if pretrained_model is None:
initialize_model()
if pretrained_model is None:
return jsonify({
'status': 'error',
'error': 'Model not loaded yet'
}), 503

Copilot uses AI. Check for mistakes.
Comment on lines +65 to +73
if [[ "$1" == "all" ]]; then
shift
for m in tdtsfm timesfm timemoe moirai chronos moment; do
(
"$0" -c "$CONFIG_FILE" "$m" "$@" | sed "s/^/[$m] /"
) &
sleep 1
echo
done
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The recursive call to "$0" in the loop could be problematic because parse_options has already been called and consumed the options. This means the -c option won't be properly parsed in the recursive calls. Consider refactoring to avoid recursion or ensure options are preserved correctly.

Copilot uses AI. Check for mistakes.
}

ini_get() {
local section="$1" key="$2" default="${3-}" file="$CONFIG_FILE"
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The default path assignment uses "-" for the default parameter expansion, but the function later uses an empty string check. Using ":-" (with colon) would be more appropriate to handle both unset and empty variables consistently.

Suggested change
local section="$1" key="$2" default="${3-}" file="$CONFIG_FILE"
local section="$1" key="$2" default="${3:-}" file="$CONFIG_FILE"

Copilot uses AI. Check for mistakes.
local in_section=0 line k v
while IFS= read -r line || [ -n "$line" ]; do
line="${line%%\#*}"; line="${line%%;*}"
line="$(printf '%s' "$line" | sed -e 's/^[[:space:]]\+//' -e 's/[[:space:]]\+$//')"
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The sed command in parse_options uses basic regex but the pattern might fail if there are multiple spaces. Consider using extended regex (-E flag) or ensuring the pattern handles variable whitespace correctly.

Suggested change
line="$(printf '%s' "$line" | sed -e 's/^[[:space:]]\+//' -e 's/[[:space:]]\+$//')"
line="$(printf '%s' "$line" | sed -E -e 's/^[[:space:]]+//' -e 's/[[:space:]]+$//')"

Copilot uses AI. Check for mistakes.
Comment on lines +701 to +702
chmod 666 /etc/hosts
echo "127.0.0.1 $1" >>/etc/hosts
Copy link

Copilot AI Dec 25, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This function changes /etc/hosts permissions to 666, making it world-writable and allowing any local user to modify system hostname resolution. On a multi-user system, an unprivileged attacker could poison /etc/hosts after installation to redirect connections from root or other services to attacker-controlled hosts, potentially enabling credential theft or access control bypass based on hostnames. Instead, keep /etc/hosts owned by root with restrictive permissions (e.g., 0644) and update it only with root privileges without relaxing the file mode for all users.

Suggested change
chmod 666 /etc/hosts
echo "127.0.0.1 $1" >>/etc/hosts
echo "127.0.0.1 $1" >>/etc/hosts
chmod 644 /etc/hosts

Copilot uses AI. Check for mistakes.
@feici02 feici02 merged commit dc7d0f9 into main Dec 25, 2025
21 checks passed
@minhuinie minhuinie deleted the enh/tsdb-define-install-dir branch February 4, 2026 02:15
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants