RitchieSolo

Xanmod4Slack

Jul 27th, 2026
52
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 52.75 KB | Software | 0 0
  1. #!/bin/bash
  2. # Xanmod4Slack - Universal XanMod Kernel Builder and Package Manager
  3. # Compatible with: Slackware, Debian, Devuan, and derivatives
  4. # Supports: systemd, OpenRC, runit, dinit, shepherd, sysvinit
  5. # Supports: GRUB2, GRUB Legacy, LILO, ELILO, SYSLINUX, systemd-boot, EFISTUB
  6. # Version: 2.1.0 (bugfix revision)
  7.  
  8. set -o pipefail
  9.  
  10. # --- Color definitions for terminal output ---
  11. RED='\033[0;31m'
  12. GREEN='\033[0;32m'
  13. YELLOW='\033[0;33m'
  14. BLUE='\033[0;34m'
  15. NC='\033[0m' # No Color
  16.  
  17. # --- Configuration ---
  18. VERSION="2.1.0"
  19. CONFIG_DIR="${HOME}/.config/xanmod4slack"
  20. CONFIG_FILE="${CONFIG_DIR}/xanmod4slack.conf"
  21. LOG_DIR="${HOME}/.local/log/xanmod4slack"
  22. LOG_FILE="${LOG_DIR}/xanmod4slack.log"
  23. WORK_DIR="${HOME}/src/xanmod"
  24. XANMOD_REPO="https://github.com/xanmod/linux.git"
  25. XANMOD_BRANCH="master"
  26. NOTIFICATION_SENT_FILE="${CONFIG_DIR}/last_notification"
  27.  
  28. # --- Default Configuration Values ---
  29. DEFAULT_UPDATE_FREQ="weekly"
  30. DEFAULT_INTERFACE="auto"
  31. DEFAULT_KEEP_OLD="3"
  32. DEFAULT_AUTO_UPDATE="false"
  33. DEFAULT_AUTO_CLEANUP="false"
  34. DEFAULT_SILENT_NOTIFICATIONS="false"
  35. DEFAULT_DETECT_HARDWARE="true"
  36. DEFAULT_CUSTOM_CONFIG=""
  37. DEFAULT_MARCH="native"
  38.  
  39. # --- Initialize directories ---
  40. mkdir -p "${CONFIG_DIR}" "${LOG_DIR}" "${WORK_DIR}"
  41. touch "${LOG_FILE}"
  42.  
  43. # --- Functions ---
  44.  
  45. # FIX: log() now writes ONLY to the log file and to stderr (fd 2).
  46. # The original wrote to stdout via `tee`, which silently corrupted
  47. # every function whose return value was captured with $(function_name),
  48. # since log messages got mixed into the captured string.
  49. log() {
  50. local msg="[$(date '+%Y-%m-%d %H:%M:%S')] $1"
  51. echo "$msg" >> "${LOG_FILE}"
  52. echo "$msg" >&2
  53. }
  54.  
  55. # error() is for genuinely fatal, top-level failures only.
  56. # It is intentionally NOT called from inside functions that are invoked
  57. # via command substitution $(...), because exit inside a subshell only
  58. # kills that subshell, not the parent script (see functions below, which
  59. # use `log "ERROR: ..."; return 1` instead and let the caller decide).
  60. error() {
  61. log "ERROR: $1"
  62. if [ -n "$2" ]; then
  63. show_msg "Error" "$1" "error"
  64. fi
  65. exit 1
  66. }
  67.  
  68. # FIX: added — nothing in the original checked for root before doing
  69. # privileged work (installing packages, writing to /boot, editing
  70. # bootloader configs). It would just fail confusingly partway through.
  71. require_root() {
  72. if [ "$(id -u)" -ne 0 ]; then
  73. log "ERROR: This action requires root privileges. Re-run with sudo."
  74. return 1
  75. fi
  76. return 0
  77. }
  78.  
  79. load_config() {
  80. if [ -f "${CONFIG_FILE}" ]; then
  81. source "${CONFIG_FILE}"
  82. log "Loaded configuration from ${CONFIG_FILE}"
  83. else
  84. log "No configuration file found. Using defaults."
  85. UPDATE_FREQ="${DEFAULT_UPDATE_FREQ}"
  86. INTERFACE="${DEFAULT_INTERFACE}"
  87. KEEP_OLD="${DEFAULT_KEEP_OLD}"
  88. AUTO_UPDATE="${DEFAULT_AUTO_UPDATE}"
  89. AUTO_CLEANUP="${DEFAULT_AUTO_CLEANUP}"
  90. SILENT_NOTIFICATIONS="${DEFAULT_SILENT_NOTIFICATIONS}"
  91. DETECT_HARDWARE="${DEFAULT_DETECT_HARDWARE}"
  92. CUSTOM_CONFIG="${DEFAULT_CUSTOM_CONFIG}"
  93. MARCH="${DEFAULT_MARCH}"
  94. fi
  95. }
  96.  
  97. save_config() {
  98. cat > "${CONFIG_FILE}" << EOF
  99. # Xanmod4Slack Configuration
  100. UPDATE_FREQ="${UPDATE_FREQ:-$DEFAULT_UPDATE_FREQ}"
  101. INTERFACE="${INTERFACE:-$DEFAULT_INTERFACE}"
  102. KEEP_OLD="${KEEP_OLD:-$DEFAULT_KEEP_OLD}"
  103. AUTO_UPDATE="${AUTO_UPDATE:-$DEFAULT_AUTO_UPDATE}"
  104. AUTO_CLEANUP="${AUTO_CLEANUP:-$DEFAULT_AUTO_CLEANUP}"
  105. SILENT_NOTIFICATIONS="${SILENT_NOTIFICATIONS:-$DEFAULT_SILENT_NOTIFICATIONS}"
  106. DETECT_HARDWARE="${DETECT_HARDWARE:-$DEFAULT_DETECT_HARDWARE}"
  107. CUSTOM_CONFIG="${CUSTOM_CONFIG:-$DEFAULT_CUSTOM_CONFIG}"
  108. MARCH="${MARCH:-$DEFAULT_MARCH}"
  109. EOF
  110. log "Configuration saved to ${CONFIG_FILE}"
  111. }
  112.  
  113. detect_distro() {
  114. if [ -f /etc/debian_version ]; then
  115. echo "debian"
  116. elif [ -f /etc/slackware-version ]; then
  117. echo "slackware"
  118. else
  119. echo "unknown"
  120. fi
  121. }
  122.  
  123. detect_init() {
  124. if pidof -q systemd 2>/dev/null && [ -d /run/systemd/system ]; then
  125. echo "systemd"
  126. return
  127. fi
  128. if command -v openrc-init &> /dev/null; then
  129. echo "openrc"
  130. return
  131. fi
  132. if command -v runsvdir &> /dev/null; then
  133. echo "runit"
  134. return
  135. fi
  136. if command -v dinit &> /dev/null; then
  137. echo "dinit"
  138. return
  139. fi
  140. if command -v shepherd &> /dev/null; then
  141. echo "shepherd"
  142. return
  143. fi
  144. if [ -f /sbin/init ] && file /sbin/init 2>/dev/null | grep -q "sysvinit"; then
  145. echo "sysvinit"
  146. return
  147. fi
  148. echo "unknown"
  149. }
  150.  
  151. # FIX: the original efistub test was `[ -f /boot/vmlinuz-* ]`, which does
  152. # NOT expand the wildcard inside single-bracket test — it just tests the
  153. # literal string "/boot/vmlinuz-*", which never exists, so this branch
  154. # could never actually trigger. Replaced with `compgen -G`, which is the
  155. # correct way to test "does at least one file match this glob".
  156. detect_bootloader() {
  157. if command -v grub-mkconfig &> /dev/null; then
  158. echo "grub2"
  159. return
  160. elif [ -f /boot/grub/menu.lst ] || [ -f /boot/grub/grub.conf ]; then
  161. echo "grub-legacy"
  162. return
  163. elif command -v lilo &> /dev/null && [ -f /etc/lilo.conf ]; then
  164. echo "lilo"
  165. return
  166. elif command -v elilo &> /dev/null && [ -f /etc/elilo.conf ]; then
  167. echo "elilo"
  168. return
  169. elif command -v extlinux &> /dev/null || [ -f /boot/extlinux/extlinux.conf ]; then
  170. echo "syslinux"
  171. return
  172. elif [ -d /boot/efi/EFI/systemd ] || [ -d /boot/EFI/systemd ]; then
  173. echo "systemd-boot"
  174. return
  175. elif [ -d /sys/firmware/efi ] && compgen -G "/boot/vmlinuz-*" > /dev/null; then
  176. echo "efistub"
  177. return
  178. else
  179. echo "unknown"
  180. return
  181. fi
  182. }
  183.  
  184. detect_interface() {
  185. if [ -n "$DISPLAY" ] && [ -x "$(command -v zenity)" ]; then
  186. echo "gui"
  187. elif [ -n "$DISPLAY" ] && [ -x "$(command -v kdialog)" ]; then
  188. echo "gui"
  189. elif [ -x "$(command -v dialog)" ]; then
  190. echo "dialog"
  191. else
  192. echo "text"
  193. fi
  194. }
  195.  
  196. show_msg() {
  197. local title="$1"
  198. local message="$2"
  199. local type="${3:-info}"
  200. local interface="${INTERFACE:-auto}"
  201.  
  202. if [ "$interface" = "auto" ]; then
  203. interface=$(detect_interface)
  204. fi
  205.  
  206. case "$interface" in
  207. gui)
  208. if [ -x "$(command -v zenity)" ]; then
  209. zenity --${type} --title="${title}" --text="${message}" --width=400 --timeout=30 2>/dev/null || true
  210. elif [ -x "$(command -v kdialog)" ]; then
  211. kdialog --${type} "${message}" --title "${title}" 2>/dev/null || true
  212. else
  213. echo "=== ${title} ==="
  214. echo "${message}"
  215. fi
  216. ;;
  217. dialog)
  218. dialog --title "${title}" --msgbox "${message}" 10 60 2>/dev/null || true
  219. ;;
  220. *)
  221. echo "=== ${title} ==="
  222. echo "${message}"
  223. ;;
  224. esac
  225. }
  226.  
  227. show_question() {
  228. local title="$1"
  229. local message="$2"
  230. local interface="${INTERFACE:-auto}"
  231.  
  232. if [ "$interface" = "auto" ]; then
  233. interface=$(detect_interface)
  234. fi
  235.  
  236. case "$interface" in
  237. gui)
  238. if [ -x "$(command -v zenity)" ]; then
  239. zenity --question --title="${title}" --text="${message}" --width=400 2>/dev/null && return 0 || return 1
  240. elif [ -x "$(command -v kdialog)" ]; then
  241. kdialog --yesno "${message}" --title "${title}" 2>/dev/null && return 0 || return 1
  242. else
  243. echo "${message} (y/N): "
  244. read -r answer
  245. [[ "$answer" =~ ^[Yy]$ ]] && return 0 || return 1
  246. fi
  247. ;;
  248. dialog)
  249. dialog --title "${title}" --yesno "${message}" 10 60 2>/dev/null && return 0 || return 1
  250. ;;
  251. *)
  252. echo "${message} (y/N): "
  253. read -r answer
  254. [[ "$answer" =~ ^[Yy]$ ]] && return 0 || return 1
  255. ;;
  256. esac
  257. }
  258.  
  259. show_menu() {
  260. local title="$1"
  261. local prompt="$2"
  262. shift 2
  263. local options=("$@")
  264. local interface="${INTERFACE:-auto}"
  265. local choice=""
  266. local i=0
  267.  
  268. if [ "$interface" = "auto" ]; then
  269. interface=$(detect_interface)
  270. fi
  271.  
  272. case "$interface" in
  273. gui)
  274. if [ -x "$(command -v zenity)" ]; then
  275. local menu_items=()
  276. i=0
  277. while [ $i -lt ${#options[@]} ]; do
  278. menu_items+=("${options[$i]}" "${options[$i+1]}")
  279. i=$((i+2))
  280. done
  281. choice=$(zenity --list --title="${title}" --text="${prompt}" \
  282. --column="Option" --column="Description" \
  283. "${menu_items[@]}" --width=600 --height=400 2>/dev/null || echo "cancel")
  284. echo "$choice"
  285. return
  286. elif [ -x "$(command -v kdialog)" ]; then
  287. local menu_string=""
  288. i=0
  289. while [ $i -lt ${#options[@]} ]; do
  290. menu_string="${menu_string}${options[$i]} \"${options[$i+1]}\" "
  291. i=$((i+2))
  292. done
  293. choice=$(kdialog --menu "${prompt}" ${menu_string} --title "${title}" 2>/dev/null || echo "cancel")
  294. echo "$choice"
  295. return
  296. else
  297. echo "cancel"
  298. return
  299. fi
  300. ;;
  301. dialog)
  302. local menu_items=()
  303. i=0
  304. while [ $i -lt ${#options[@]} ]; do
  305. menu_items+=("${options[$i]}" "${options[$i+1]}")
  306. i=$((i+2))
  307. done
  308. choice=$(dialog --title "${title}" --menu "${prompt}" 20 60 10 "${menu_items[@]}" 2>&1 >/dev/tty || echo "cancel")
  309. echo "$choice"
  310. return
  311. ;;
  312. *)
  313. echo "=== ${title} ==="
  314. echo "${prompt}"
  315. i=0
  316. while [ $i -lt ${#options[@]} ]; do
  317. echo " ${options[$i]}) ${options[$i+1]}"
  318. i=$((i+2))
  319. done
  320. echo -n "Select option: "
  321. read -r choice
  322. echo "${choice}"
  323. return
  324. ;;
  325. esac
  326. }
  327.  
  328. detect_hardware() {
  329. log "Detecting hardware..."
  330. local hw_profile="${WORK_DIR}/hw_profile.txt"
  331. mkdir -p "$(dirname "$hw_profile")"
  332.  
  333. {
  334. echo "# Hardware Profile for Xanmod Kernel"
  335. echo "Generated: $(date)"
  336. echo ""
  337. echo "=== CPU ==="
  338. lscpu 2>/dev/null || cat /proc/cpuinfo 2>/dev/null | head -20
  339. echo ""
  340. echo "=== PCI Devices ==="
  341. lspci -k 2>/dev/null || echo "lspci not available"
  342. echo ""
  343. echo "=== USB Devices ==="
  344. lsusb 2>/dev/null || echo "lsusb not available"
  345. echo ""
  346. echo "=== Block Devices ==="
  347. lsblk 2>/dev/null || echo "lsblk not available"
  348. echo ""
  349. echo "=== Network ==="
  350. ip link show 2>/dev/null || ifconfig -a 2>/dev/null
  351. echo ""
  352. echo "=== DMI Info ==="
  353. dmidecode -t system 2>/dev/null || echo "dmidecode not available (needs root)"
  354. echo ""
  355. echo "=== Kernel Modules ==="
  356. lsmod 2>/dev/null | head -20
  357. } > "$hw_profile"
  358.  
  359. log "Hardware profile saved to $hw_profile"
  360. return 0
  361. }
  362.  
  363. generate_optimized_config() {
  364. log "Generating optimized kernel config..."
  365.  
  366. if ! command -v make &> /dev/null; then
  367. log "ERROR: make is required but not installed"
  368. return 1
  369. fi
  370.  
  371. if [ -f "${WORK_DIR}/.config" ]; then
  372. rm "${WORK_DIR}/.config"
  373. fi
  374.  
  375. make -C "${WORK_DIR}" defconfig 2>&1 | while IFS= read -r line; do log "$line"; done
  376. if [ ! -f "${WORK_DIR}/.config" ]; then
  377. log "ERROR: Failed to generate default config"
  378. return 1
  379. fi
  380.  
  381. if [ "${DETECT_HARDWARE}" = "true" ]; then
  382. log "Applying localmodconfig to strip unnecessary drivers..."
  383. make -C "${WORK_DIR}" localmodconfig 2>&1 | while IFS= read -r line; do log "$line"; done \
  384. || log "Warning: localmodconfig failed"
  385. fi
  386.  
  387. local march_flag=""
  388. case "${MARCH}" in
  389. native) march_flag="-march=native" ;;
  390. v3) march_flag="-march=x86-64-v3" ;;
  391. v4) march_flag="-march=x86-64-v4" ;;
  392. generic) march_flag="-march=x86-64" ;;
  393. *) march_flag="-march=native" ;;
  394. esac
  395. log "Using CPU optimization flag: ${march_flag}"
  396.  
  397. "${WORK_DIR}/scripts/config" --file "${WORK_DIR}/.config" --set-val CONFIG_CC_OPTIMIZE_FOR_PERFORMANCE y 2>/dev/null || true
  398.  
  399. if [ -n "${CUSTOM_CONFIG}" ] && [ -f "${CUSTOM_CONFIG}" ]; then
  400. log "Applying custom configuration from ${CUSTOM_CONFIG}"
  401. cp "${CUSTOM_CONFIG}" "${WORK_DIR}/.config" || { log "ERROR: Failed to apply custom config"; return 1; }
  402. fi
  403.  
  404. log "Hardware detection and optimization completed"
  405. return 0
  406. }
  407.  
  408. # FIX: the original tag regex `refs/tags/v[0-9]+\.[0-9]+\.[0-9]+$` requires
  409. # the tag to end exactly at X.Y.Z. Real XanMod release tags are typically
  410. # named like v6.9.1-xanmod1 (a build-number suffix after the kernel
  411. # version), so the old pattern would likely never match a real tag at all.
  412. # This version accepts an optional -xanmodN suffix.
  413. check_updates() {
  414. log "Checking for XanMod updates..."
  415.  
  416. if ! command -v git &> /dev/null; then
  417. log "ERROR: git is not installed"
  418. show_msg "Error" "git is required but not installed. Please install git first." "error"
  419. return 1
  420. fi
  421.  
  422. mkdir -p "${WORK_DIR}"
  423.  
  424. local remote_version=""
  425. local local_version=""
  426.  
  427. remote_version=$(git ls-remote --tags "${XANMOD_REPO}" 2>/dev/null \
  428. | grep -E "refs/tags/v[0-9]+\.[0-9]+(\.[0-9]+)?(-xanmod[0-9]+)?$" \
  429. | sed 's|.*refs/tags/||' | sort -V | tail -n1)
  430.  
  431. if [ -d "${WORK_DIR}/.git" ]; then
  432. local_version=$(git -C "${WORK_DIR}" describe --tags --abbrev=0 2>/dev/null)
  433. fi
  434.  
  435. if [ -z "$remote_version" ]; then
  436. log "ERROR: Could not determine latest version (check network / tag format)"
  437. return 1
  438. fi
  439.  
  440. if [ -z "$local_version" ] || [ "$remote_version" != "$local_version" ]; then
  441. log "New version available: $remote_version (current: ${local_version:-none})"
  442. echo "$remote_version"
  443. return 0
  444. else
  445. log "Already up to date: $local_version"
  446. echo "$local_version"
  447. return 1
  448. fi
  449. }
  450.  
  451. send_notification() {
  452. local title="$1"
  453. local message="$2"
  454. local urgency="${3:-normal}"
  455.  
  456. if [ "${SILENT_NOTIFICATIONS}" = "true" ]; then
  457. log "Notifications silenced. Would have sent: $title"
  458. return 0
  459. fi
  460.  
  461. local interface
  462. interface=$(detect_interface)
  463.  
  464. case "$interface" in
  465. gui)
  466. if [ -x "$(command -v notify-send)" ]; then
  467. notify-send -u "${urgency}" "${title}" "${message}" 2>/dev/null || true
  468. elif [ -x "$(command -v zenity)" ]; then
  469. zenity --notification --text="${title}: ${message}" 2>/dev/null || true
  470. fi
  471. ;;
  472. *)
  473. echo "${title}: ${message}"
  474. ;;
  475. esac
  476.  
  477. log "Notification sent: $title - $message"
  478. }
  479.  
  480. # FIX: this is the function that most needed the log()/stdout separation.
  481. # It is routinely called as `local new_version=$(build_kernel)`, so ANY
  482. # plain command output that used to land on stdout (git clone/fetch text,
  483. # make's build output via the old `2>&1`) would get captured right along
  484. # with the intended return value, corrupting $new_version. Every
  485. # diagnostic/build command below is now redirected into the log file
  486. # instead of stdout. The ONLY thing this function prints to stdout is the
  487. # final `echo "$latest_version"` on success.
  488. build_kernel() {
  489. log "Building XanMod kernel..."
  490.  
  491. if ! command -v make &> /dev/null || ! command -v gcc &> /dev/null; then
  492. log "ERROR: Build tools (make, gcc) are not installed. Please install them first."
  493. return 1
  494. fi
  495.  
  496. if [ "$(id -u)" -eq 0 ]; then
  497. log "Warning: running the build as root is not recommended (source tree will be root-owned). Consider building as a regular user and only using sudo for the install step."
  498. fi
  499.  
  500. if [ ! -d "${WORK_DIR}/.git" ]; then
  501. log "Cloning XanMod repository (this can take a while)..."
  502. git clone "${XANMOD_REPO}" "${WORK_DIR}" >> "${LOG_FILE}" 2>&1 \
  503. || { log "ERROR: Failed to clone repository"; return 1; }
  504. else
  505. log "Updating XanMod repository..."
  506. git -C "${WORK_DIR}" fetch --tags >> "${LOG_FILE}" 2>&1 \
  507. || { log "ERROR: Failed to fetch updates"; return 1; }
  508. fi
  509.  
  510. local latest_version
  511. latest_version=$(git -C "${WORK_DIR}" describe --tags --abbrev=0 2>/dev/null)
  512. if [ -z "$latest_version" ]; then
  513. log "ERROR: Failed to determine latest version from repository"
  514. return 1
  515. fi
  516. log "Checking out version: $latest_version"
  517. git -C "${WORK_DIR}" checkout "${latest_version}" >> "${LOG_FILE}" 2>&1 \
  518. || { log "ERROR: Failed to checkout latest version"; return 1; }
  519.  
  520. if [ "${DETECT_HARDWARE}" = "true" ]; then
  521. detect_hardware
  522. fi
  523.  
  524. generate_optimized_config || { log "ERROR: Config generation failed"; return 1; }
  525.  
  526. local cpu_count
  527. cpu_count=$(nproc 2>/dev/null || echo 1)
  528. log "Compiling kernel with $cpu_count parallel jobs (this will take a long time)..."
  529.  
  530. make -C "${WORK_DIR}" -j"${cpu_count}" all >> "${LOG_FILE}" 2>&1 \
  531. || { log "ERROR: Kernel compilation failed. See ${LOG_FILE} for details."; return 1; }
  532.  
  533. local distro
  534. distro=$(detect_distro)
  535. log "Packaging for distribution: $distro"
  536.  
  537. case "$distro" in
  538. debian)
  539. make -C "${WORK_DIR}" deb-pkg -j"${cpu_count}" >> "${LOG_FILE}" 2>&1 \
  540. || { log "ERROR: Debian package creation failed"; return 1; }
  541. log "Debian packages created successfully"
  542. ;;
  543. slackware)
  544. if [ -f "${WORK_DIR}/build-all-kernels.sh" ]; then
  545. "${WORK_DIR}/build-all-kernels.sh" >> "${LOG_FILE}" 2>&1 \
  546. || { log "ERROR: Slackware package creation failed"; return 1; }
  547. log "Slackware packages created successfully"
  548. else
  549. local package_name="kernel-xanmod-${latest_version#v}"
  550. mkdir -p "${WORK_DIR}/pkg" "${WORK_DIR}/pkg/boot" "${WORK_DIR}/pkg/lib/modules"
  551. make -C "${WORK_DIR}" modules_install INSTALL_MOD_PATH="${WORK_DIR}/pkg" >> "${LOG_FILE}" 2>&1 \
  552. || { log "ERROR: Module installation failed"; return 1; }
  553. cp "${WORK_DIR}/arch/x86/boot/bzImage" "${WORK_DIR}/pkg/boot/vmlinuz-${latest_version#v}" \
  554. || { log "ERROR: Failed to copy kernel image"; return 1; }
  555. cp "${WORK_DIR}/System.map" "${WORK_DIR}/pkg/boot/System.map-${latest_version#v}" \
  556. || { log "ERROR: Failed to copy System.map"; return 1; }
  557. cp "${WORK_DIR}/.config" "${WORK_DIR}/pkg/boot/config-${latest_version#v}" \
  558. || { log "ERROR: Failed to copy kernel config"; return 1; }
  559. ( cd "${WORK_DIR}/pkg" && tar czf "../${package_name}.txz" ./* ) >> "${LOG_FILE}" 2>&1 \
  560. || { log "ERROR: Package creation failed"; return 1; }
  561. log "Manual package created: ${package_name}.txz"
  562. PACKAGE_FILE="${WORK_DIR}/${package_name}.txz"
  563. fi
  564. ;;
  565. *)
  566. log "ERROR: Unsupported distribution for packaging: $distro"
  567. return 1
  568. ;;
  569. esac
  570.  
  571. log "Kernel build and packaging completed successfully"
  572. echo "$latest_version"
  573. return 0
  574. }
  575.  
  576. # FIX: added require_root — the original just ran dpkg -i / installpkg
  577. # directly, which fails with a permission error mid-run if not root,
  578. # rather than telling you upfront.
  579. install_kernel() {
  580. log "Installing kernel..."
  581. require_root || return 1
  582.  
  583. local distro
  584. distro=$(detect_distro)
  585. local kernel_version=""
  586.  
  587. case "$distro" in
  588. debian)
  589. local latest_deb
  590. latest_deb=$(ls -t "${WORK_DIR}"/*.deb 2>/dev/null | head -1)
  591. if [ -n "$latest_deb" ] && [ -f "$latest_deb" ]; then
  592. kernel_version=$(basename "$latest_deb" | sed 's/linux-image-//' | sed 's/_.*$//' | sed 's/.*-//')
  593. log "Installing: $(basename "$latest_deb")"
  594. dpkg -i "$latest_deb" >> "${LOG_FILE}" 2>&1 \
  595. || { log "ERROR: Failed to install Debian package"; return 1; }
  596. else
  597. log "ERROR: No Debian packages found to install"
  598. return 1
  599. fi
  600. ;;
  601. slackware)
  602. local latest_txz
  603. latest_txz=$(ls -t "${WORK_DIR}"/*.txz 2>/dev/null | head -1)
  604. if [ -n "$latest_txz" ] && [ -f "$latest_txz" ]; then
  605. kernel_version=$(basename "$latest_txz" | sed 's/kernel-xanmod-//' | sed 's/\.txz$//')
  606. log "Installing: $(basename "$latest_txz")"
  607. installpkg "$latest_txz" >> "${LOG_FILE}" 2>&1 \
  608. || { log "ERROR: Failed to install Slackware package"; return 1; }
  609. local modules_txz="${WORK_DIR}/kernel-modules-${kernel_version}.txz"
  610. if [ -f "$modules_txz" ]; then
  611. installpkg "$modules_txz" >> "${LOG_FILE}" 2>&1 || log "Warning: Failed to install modules package"
  612. fi
  613. else
  614. log "ERROR: No Slackware packages found to install"
  615. return 1
  616. fi
  617. ;;
  618. *)
  619. log "ERROR: Unsupported distribution for installation: $distro"
  620. return 1
  621. ;;
  622. esac
  623.  
  624. if [ -z "$kernel_version" ]; then
  625. case "$distro" in
  626. debian)
  627. kernel_version=$(dpkg -l 2>/dev/null | grep -E 'linux-image-[0-9]+\.[0-9]+\.[0-9]+' | awk '{print $2}' | sort -V | tail -n1 | sed 's/linux-image-//')
  628. ;;
  629. slackware)
  630. kernel_version=$(ls -1 /boot/vmlinuz-* 2>/dev/null | sort -V | tail -n1 | sed 's|/boot/vmlinuz-||')
  631. ;;
  632. esac
  633. fi
  634.  
  635. if [ -n "$kernel_version" ]; then
  636. log "Installed kernel version: $kernel_version"
  637. update_bootloader "$kernel_version"
  638. update_initramfs "$kernel_version"
  639. else
  640. log "Warning: Could not determine installed kernel version"
  641. fi
  642.  
  643. log "Kernel installation completed successfully"
  644. return 0
  645. }
  646.  
  647. update_initramfs() {
  648. local kernel_version="$1"
  649. if [ -z "$kernel_version" ]; then
  650. log "No kernel version provided for initramfs update"
  651. return 1
  652. fi
  653. require_root || return 1
  654.  
  655. log "Updating initramfs for kernel: $kernel_version"
  656. local distro
  657. distro=$(detect_distro)
  658.  
  659. case "$distro" in
  660. debian)
  661. if command -v update-initramfs &> /dev/null; then
  662. update-initramfs -u -k "$kernel_version" >> "${LOG_FILE}" 2>&1 || log "Warning: update-initramfs failed"
  663. elif command -v mkinitramfs &> /dev/null; then
  664. mkinitramfs -o "/boot/initrd.img-$kernel_version" "$kernel_version" >> "${LOG_FILE}" 2>&1 || log "Warning: mkinitramfs failed"
  665. else
  666. log "Warning: No initramfs tool found"
  667. fi
  668. ;;
  669. slackware)
  670. if command -v mkinitrd &> /dev/null; then
  671. mkinitrd -c -k "$kernel_version" >> "${LOG_FILE}" 2>&1 || log "Warning: mkinitrd failed"
  672. else
  673. log "Warning: mkinitrd not found"
  674. fi
  675. ;;
  676. *)
  677. log "Warning: Unknown distribution for initramfs update"
  678. ;;
  679. esac
  680.  
  681. log "Initramfs update completed"
  682. return 0
  683. }
  684.  
  685. # NOTE: the original script also defined an update_grub() function that
  686. # was never called anywhere, and its logic was backwards for automation
  687. # (it SKIPPED updating GRUB unless a graphical $DISPLAY was present, which
  688. # would silently break unattended `--auto-update` cron runs). It's been
  689. # removed here since update_bootloader's own grub2 case below already
  690. # does the real work, independent of whether a display is attached.
  691. update_bootloader() {
  692. local kernel_version="$1"
  693. require_root || return 1
  694.  
  695. log "Updating bootloader..."
  696. local bootloader
  697. bootloader=$(detect_bootloader)
  698. local distro
  699. distro=$(detect_distro)
  700.  
  701. if [ -z "$kernel_version" ]; then
  702. case "$distro" in
  703. debian)
  704. kernel_version=$(dpkg -l 2>/dev/null | grep -E 'linux-image-[0-9]+\.[0-9]+\.[0-9]+' | awk '{print $2}' | sort -V | tail -n1 | sed 's/linux-image-//')
  705. ;;
  706. slackware)
  707. kernel_version=$(ls -1 /boot/vmlinuz-* 2>/dev/null | sort -V | tail -n1 | sed 's|/boot/vmlinuz-||')
  708. ;;
  709. *)
  710. kernel_version=$(uname -r)
  711. ;;
  712. esac
  713. fi
  714.  
  715. if [ -z "$kernel_version" ]; then
  716. log "ERROR: Could not determine kernel version for bootloader update"
  717. return 1
  718. fi
  719.  
  720. log "Detected bootloader: $bootloader"
  721. log "Kernel version: $kernel_version"
  722.  
  723. case "$bootloader" in
  724. grub2)
  725. log "Updating GRUB2 configuration..."
  726. if [ -f /etc/default/grub ] && grep -q "^GRUB_DISABLE_OS_PROBER=true" /etc/default/grub; then
  727. log "Note: OS_PROBER is disabled. Other OSes may not be detected."
  728. fi
  729. if command -v grub-mkconfig &> /dev/null; then
  730. grub-mkconfig -o /boot/grub/grub.cfg >> "${LOG_FILE}" 2>&1 \
  731. || { log "ERROR: Failed to update GRUB configuration"; return 1; }
  732. log "GRUB2 configuration updated"
  733. elif command -v update-grub &> /dev/null; then
  734. update-grub >> "${LOG_FILE}" 2>&1 \
  735. || { log "ERROR: Failed to update GRUB configuration"; return 1; }
  736. log "GRUB2 configuration updated (via update-grub)"
  737. else
  738. log "ERROR: No GRUB2 update command found"
  739. return 1
  740. fi
  741. ;;
  742. grub-legacy)
  743. log "Updating GRUB Legacy configuration..."
  744. if [ -f /boot/grub/menu.lst ]; then
  745. if ! grep -q "vmlinuz-${kernel_version}" /boot/grub/menu.lst; then
  746. local root_partition
  747. root_partition=$(grep "^kernel" /boot/grub/menu.lst | head -1 | sed 's/.*root=\([^ ]*\).*/\1/')
  748. [ -z "$root_partition" ] && root_partition="/dev/sda1"
  749. cat >> /boot/grub/menu.lst << EOF
  750.  
  751. title Linux ${kernel_version}
  752. root (hd0,0)
  753. kernel /boot/vmlinuz-${kernel_version} root=${root_partition} ro
  754. initrd /boot/initrd-${kernel_version}
  755. EOF
  756. log "Added new kernel entry to GRUB Legacy menu.lst"
  757. else
  758. log "Kernel already present in GRUB Legacy menu.lst"
  759. fi
  760. else
  761. log "Warning: GRUB Legacy menu.lst not found"
  762. fi
  763. ;;
  764. lilo)
  765. log "Updating LILO configuration..."
  766. if [ -f /etc/lilo.conf ]; then
  767. if ! grep -q "vmlinuz-${kernel_version}" /etc/lilo.conf; then
  768. local root_partition
  769. root_partition=$(grep "^root=" /etc/lilo.conf | head -1 | sed 's/root=//' | tr -d ' ')
  770. [ -z "$root_partition" ] && root_partition="/dev/sda1"
  771. cp /etc/lilo.conf /etc/lilo.conf.bak
  772. cat >> /etc/lilo.conf << EOF
  773.  
  774. image = /boot/vmlinuz-${kernel_version}
  775. root = ${root_partition}
  776. label = Linux-${kernel_version}
  777. read-only
  778. EOF
  779. log "Added new kernel entry to LILO configuration"
  780. else
  781. log "Kernel already present in LILO configuration"
  782. fi
  783. if command -v lilo &> /dev/null; then
  784. lilo >> "${LOG_FILE}" 2>&1 || { log "ERROR: Failed to run lilo"; return 1; }
  785. log "LILO updated successfully"
  786. else
  787. log "ERROR: lilo command not found"
  788. return 1
  789. fi
  790. else
  791. log "ERROR: LILO configuration file /etc/lilo.conf not found"
  792. return 1
  793. fi
  794. ;;
  795. elilo)
  796. log "Updating ELILO configuration..."
  797. if [ -f /etc/elilo.conf ]; then
  798. if ! grep -q "vmlinuz-${kernel_version}" /etc/elilo.conf; then
  799. local root_partition
  800. root_partition=$(grep "^root=" /etc/elilo.conf | head -1 | sed 's/root=//' | tr -d ' ')
  801. [ -z "$root_partition" ] && root_partition="/dev/sda1"
  802. {
  803. echo ""
  804. echo "image = /boot/vmlinuz-${kernel_version}"
  805. echo "root = ${root_partition}"
  806. echo "label = Linux-${kernel_version}"
  807. echo "read-only"
  808. } >> /etc/elilo.conf
  809. log "Added new kernel entry to ELILO configuration"
  810. fi
  811. if command -v elilo &> /dev/null; then
  812. elilo >> "${LOG_FILE}" 2>&1 || { log "ERROR: Failed to run elilo"; return 1; }
  813. log "ELILO updated successfully"
  814. else
  815. log "ERROR: elilo command not found"
  816. return 1
  817. fi
  818. else
  819. log "ERROR: ELILO configuration file /etc/elilo.conf not found"
  820. return 1
  821. fi
  822. ;;
  823. syslinux)
  824. log "Updating SYSLINUX configuration..."
  825. local syslinux_conf=""
  826. if [ -f /boot/extlinux/extlinux.conf ]; then
  827. syslinux_conf="/boot/extlinux/extlinux.conf"
  828. elif [ -f /boot/syslinux/syslinux.cfg ]; then
  829. syslinux_conf="/boot/syslinux/syslinux.cfg"
  830. fi
  831. if [ -n "$syslinux_conf" ]; then
  832. if ! grep -q "vmlinuz-${kernel_version}" "$syslinux_conf"; then
  833. {
  834. echo ""
  835. echo "label Linux-${kernel_version}"
  836. echo "kernel /boot/vmlinuz-${kernel_version}"
  837. echo "append initrd=/boot/initrd-${kernel_version} root=/dev/sda1 ro"
  838. } >> "$syslinux_conf"
  839. log "Added new kernel entry to $syslinux_conf"
  840. fi
  841. if command -v extlinux &> /dev/null && [ -f /boot/extlinux/extlinux.conf ]; then
  842. extlinux --update /boot/extlinux >> "${LOG_FILE}" 2>&1 || log "Warning: extlinux update failed"
  843. fi
  844. else
  845. log "Warning: SYSLINUX configuration not found"
  846. fi
  847. ;;
  848. systemd-boot)
  849. log "Updating systemd-boot configuration..."
  850. local boot_dir=""
  851. if [ -d /boot/efi/EFI/systemd ]; then
  852. boot_dir="/boot/efi/EFI/systemd"
  853. elif [ -d /boot/EFI/systemd ]; then
  854. boot_dir="/boot/EFI/systemd"
  855. fi
  856. if [ -n "$boot_dir" ]; then
  857. mkdir -p "${boot_dir}/entries"
  858. local entry_file="${boot_dir}/entries/linux-${kernel_version}.conf"
  859. cat > "${entry_file}" << EOF
  860. title Linux ${kernel_version}
  861. linux /vmlinuz-${kernel_version}
  862. initrd /initrd-${kernel_version}
  863. options root=/dev/sda1 ro quiet
  864. EOF
  865. log "Created systemd-boot entry: ${entry_file}"
  866. if [ -f "${boot_dir}/loader/loader.conf" ]; then
  867. sed -i "s/default .*/default linux-${kernel_version}/" "${boot_dir}/loader/loader.conf" 2>/dev/null || true
  868. fi
  869. log "systemd-boot updated successfully"
  870. else
  871. log "Warning: systemd-boot directory not found"
  872. fi
  873. ;;
  874. efistub)
  875. log "EFISTUB detected. No bootloader to update."
  876. log "You may need to update your UEFI boot entries manually using efibootmgr."
  877. if command -v efibootmgr &> /dev/null; then
  878. log "Current EFI boot entries logged to ${LOG_FILE}"
  879. efibootmgr >> "${LOG_FILE}" 2>&1 || true
  880. else
  881. log "efibootmgr not found. Install it to manage EFI entries."
  882. fi
  883. ;;
  884. unknown)
  885. log "Warning: Unknown bootloader. Please update your boot configuration manually."
  886. ;;
  887. *)
  888. log "ERROR: Unsupported bootloader: $bootloader"
  889. return 1
  890. ;;
  891. esac
  892.  
  893. log "Bootloader update completed"
  894. return 0
  895. }
  896.  
  897. cleanup_kernels() {
  898. log "Cleaning up old kernels..."
  899. local keep_count=${KEEP_OLD:-3}
  900. local distro
  901. distro=$(detect_distro)
  902.  
  903. local installed_kernels=()
  904. local current_kernel
  905. current_kernel=$(uname -r)
  906.  
  907. case "$distro" in
  908. debian)
  909. mapfile -t installed_kernels < <(dpkg -l 2>/dev/null | grep -E 'linux-image-[0-9]+\.[0-9]+\.[0-9]+' | awk '{print $2}' | sort -V)
  910. ;;
  911. slackware)
  912. mapfile -t installed_kernels < <(ls -1 /boot/vmlinuz-* 2>/dev/null | sed 's|/boot/vmlinuz-||' | sort -V)
  913. ;;
  914. *)
  915. log "Warning: Unknown distribution for cleanup"
  916. return 1
  917. ;;
  918. esac
  919.  
  920. if [ ${#installed_kernels[@]} -eq 0 ]; then
  921. log "No kernels found to clean up"
  922. return 0
  923. fi
  924.  
  925. local keep_list=()
  926. local remove_list=()
  927. local count=0
  928.  
  929. for kernel in "${installed_kernels[@]}"; do
  930. if [ "$kernel" = "$current_kernel" ]; then
  931. keep_list+=("$kernel")
  932. continue
  933. fi
  934. if [ $count -lt "$keep_count" ]; then
  935. keep_list+=("$kernel")
  936. ((count++))
  937. else
  938. remove_list+=("$kernel")
  939. fi
  940. done
  941.  
  942. if [ ${#remove_list[@]} -eq 0 ]; then
  943. log "No old kernels to remove"
  944. return 0
  945. fi
  946.  
  947. local message="The following kernels will be removed:\n\n"
  948. for kernel in "${remove_list[@]}"; do
  949. message+=" - $kernel\n"
  950. done
  951. message+="\nKeep count: $keep_count (current + $((keep_count-1)) most recent)\n\nProceed with removal?"
  952.  
  953. if ! show_question "Cleanup Confirmation" "$message"; then
  954. log "Cleanup cancelled by user"
  955. return 0
  956. fi
  957.  
  958. require_root || return 1
  959.  
  960. for kernel in "${remove_list[@]}"; do
  961. log "Removing kernel: $kernel"
  962. case "$distro" in
  963. debian)
  964. if echo "$kernel" | grep -q "^linux-image-\|^[0-9]"; then
  965. dpkg --purge "linux-image-${kernel#linux-image-}" >> "${LOG_FILE}" 2>&1 || log "Warning: Failed to remove $kernel"
  966. fi
  967. ;;
  968. slackware)
  969. removepkg "kernel-${kernel}" >> "${LOG_FILE}" 2>&1 || log "Warning: Failed to remove kernel-$kernel"
  970. rm -rf "/lib/modules/${kernel}" 2>/dev/null
  971. rm -f "/boot/vmlinuz-${kernel}" "/boot/System.map-${kernel}" "/boot/config-${kernel}" 2>/dev/null
  972. ;;
  973. esac
  974. done
  975.  
  976. log "Removed ${#remove_list[@]} old kernels"
  977. return 0
  978. }
  979.  
  980. setup_cron() {
  981. local freq="${1:-$UPDATE_FREQ}"
  982. local script_path
  983. script_path=$(realpath "$0" 2>/dev/null || echo "$0")
  984. local cron_cmd="${script_path} --auto-update"
  985.  
  986. ( crontab -l 2>/dev/null | grep -vF "$cron_cmd" ) | crontab - 2>/dev/null || true
  987.  
  988. if [ "$freq" = "never" ]; then
  989. log "Cron job disabled"
  990. return 0
  991. fi
  992.  
  993. local cron_schedule=""
  994. case "$freq" in
  995. daily) cron_schedule="0 3 * * *" ;;
  996. weekly) cron_schedule="0 3 * * 0" ;;
  997. monthly) cron_schedule="0 3 1 * *" ;;
  998. *) log "Unknown frequency: $freq"; return 1 ;;
  999. esac
  1000.  
  1001. ( crontab -l 2>/dev/null; echo "$cron_schedule $cron_cmd" ) | crontab - 2>/dev/null || {
  1002. log "ERROR: Failed to install cron job"
  1003. return 1
  1004. }
  1005.  
  1006. log "Cron job installed: $freq updates at $cron_schedule"
  1007. return 0
  1008. }
  1009.  
  1010. uninstall_kernel_menu() {
  1011. log "Kernel uninstallation menu"
  1012. local distro
  1013. distro=$(detect_distro)
  1014. local installed_kernels=()
  1015. local current_kernel
  1016. current_kernel=$(uname -r)
  1017.  
  1018. case "$distro" in
  1019. debian)
  1020. mapfile -t installed_kernels < <(dpkg -l 2>/dev/null | grep -E 'linux-image-[0-9]+\.[0-9]+\.[0-9]+' | awk '{print $2}' | sort -V)
  1021. ;;
  1022. slackware)
  1023. mapfile -t installed_kernels < <(ls -1 /boot/vmlinuz-* 2>/dev/null | sed 's|/boot/vmlinuz-||' | sort -V)
  1024. ;;
  1025. *)
  1026. log "Warning: Unknown distribution"
  1027. return 1
  1028. ;;
  1029. esac
  1030.  
  1031. if [ ${#installed_kernels[@]} -eq 0 ]; then
  1032. show_msg "No Kernels" "No kernels found to uninstall."
  1033. return 0
  1034. fi
  1035.  
  1036. local menu_options=()
  1037. for kernel in "${installed_kernels[@]}"; do
  1038. local label="$kernel"
  1039. if [ "$kernel" = "$current_kernel" ]; then
  1040. label="$kernel (CURRENT)"
  1041. fi
  1042. menu_options+=("$kernel" "$label")
  1043. done
  1044. menu_options+=("back" "Return to main menu")
  1045.  
  1046. local choice
  1047. choice=$(show_menu "Uninstall Kernel" "Select kernel to uninstall:" "${menu_options[@]}")
  1048.  
  1049. if [ "$choice" = "back" ] || [ -z "$choice" ]; then
  1050. return 0
  1051. fi
  1052.  
  1053. if [ "$choice" = "$current_kernel" ]; then
  1054. show_msg "Cannot Remove" "You cannot remove the currently running kernel."
  1055. return 0
  1056. fi
  1057.  
  1058. if show_question "Confirm Removal" "Are you sure you want to remove kernel: $choice?"; then
  1059. require_root || { show_msg "Permission Denied" "Removing kernels requires root. Re-run with sudo."; return 1; }
  1060. log "Removing kernel: $choice"
  1061. case "$distro" in
  1062. debian)
  1063. dpkg --purge "linux-image-$choice" >> "${LOG_FILE}" 2>&1 || log "Warning: Failed to remove $choice"
  1064. ;;
  1065. slackware)
  1066. removepkg "kernel-$choice" >> "${LOG_FILE}" 2>&1 || log "Warning: Failed to remove $choice"
  1067. rm -rf "/lib/modules/$choice" 2>/dev/null
  1068. rm -f "/boot/vmlinuz-$choice" "/boot/System.map-$choice" "/boot/config-$choice" 2>/dev/null
  1069. ;;
  1070. esac
  1071.  
  1072. update_bootloader "$(uname -r)"
  1073. show_msg "Removal Complete" "Kernel $choice has been removed."
  1074. fi
  1075. }
  1076.  
  1077. # FIX: throughout this function, every instance of
  1078. # local new_version=$(some_function)
  1079. # if [ $? -eq 0 ]; then
  1080. # has been split into
  1081. # local new_version
  1082. # new_version=$(some_function)
  1083. # local rc=$?
  1084. # if [ "$rc" -eq 0 ]; then
  1085. # because `local var=$(cmd)` always makes $? reflect `local`'s own
  1086. # success (0), never the command's real exit status. This was silently
  1087. # masking every build/update failure in the original script.
  1088. main_menu() {
  1089. while true; do
  1090. local choice
  1091. choice=$(show_menu "Xanmod4Slack v$VERSION" "Choose an action:" \
  1092. "update" "Check for kernel updates" \
  1093. "build" "Build and install new kernel" \
  1094. "cleanup" "Remove old kernels" \
  1095. "config" "Configure settings" \
  1096. "cron" "Manage automatic updates" \
  1097. "uninstall" "Uninstall a specific kernel" \
  1098. "quit" "Exit")
  1099.  
  1100. case "$choice" in
  1101. "update"|"Update")
  1102. local new_version
  1103. new_version=$(check_updates)
  1104. local rc=$?
  1105. if [ "$rc" -eq 0 ]; then
  1106. local current_version
  1107. current_version=$(uname -r)
  1108. send_notification "Kernel Update Available" "XanMod $new_version is available (current: $current_version)" "normal"
  1109. if show_question "Update Available" "XanMod $new_version is available.\nCurrent: $current_version\n\nBuild and install now?"; then
  1110. build_kernel
  1111. if [ $? -eq 0 ]; then
  1112. install_kernel
  1113. if [ "${AUTO_CLEANUP}" = "true" ]; then
  1114. cleanup_kernels
  1115. fi
  1116. show_msg "Complete" "Kernel $new_version installed successfully."
  1117. else
  1118. show_msg "Build Failed" "Kernel build failed. Check ${LOG_FILE} for details."
  1119. fi
  1120. fi
  1121. else
  1122. show_msg "Up to Date" "Your XanMod kernel is current: $new_version"
  1123. fi
  1124. ;;
  1125. "build"|"Build")
  1126. if show_question "Confirm Build" "This will take a long time.\nProceed with kernel build?"; then
  1127. local new_version
  1128. new_version=$(build_kernel)
  1129. local rc=$?
  1130. if [ "$rc" -eq 0 ]; then
  1131. install_kernel
  1132. if [ "${AUTO_CLEANUP}" = "true" ]; then
  1133. cleanup_kernels
  1134. fi
  1135. show_msg "Complete" "Kernel $new_version built and installed successfully."
  1136. else
  1137. show_msg "Build Failed" "Kernel build failed. Check ${LOG_FILE} for details."
  1138. fi
  1139. fi
  1140. ;;
  1141. "cleanup"|"Cleanup")
  1142. cleanup_kernels
  1143. show_msg "Cleanup Complete" "Old kernels have been removed."
  1144. ;;
  1145. "config"|"Config")
  1146. local config_menu_choice
  1147. config_menu_choice=$(show_menu "Configuration" "Select setting to change:" \
  1148. "freq" "Update frequency (${UPDATE_FREQ:-$DEFAULT_UPDATE_FREQ})" \
  1149. "interface" "Interface type (${INTERFACE:-$DEFAULT_INTERFACE})" \
  1150. "keep" "Kernels to keep (${KEEP_OLD:-$DEFAULT_KEEP_OLD})" \
  1151. "auto" "Auto-update (${AUTO_UPDATE:-$DEFAULT_AUTO_UPDATE})" \
  1152. "clean" "Auto-cleanup (${AUTO_CLEANUP:-$DEFAULT_AUTO_CLEANUP})" \
  1153. "silent" "Silent notifications (${SILENT_NOTIFICATIONS:-$DEFAULT_SILENT_NOTIFICATIONS})" \
  1154. "hardware" "Hardware detection (${DETECT_HARDWARE:-$DEFAULT_DETECT_HARDWARE})" \
  1155. "march" "CPU optimization (${MARCH:-$DEFAULT_MARCH})" \
  1156. "back" "Return to main menu")
  1157.  
  1158. case "$config_menu_choice" in
  1159. "freq")
  1160. local new_freq
  1161. new_freq=$(show_menu "Update Frequency" "Choose frequency:" \
  1162. "daily" "Check daily" \
  1163. "weekly" "Check weekly" \
  1164. "monthly" "Check monthly" \
  1165. "never" "Never check automatically")
  1166. if [ -n "$new_freq" ]; then
  1167. UPDATE_FREQ="$new_freq"
  1168. save_config
  1169. setup_cron "$UPDATE_FREQ"
  1170. log "Update frequency set to $UPDATE_FREQ"
  1171. fi
  1172. ;;
  1173. "interface")
  1174. local new_interface
  1175. new_interface=$(show_menu "Interface Type" "Choose interface:" \
  1176. "auto" "Auto-detect" \
  1177. "gui" "GUI (zenity/kdialog)" \
  1178. "dialog" "Dialog (ncurses)" \
  1179. "text" "Text (CLI)")
  1180. if [ -n "$new_interface" ]; then
  1181. INTERFACE="$new_interface"
  1182. save_config
  1183. log "Interface set to $INTERFACE"
  1184. fi
  1185. ;;
  1186. "keep")
  1187. local new_keep
  1188. new_keep=$(show_menu "Kernels to Keep" "Choose number to keep:" \
  1189. "1" "Keep 1 (most recent)" \
  1190. "2" "Keep 2" \
  1191. "3" "Keep 3" \
  1192. "4" "Keep 4" \
  1193. "5" "Keep 5")
  1194. if [[ "$new_keep" =~ ^[0-9]+$ ]]; then
  1195. KEEP_OLD="$new_keep"
  1196. save_config
  1197. log "Keeping $KEEP_OLD kernels"
  1198. fi
  1199. ;;
  1200. "auto")
  1201. local new_auto
  1202. new_auto=$(show_menu "Auto-Update" "Enable automatic updates?" \
  1203. "true" "Yes" \
  1204. "false" "No")
  1205. if [ -n "$new_auto" ]; then
  1206. AUTO_UPDATE="$new_auto"
  1207. save_config
  1208. log "Auto-update set to $AUTO_UPDATE"
  1209. if [ "$AUTO_UPDATE" = "true" ]; then
  1210. setup_cron "$UPDATE_FREQ"
  1211. fi
  1212. fi
  1213. ;;
  1214. "clean")
  1215. local new_clean
  1216. new_clean=$(show_menu "Auto-Cleanup" "Automatically remove old kernels after update?" \
  1217. "true" "Yes" \
  1218. "false" "No")
  1219. if [ -n "$new_clean" ]; then
  1220. AUTO_CLEANUP="$new_clean"
  1221. save_config
  1222. log "Auto-cleanup set to $AUTO_CLEANUP"
  1223. fi
  1224. ;;
  1225. "silent")
  1226. local new_silent
  1227. new_silent=$(show_menu "Silent Notifications" "Silence update notifications?" \
  1228. "true" "Yes" \
  1229. "false" "No")
  1230. if [ -n "$new_silent" ]; then
  1231. SILENT_NOTIFICATIONS="$new_silent"
  1232. save_config
  1233. log "Silent notifications set to $SILENT_NOTIFICATIONS"
  1234. fi
  1235. ;;
  1236. "hardware")
  1237. local new_hardware
  1238. new_hardware=$(show_menu "Hardware Detection" "Detect hardware for optimized config?" \
  1239. "true" "Yes" \
  1240. "false" "No")
  1241. if [ -n "$new_hardware" ]; then
  1242. DETECT_HARDWARE="$new_hardware"
  1243. save_config
  1244. log "Hardware detection set to $DETECT_HARDWARE"
  1245. fi
  1246. ;;
  1247. "march")
  1248. local new_march
  1249. new_march=$(show_menu "CPU Optimization" "Choose optimization level:" \
  1250. "native" "Optimize for this CPU (-march=native)" \
  1251. "v3" "x86-64-v3 (Haswell/Excavator)" \
  1252. "v4" "x86-64-v4 (Skylake/Zen3)" \
  1253. "generic" "Generic x86-64")
  1254. if [ -n "$new_march" ]; then
  1255. MARCH="$new_march"
  1256. save_config
  1257. log "CPU optimization set to $MARCH"
  1258. fi
  1259. ;;
  1260. esac
  1261. ;;
  1262. "cron"|"Cron")
  1263. local cron_choice
  1264. cron_choice=$(show_menu "Automatic Updates" "Manage cron job:" \
  1265. "enable" "Enable automatic updates" \
  1266. "disable" "Disable automatic updates" \
  1267. "status" "Show current status" \
  1268. "back" "Return to main menu")
  1269.  
  1270. case "$cron_choice" in
  1271. "enable")
  1272. local freq_choice
  1273. freq_choice=$(show_menu "Update Frequency" "Choose frequency:" \
  1274. "daily" "Daily" \
  1275. "weekly" "Weekly" \
  1276. "monthly" "Monthly")
  1277. if [ -n "$freq_choice" ]; then
  1278. UPDATE_FREQ="$freq_choice"
  1279. save_config
  1280. setup_cron "$freq_choice"
  1281. show_msg "Cron Enabled" "Automatic updates enabled: $freq_choice"
  1282. fi
  1283. ;;
  1284. "disable")
  1285. setup_cron "never"
  1286. UPDATE_FREQ="never"
  1287. save_config
  1288. show_msg "Cron Disabled" "Automatic updates disabled."
  1289. ;;
  1290. "status")
  1291. local cron_status
  1292. cron_status=$(crontab -l 2>/dev/null | grep "$(realpath "$0" 2>/dev/null || echo "$0")" || echo "No cron job found")
  1293. show_msg "Cron Status" "Current cron jobs:\n\n$cron_status"
  1294. ;;
  1295. esac
  1296. ;;
  1297. "uninstall"|"Uninstall")
  1298. uninstall_kernel_menu
  1299. ;;
  1300. "quit"|"Quit")
  1301. show_msg "Goodbye" "Exiting Xanmod4Slack."
  1302. exit 0
  1303. ;;
  1304. *)
  1305. exit 0
  1306. ;;
  1307. esac
  1308.  
  1309. sleep 2
  1310. done
  1311. }
  1312.  
  1313. # --- Main Execution ---
  1314.  
  1315. # FIX: the original used `local new_version=...` inside this top-level
  1316. # case statement. `local` is only valid INSIDE a function — used here at
  1317. # script top level, bash rejects it with "local: can only be used in a
  1318. # function", the variable is never actually set, and --auto-update /
  1319. # --force-update were effectively broken. Plain variables (no `local`)
  1320. # are used below instead, plus the same declare/assign separation as
  1321. # above so exit-status checks are accurate.
  1322.  
  1323. case "$1" in
  1324. --auto-update)
  1325. log "Running automatic update check..."
  1326. load_config
  1327. if [ "${AUTO_UPDATE}" != "true" ]; then
  1328. log "Auto-update is disabled in configuration. Exiting."
  1329. exit 0
  1330. fi
  1331.  
  1332. new_version=$(check_updates)
  1333. rc=$?
  1334. if [ "$rc" -eq 0 ]; then
  1335. send_notification "Kernel Update Available" "XanMod $new_version is available. Building now..." "normal"
  1336. new_version=$(build_kernel)
  1337. rc=$?
  1338. if [ "$rc" -eq 0 ]; then
  1339. install_kernel
  1340. if [ "${AUTO_CLEANUP}" = "true" ]; then
  1341. cleanup_kernels
  1342. fi
  1343. send_notification "Kernel Update Complete" "XanMod $new_version installed successfully." "low"
  1344. log "Automatic update completed successfully"
  1345. else
  1346. send_notification "Kernel Build Failed" "Automatic build of XanMod $new_version failed. Check ${LOG_FILE}." "critical"
  1347. log "Automatic update FAILED during build"
  1348. exit 1
  1349. fi
  1350. fi
  1351. exit 0
  1352. ;;
  1353. --force-update)
  1354. log "Forcing kernel update..."
  1355. load_config
  1356. new_version=$(build_kernel)
  1357. rc=$?
  1358. if [ "$rc" -eq 0 ]; then
  1359. install_kernel
  1360. if [ "${AUTO_CLEANUP}" = "true" ]; then
  1361. cleanup_kernels
  1362. fi
  1363. send_notification "Kernel Update Complete" "XanMod $new_version installed successfully." "low"
  1364. else
  1365. log "Forced update FAILED during build"
  1366. exit 1
  1367. fi
  1368. exit 0
  1369. ;;
  1370. --help|-h)
  1371. echo "Xanmod4Slack v$VERSION - Universal XanMod Kernel Builder"
  1372. echo ""
  1373. echo "Usage: $0 [OPTIONS]"
  1374. echo " --auto-update Run automatic update check and build (cron mode)"
  1375. echo " --force-update Force update to latest version"
  1376. echo " --help, -h Show this help message"
  1377. echo " No options Run interactive menu"
  1378. echo ""
  1379. echo "Configuration file: $CONFIG_FILE"
  1380. echo "Log file: $LOG_FILE"
  1381. echo "Work directory: $WORK_DIR"
  1382. exit 0
  1383. ;;
  1384. *)
  1385. load_config
  1386. if [ $# -eq 0 ]; then
  1387. main_menu
  1388. else
  1389. echo "Unknown option: $1"
  1390. echo "Try '$0 --help' for usage information."
  1391. exit 1
  1392. fi
  1393. ;;
  1394. esac
  1395.  
  1396. exit 0
  1397.  
Advertisement
Add Comment
Please, Sign In to add comment