mush.sh 33 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944
  1. #!/bin/bash
  2. get_largest_nvme_namespace() {
  3. # this function doesn't exist if the version is old enough, so we redefine it
  4. local largest size tmp_size dev
  5. size=0
  6. dev=$(basename "$1")
  7. for nvme in /sys/block/"${dev%n*}"*; do
  8. tmp_size=$(cat "${nvme}"/size)
  9. if [ "${tmp_size}" -gt "${size}" ]; then
  10. largest="${nvme##*/}"
  11. size="${tmp_size}"
  12. fi
  13. done
  14. echo "${largest}"
  15. }
  16. traps() {
  17. set +e
  18. trap 'last_command=$current_command; current_command=$BASH_COMMAND' DEBUG
  19. trap 'echo \"${last_command}\" command failed with exit code $? - press a key to exit.' EXIT
  20. trap '' INT
  21. }
  22. mush_info() {
  23. echo -ne "\033]0;mush\007"
  24. if [ ! -f /mnt/stateful_partition/custom_greeting ]; then
  25. cat <<-EOF
  26. Welcome to mush, the murkmod developer shell.
  27. If you got here by mistake, don't panic! Just close this tab and carry on.
  28. This shell contains a list of utilities for performing various actions on a murkmodded chromebook.
  29. murkmod is now maintained completely independently from fakemurk. Don't report any bugs you encounter with it to the fakemurk developers.
  30. EOF
  31. else
  32. cat /mnt/stateful_partition/custom_greeting
  33. fi
  34. }
  35. doas() {
  36. ssh -t -p 1337 -i /rootkey -oStrictHostKeyChecking=no root@127.0.0.1 "$@"
  37. }
  38. runjob() {
  39. clear
  40. trap 'kill -2 $! >/dev/null 2>&1' INT
  41. (
  42. # shellcheck disable=SC2068
  43. $@
  44. )
  45. trap '' INT
  46. clear
  47. }
  48. swallow_stdin() {
  49. while read -t 0 notused; do
  50. read input
  51. done
  52. }
  53. edit() {
  54. if which nano 2>/dev/null; then
  55. doas nano "$@"
  56. else
  57. doas vi "$@"
  58. fi
  59. }
  60. locked_main() {
  61. traps
  62. mush_info
  63. while true; do
  64. echo -ne "\033]0;mush\007"
  65. cat <<-EOF
  66. (1) Emergency Revert & Re-Enroll
  67. (2) Soft Disable Extensions
  68. (3) Hard Disable Extensions
  69. (4) Hard Enable Extensions
  70. (5) Enter Admin Mode (Password-Protected)
  71. (6) Check for updates
  72. EOF
  73. swallow_stdin
  74. read -r -p "> (1-5): " choice
  75. case "$choice" in
  76. 1) runjob revert ;;
  77. 2) runjob softdisableext ;;
  78. 3) runjob harddisableext ;;
  79. 4) runjob hardenableext ;;
  80. 5) runjob prompt_passwd ;;
  81. 6) runjob do_updates && exit 0 ;;
  82. *) echo && echo "Invalid option, dipshit." && echo ;;
  83. esac
  84. done
  85. }
  86. main() {
  87. if [ -f /mnt/stateful_partition/murkmod/mush_password ]; then
  88. locked_main
  89. return
  90. fi
  91. traps
  92. mush_info
  93. while true; do
  94. echo -ne "\033]0;mush\007"
  95. cat <<-EOF
  96. (1) Root Shell
  97. (2) Chronos Shell
  98. (3) Crosh
  99. (4) Plugins
  100. (5) Install plugins
  101. (6) Uninstall plugins
  102. (7) Powerwash
  103. (8) Emergency Revert & Re-Enroll
  104. (9) Soft Disable Extensions
  105. (10) Hard Disable Extensions
  106. (11) Hard Enable Extensions
  107. (12) Automagically Disable Extensions
  108. (13) Edit Pollen
  109. (14) Install Crouton
  110. (15) Start Crouton
  111. (16) Enable dev_boot_usb
  112. (17) Disable dev_boot_usb
  113. (18) Set mush password
  114. (19) Remove mush password
  115. (20) [EXPERIMENTAL] Update ChromeOS
  116. (21) [EXPERIMENTAL] Update Emergency Backup
  117. (22) [EXPERIMENTAL] Restore Emergency Backup Backup
  118. (23) [EXPERIMENTAL] Install Chromebrew
  119. (24) [EXPERIMENTAL] Install Gentoo Boostrap (dev_install)
  120. (25) Check for updates
  121. EOF
  122. swallow_stdin
  123. read -r -p "> (1-25): " choice
  124. case "$choice" in
  125. 1) runjob doas bash ;;
  126. 2) runjob doas "cd /home/chronos; sudo -i -u chronos" ;;
  127. 3) runjob /usr/bin/crosh.old ;;
  128. 4) runjob show_plugins ;;
  129. 5) runjob install_plugins ;;
  130. 6) runjob uninstall_plugins ;;
  131. 7) runjob powerwash ;;
  132. 8) runjob revert ;;
  133. 9) runjob softdisableext ;;
  134. 10) runjob harddisableext ;;
  135. 11) runjob hardenableext ;;
  136. 12) runjob autodisableexts ;;
  137. 13) runjob edit /etc/opt/chrome/policies/managed/policy.json ;;
  138. 14) runjob install_crouton ;;
  139. 15) runjob run_crouton ;;
  140. 16) runjob enable_dev_boot_usb ;;
  141. 17) runjob disable_dev_boot_usb ;;
  142. 18) runjob set_passwd ;;
  143. 19) runjob remove_passwd ;;
  144. 20) runjob attempt_chromeos_update ;;
  145. 21) runjob attempt_backup_update ;;
  146. 22) runjob attempt_restore_backup_backup ;;
  147. 23) runjob attempt_chromebrew_install ;;
  148. 24) runjob attempt_dev_install ;;
  149. 25) runjob do_updates && exit 0 ;;
  150. 26) runjob do_dev_updates && exit 0 ;;
  151. 101) runjob hard_disable_nokill ;; # anything prefixed with a 1 is for the helper extension, you can safely ignore these as a user
  152. 111) runjob hard_enable_nokill ;;
  153. 112) runjob ext_purge ;;
  154. 113) runjob list_plugins ;;
  155. 114) runjob install_plugin_legacy ;;
  156. 115) runjob uninstall_plugin_legacy ;;
  157. *) echo && echo "Invalid option, dipshit." && echo ;;
  158. esac
  159. done
  160. }
  161. do_install_plugin() {
  162. local url=$1
  163. local filename="$(echo "${url}" | rev | cut -d/ -f1 | rev)"
  164. if [ ! -f /mnt/stateful_partition/murkmod/plugins/$filename ]; then
  165. doas "pushd /mnt/stateful_partition/murkmod/plugins
  166. curl $url -O
  167. chmod 775 /mnt/stateful_partition/murkmod/plugins/$filename
  168. popd" > /dev/null
  169. local dependencies=($(grep -o 'PLUGIN_DEPENDENCIES\+=([^)]*)' "/mnt/stateful_partition/murkmod/plugins/$filename" | sed 's/PLUGIN_DEPENDENCIES\+=//; s/[()]//g'))
  170. for dep in "${dependencies[@]}"; do
  171. local dep_fixed=$(echo "$dep" | tr -d '"')
  172. echo "Installing $dep_fixed..."
  173. do_install_plugin "https://raw.githubusercontent.com/rainestorme/murkmod/main/plugins/$dep_fixed"
  174. done
  175. fi
  176. read -r -p "Press enter to continue." throwaway
  177. }
  178. install_plugin_legacy() {
  179. local raw_url="https://raw.githubusercontent.com/rainestorme/murkmod/main/plugins"
  180. echo "Find a plugin you want to install here: "
  181. echo " https://github.com/rainestorme/murkmod/tree/main/plugins"
  182. echo "Enter the name of a plugin (including the .sh) to install it (or q to quit):"
  183. read -r plugin_name
  184. local plugin_url="$raw_url/$plugin_name"
  185. local plugin_info=$(curl -s $plugin_url)
  186. if [[ $plugin_info == *"Not Found"* ]]; then
  187. echo "Plugin not found"
  188. else
  189. echo "Installing..."
  190. do_install_plugin "https://raw.githubusercontent.com/rainestorme/murkmod/main/plugins/$plugin_name"
  191. echo "Installed $plugin_name"
  192. fi
  193. }
  194. uninstall_plugin_legacy() {
  195. local raw_url="https://raw.githubusercontent.com/rainestorme/murkmod/main/plugins"
  196. echo "Enter the name of a plugin (including the .sh) to uninstall it (or q to quit):"
  197. read -r plugin_name
  198. doas "rm -rf /mnt/stateful_partition/murkmod/plugins/$plugin_name"
  199. }
  200. list_plugins() {
  201. plugins_dir="/mnt/stateful_partition/murkmod/plugins"
  202. plugin_files=()
  203. while IFS= read -r -d '' file; do
  204. plugin_files+=("$file")
  205. done < <(find "$plugins_dir" -type f -name "*.sh" -print0)
  206. plugin_info=()
  207. for file in "${plugin_files[@]}"; do
  208. plugin_script=$file
  209. PLUGIN_NAME=$(grep -o 'PLUGIN_NAME=".*"' "$plugin_script" | cut -d= -f2-)
  210. PLUGIN_FUNCTION=$(grep -o 'PLUGIN_FUNCTION=".*"' "$plugin_script" | cut -d= -f2-)
  211. PLUGIN_DESCRIPTION=$(grep -o 'PLUGIN_DESCRIPTION=".*"' "$plugin_script" | cut -d= -f2-)
  212. PLUGIN_AUTHOR=$(grep -o 'PLUGIN_AUTHOR=".*"' "$plugin_script" | cut -d= -f2-)
  213. PLUGIN_VERSION=$(grep -o 'PLUGIN_VERSION=".*"' "$plugin_script" | cut -d= -f2-)
  214. # remove quotes from around each PLUGIN_* variable
  215. PLUGIN_NAME=${PLUGIN_NAME:1:-1}
  216. PLUGIN_FUNCTION=${PLUGIN_FUNCTION:1:-1}
  217. PLUGIN_DESCRIPTION=${PLUGIN_DESCRIPTION:1:-1}
  218. PLUGIN_AUTHOR=${PLUGIN_AUTHOR:1:-1}
  219. if grep -q "menu_plugin" "$plugin_script"; then
  220. plugin_info+=("$PLUGIN_FUNCTION,$PLUGIN_NAME,$PLUGIN_DESCRIPTION,$PLUGIN_AUTHOR,$PLUGIN_VERSION")
  221. fi
  222. done
  223. to_print=""
  224. # Print menu options
  225. for i in "${!plugin_info[@]}"; do
  226. to_print="$to_print[][]${plugin_info[$i]}"
  227. done
  228. echo "$to_print"
  229. }
  230. do_dev_updates() {
  231. echo "Welcome to the secret murkmod developer update menu!"
  232. echo "This utility allows you to install murkmod from a specific branch on the git repo."
  233. echo "If you were trying to update murkmod normally, then don't panic! Just enter 'main' at the prompt and everything will work normally."
  234. read -p "> (branch name, eg. main): " branch
  235. doas "MURKMOD_BRANCH=$branch bash <(curl -SLk https://raw.githubusercontent.com/rainestorme/murkmod/main/murkmod.sh)"
  236. exit
  237. }
  238. disable_ext() {
  239. local extid="$1"
  240. echo "$extid" | grep -qE '^[a-z]{32}$' && chmod 000 "/home/chronos/user/Extensions/$extid" && kill -9 $(pgrep -f "\-\-extension\-process") || "Extension ID $extid is invalid."
  241. }
  242. disable_ext_nokill() {
  243. local extid="$1"
  244. echo "$extid" | grep -qE '^[a-z]{32}$' && chmod 000 "/home/chronos/user/Extensions/$extid" || "Extension ID $extid is invalid."
  245. }
  246. enable_ext_nokill() {
  247. local extid="$1"
  248. echo "$extid" | grep -qE '^[a-z]{32}$' && chmod 777 "/home/chronos/user/Extensions/$extid" || "Invalid extension id."
  249. }
  250. ext_purge() {
  251. kill -9 $(pgrep -f "\-\-extension\-process")
  252. }
  253. hard_disable_nokill() {
  254. read -r -p "Enter extension ID > " extid
  255. disable_ext_nokill $extid
  256. }
  257. hard_enable_nokill() {
  258. read -r -p "Enter extension ID > " extid
  259. enable_ext_nokill $extid
  260. }
  261. autodisableexts() {
  262. echo "Disabling extensions..."
  263. disable_ext_nokill "haldlgldplgnggkjaafhelgiaglafanh" # GoGuardian
  264. disable_ext_nokill "dikiaagfielfbnbbopidjjagldjopbpa" # Clever Plus
  265. disable_ext_nokill "cgbbbjmgdpnifijconhamggjehlamcif" # Gopher Buddy
  266. disable_ext_nokill "inoeonmfapjbbkmdafoankkfajkcphgd" # Read and Write for Google Chrome
  267. disable_ext_nokill "enfolipbjmnmleonhhebhalojdpcpdoo" # Screenshot reader
  268. disable_ext_nokill "joflmkccibkooplaeoinecjbmdebglab" # Securly
  269. disable_ext_nokill "iheobagjkfklnlikgihanlhcddjoihkg" # Securly again
  270. disable_ext_nokill "adkcpkpghahmbopkjchobieckeoaoeem" # LightSpeed
  271. disable_ext_nokill "jcdhmojfecjfmbdpchihbeilohgnbdci" # Cisco Umbrella
  272. disable_ext_nokill "jdogphakondfdmcanpapfahkdomaicfa" # ContentKeeper Authenticator
  273. disable_ext_nokill "aceopacgaepdcelohobicpffbbejnfac" # Hapara
  274. disable_ext_nokill "kmffehbidlalibfeklaefnckpidbodff" # iBoss
  275. disable_ext_nokill "jaoebcikabjppaclpgbodmmnfjihdngk" # LightSpeed Classroom
  276. disable_ext_nokill "ghlpmldmjjhmdgmneoaibbegkjjbonbk" # Blocksi
  277. disable_ext_nokill "ddfbkhpmcdbciejenfcolaaiebnjcbfc" # Linewize
  278. disable_ext_nokill "jfbecfmiegcjddenjhlbhlikcbfmnafd" # Securly Classroom
  279. disable_ext_nokill "jjpmjccpemllnmgiaojaocgnakpmfgjg" # Impero
  280. disable_ext_nokill "feepmdlmhplaojabeoecaobfmibooaid" # OrbitNote
  281. disable_ext_nokill "dmhpekdihnngbkinliefnclgmgkpjeoo" # GoGuardian License
  282. disable_ext_nokill "modkadcjnbamppdpdkfoackjnhnfiogi" # MyMPS Chrome SSO
  283. echo "Done."
  284. }
  285. set_passwd() {
  286. echo "Enter a new password to use for mush. This will be required to perform any future administrative actions, so make sure you write it down somewhere!"
  287. read -r -p " > " newpassword
  288. doas "touch /mnt/stateful_partition/murkmod/mush_password"
  289. doas "echo '$newpassword'> /mnt/stateful_partition/murkmod/mush_password"
  290. }
  291. remove_passwd() {
  292. echo "Removing password from mush..."
  293. doas "rm -f /mnt/stateful_partition/murkmod/mush_password"
  294. }
  295. prompt_passwd() {
  296. echo "Enter your password:"
  297. read -r -p " > " password
  298. if grep "$password" /mnt/stateful_partition/murkmod/mush_password >/dev/null
  299. then
  300. main
  301. return
  302. else
  303. echo "Incorrect password."
  304. read -r -p "Press enter to continue." throwaway
  305. fi
  306. }
  307. disable_dev_boot_usb() {
  308. echo "Disabling dev_boot_usb"
  309. sed -i 's/\(dev_boot_usb=\).*/\10/' /usr/bin/crossystem
  310. }
  311. enable_dev_boot_usb() {
  312. echo "Enabling dev_boot_usb"
  313. sed -i 's/\(dev_boot_usb=\).*/\11/' /usr/bin/crossystem
  314. }
  315. do_updates() {
  316. doas "bash <(curl -SLk https://raw.githubusercontent.com/rainestorme/murkmod/main/murkmod.sh)"
  317. exit
  318. }
  319. show_plugins() {
  320. plugins_dir="/mnt/stateful_partition/murkmod/plugins"
  321. plugin_files=()
  322. while IFS= read -r -d '' file; do
  323. plugin_files+=("$file")
  324. done < <(find "$plugins_dir" -type f -name "*.sh" -print0)
  325. plugin_info=()
  326. for file in "${plugin_files[@]}"; do
  327. plugin_script=$file
  328. PLUGIN_NAME=$(grep -o 'PLUGIN_NAME=".*"' "$plugin_script" | cut -d= -f2-)
  329. PLUGIN_FUNCTION=$(grep -o 'PLUGIN_FUNCTION=".*"' "$plugin_script" | cut -d= -f2-)
  330. PLUGIN_DESCRIPTION=$(grep -o 'PLUGIN_DESCRIPTION=".*"' "$plugin_script" | cut -d= -f2-)
  331. PLUGIN_AUTHOR=$(grep -o 'PLUGIN_AUTHOR=".*"' "$plugin_script" | cut -d= -f2-)
  332. PLUGIN_VERSION=$(grep -o 'PLUGIN_VERSION=".*"' "$plugin_script" | cut -d= -f2-)
  333. # remove quotes from around each PLUGIN_* variable
  334. PLUGIN_NAME=${PLUGIN_NAME:1:-1}
  335. PLUGIN_FUNCTION=${PLUGIN_FUNCTION:1:-1}
  336. PLUGIN_DESCRIPTION=${PLUGIN_DESCRIPTION:1:-1}
  337. PLUGIN_AUTHOR=${PLUGIN_AUTHOR:1:-1}
  338. if grep -q "menu_plugin" "$plugin_script"; then
  339. plugin_info+=("$PLUGIN_FUNCTION (provided by $PLUGIN_NAME)")
  340. fi
  341. done
  342. # Print menu options
  343. for i in "${!plugin_info[@]}"; do
  344. printf "%s. %s\n" "$((i+1))" "${plugin_info[$i]}"
  345. done
  346. # Prompt user for selection
  347. read -p "> Select a plugin (or q to quit): " selection
  348. if [ "$selection" = "q" ]; then
  349. return 0
  350. fi
  351. # Validate user's selection
  352. if ! [[ "$selection" =~ ^[1-9][0-9]*$ ]]; then
  353. echo "Invalid selection. Please enter a number between 0 and ${#plugin_info[@]}"
  354. return 1
  355. fi
  356. if ((selection < 1 || selection > ${#plugin_info[@]})); then
  357. echo "Invalid selection. Please enter a number between 0 and ${#plugin_info[@]}"
  358. return 1
  359. fi
  360. # Get plugin function name and corresponding file
  361. selected_plugin=${plugin_info[$((selection-1))]}
  362. selected_file=${plugin_files[$((selection-1))]}
  363. # Execute the plugin
  364. bash <(cat $selected_file) # weird syntax due to noexec mount
  365. return 0
  366. }
  367. install_plugins() {
  368. clear
  369. echo "Fetching plugin information..."
  370. json=$(curl -s "https://api.github.com/repos/rainestorme/murkmod/contents/plugins")
  371. file_contents=()
  372. download_urls=()
  373. for entry in $(echo "$json" | jq -c '.[]'); do
  374. # Check if the entry is a file (type equals "file")
  375. if [[ $(echo "$entry" | jq -r '.type') == "file" ]]; then
  376. # Get the download URL for the file
  377. download_url=$(echo "$entry" | jq -r '.download_url')
  378. # Fetch the content of the file and append it to the array
  379. file_contents+=("$(curl -s "$download_url")")
  380. download_urls+=("$download_url")
  381. fi
  382. done
  383. plugin_info=()
  384. for content in "${file_contents[@]}"; do
  385. # Create a temporary file to hold the content
  386. tmp_file=$(mktemp)
  387. echo "$content" > "$tmp_file"
  388. # Extract information from the file
  389. PLUGIN_NAME=$(grep -o 'PLUGIN_NAME=.*' "$tmp_file" | cut -d= -f2-)
  390. PLUGIN_FUNCTION=$(grep -o 'PLUGIN_FUNCTION=.*' "$tmp_file" | cut -d= -f2-)
  391. PLUGIN_DESCRIPTION=$(grep -o 'PLUGIN_DESCRIPTION=.*' "$tmp_file" | cut -d= -f2-)
  392. PLUGIN_AUTHOR=$(grep -o 'PLUGIN_AUTHOR=.*' "$tmp_file" | cut -d= -f2-)
  393. PLUGIN_VERSION=$(grep -o 'PLUGIN_VERSION=.*' "$tmp_file" | cut -d= -f2-)
  394. PLUGIN_NAME=${PLUGIN_NAME:1:-1}
  395. PLUGIN_FUNCTION=${PLUGIN_FUNCTION:1:-1}
  396. PLUGIN_DESCRIPTION=${PLUGIN_DESCRIPTION:1:-1}
  397. PLUGIN_AUTHOR=${PLUGIN_AUTHOR:1:-1}
  398. # Add information to the plugin_info array
  399. plugin_info+=(" $PLUGIN_NAME (version $PLUGIN_VERSION by $PLUGIN_AUTHOR) \n $PLUGIN_DESCRIPTION")
  400. # Remove the temporary file
  401. rm "$tmp_file"
  402. done
  403. clear
  404. echo "Available plugins (press q to exit):"
  405. selected_option=0
  406. while true; do
  407. for i in "${!plugin_info[@]}"; do
  408. if [ $i -eq $selected_option ]; then
  409. printf " -> "
  410. else
  411. printf " "
  412. fi
  413. printf "${plugin_info[$i]}"
  414. # see if the plugin is already installed - get the filename of the plugin from its download url
  415. filename=$(echo "${download_urls[$i]}" | rev | cut -d/ -f1 | rev)
  416. if [ -f "/mnt/stateful_partition/murkmod/plugins/$filename" ]; then
  417. echo " (installed)"
  418. else
  419. echo
  420. fi
  421. done
  422. # Read a single character from the user
  423. read -s -n 1 key
  424. # Check the pressed key and update the selected option
  425. case "$key" in
  426. "q") break ;; # Exit the loop if the user presses 'q'
  427. "A") ((selected_option--)) ;; # Arrow key up
  428. "B") ((selected_option++)) ;; # Arrow key down
  429. "") clear
  430. echo "Using URL: ${download_urls[$selected_option]}"
  431. echo "Installing plugin..."
  432. do_install_plugin "${download_urls[$selected_option]}"
  433. echo "Done!"
  434. ;;
  435. esac
  436. # Ensure the selected option stays within bounds
  437. ((selected_option = selected_option < 0 ? 0 : selected_option))
  438. ((selected_option = selected_option >= ${#plugin_info[@]} ? ${#plugin_info[@]} - 1 : selected_option))
  439. # Clear the screen for the next iteration
  440. clear
  441. echo "Available plugins (press q to exit):"
  442. done
  443. }
  444. uninstall_plugins() {
  445. clear
  446. plugins_dir="/mnt/stateful_partition/murkmod/plugins"
  447. plugin_files=()
  448. while IFS= read -r -d '' file; do
  449. plugin_files+=("$file")
  450. done < <(find "$plugins_dir" -type f -name "*.sh" -print0)
  451. plugin_info=()
  452. for file in "${plugin_files[@]}"; do
  453. plugin_script=$file
  454. PLUGIN_NAME=$(grep -o 'PLUGIN_NAME=.*' "$plugin_script" | cut -d= -f2-)
  455. PLUGIN_FUNCTION=$(grep -o 'PLUGIN_FUNCTION=.*' "$plugin_script" | cut -d= -f2-)
  456. PLUGIN_DESCRIPTION=$(grep -o 'PLUGIN_DESCRIPTION=.*' "$plugin_script" | cut -d= -f2-)
  457. PLUGIN_AUTHOR=$(grep -o 'PLUGIN_AUTHOR=.*' "$plugin_script" | cut -d= -f2-)
  458. PLUGIN_VERSION=$(grep -o 'PLUGIN_VERSION=.*' "$plugin_script" | cut -d= -f2-)
  459. PLUGIN_NAME=${PLUGIN_NAME:1:-1}
  460. PLUGIN_FUNCTION=${PLUGIN_FUNCTION:1:-1}
  461. PLUGIN_DESCRIPTION=${PLUGIN_DESCRIPTION:1:-1}
  462. PLUGIN_AUTHOR=${PLUGIN_AUTHOR:1:-1}
  463. plugin_info+=("$PLUGIN_NAME (version $PLUGIN_VERSION by $PLUGIN_AUTHOR)")
  464. done
  465. if [ ${#plugin_info[@]} -eq 0 ]; then
  466. echo "No plugins installed."
  467. read -r -p "Press enter to continue." throwaway
  468. return
  469. fi
  470. while true; do
  471. echo "Installed plugins:"
  472. for i in "${!plugin_info[@]}"; do
  473. echo "$(($i+1)). ${plugin_info[$i]}"
  474. done
  475. echo "0. Exit back to mush"
  476. read -r -p "Enter a number to uninstall a plugin, or 0 to exit: " choice
  477. if [ "$choice" -eq 0 ]; then
  478. clear
  479. return
  480. fi
  481. index=$(($choice-1))
  482. if [ "$index" -lt 0 ] || [ "$index" -ge ${#plugin_info[@]} ]; then
  483. echo "Invalid choice."
  484. continue
  485. fi
  486. plugin_file="${plugin_files[$index]}"
  487. PLUGIN_NAME=$(grep -o 'PLUGIN_NAME=".*"' "$plugin_file" | cut -d= -f2-)
  488. PLUGIN_FUNCTION=$(grep -o 'PLUGIN_FUNCTION=".*"' "$plugin_file" | cut -d= -f2-)
  489. PLUGIN_DESCRIPTION=$(grep -o 'PLUGIN_DESCRIPTION=".*"' "$plugin_file" | cut -d= -f2-)
  490. PLUGIN_AUTHOR=$(grep -o 'PLUGIN_AUTHOR=".*"' "$plugin_file" | cut -d= -f2-)
  491. PLUGIN_VERSION=$(grep -o 'PLUGIN_VERSION=".*"' "$plugin_file" | cut -d= -f2-)
  492. # remove quotes
  493. PLUGIN_NAME=${PLUGIN_NAME:1:-1}
  494. PLUGIN_FUNCTION=${PLUGIN_FUNCTION:1:-1}
  495. PLUGIN_DESCRIPTION=${PLUGIN_DESCRIPTION:1:-1}
  496. PLUGIN_AUTHOR=${PLUGIN_AUTHOR:1:-1}
  497. plugin_name="$PLUGIN_NAME (version $PLUGIN_VERSION by $PLUGIN_AUTHOR)"
  498. read -r -p "Are you sure you want to uninstall $plugin_name? [y/n] " confirm
  499. if [ "$confirm" == "y" ]; then
  500. doas rm "$plugin_file"
  501. echo "$plugin_name uninstalled."
  502. unset plugin_info[$index]
  503. plugin_info=("${plugin_info[@]}")
  504. fi
  505. done
  506. }
  507. powerwash() {
  508. echo "Are you sure you wanna powerwash? This will remove all user accounts and data, but won't remove fakemurk."
  509. sleep 2
  510. echo "(Press enter to continue, ctrl-c to cancel)"
  511. swallow_stdin
  512. read -r
  513. doas rm -f /stateful_unfucked
  514. doas reboot
  515. exit
  516. }
  517. revert() {
  518. echo "This option will re-enroll your chromebook and restore it to its exact state before fakemurk was run. This is useful if you need to quickly go back to normal."
  519. echo "This is *permanent*. You will not be able to fakemurk again unless you re-run everything from the beginning."
  520. echo "Are you sure - 100% sure - that you want to continue? (press enter to continue, ctrl-c to cancel)"
  521. swallow_stdin
  522. read -r
  523. printf "Setting kernel priority in 3 (this is your last chance to cancel)..."
  524. sleep 1
  525. printf "2..."
  526. sleep 1
  527. echo "1..."
  528. sleep 1
  529. echo "Setting kernel priority"
  530. DST=/dev/$(get_largest_nvme_namespace)
  531. if doas "((\$(cgpt show -n \"$DST\" -i 2 -P) > \$(cgpt show -n \"$DST\" -i 4 -P)))"; then
  532. doas cgpt add "$DST" -i 2 -P 0
  533. doas cgpt add "$DST" -i 4 -P 1
  534. else
  535. doas cgpt add "$DST" -i 4 -P 0
  536. doas cgpt add "$DST" -i 2 -P 1
  537. fi
  538. echo "Setting vpd..."
  539. doas vpd -i RW_VPD -s check_enrollment=1
  540. doas vpd -i RW_VPD -s block_devmode=1
  541. doas crossystem.old block_devmode=1
  542. echo "Setting stateful unfuck flag..."
  543. rm -f /stateful_unfucked
  544. echo "Done. Press enter to reboot"
  545. swallow_stdin
  546. read -r
  547. echo "Bye!"
  548. sleep 2
  549. doas reboot
  550. sleep 1000
  551. echo "Your chromebook should have rebooted by now. If your chromebook doesn't reboot in the next couple of seconds, press Esc+Refresh to do it manually."
  552. }
  553. harddisableext() { # calling it "hard disable" because it only reenables when you press
  554. read -r -p "Enter extension ID > " extid
  555. echo "$extid" | grep -qE '^[a-z]{32}$' && chmod 000 "/home/chronos/user/Extensions/$extid" && kill -9 $(pgrep -f "\-\-extension\-process") || "Invalid extension id."
  556. }
  557. hardenableext() {
  558. read -r -p "Enter extension ID > " extid
  559. echo "$extid" | grep -qE '^[a-z]{32}$' && chmod 777 "/home/chronos/user/Extensions/$extid" && kill -9 $(pgrep -f "\-\-extension\-process") || "Invalid extension id."
  560. }
  561. softdisableext() {
  562. echo "Extensions will stay disabled until you press Ctrl+c or close this tab"
  563. while true; do
  564. kill -9 $(pgrep -f "\-\-extension\-process") 2>/dev/null
  565. sleep 0.5
  566. done
  567. }
  568. # https://chromium.googlesource.com/chromiumos/docs/+/master/lsb-release.md
  569. lsbval() {
  570. local key="$1"
  571. local lsbfile="${2:-/etc/lsb-release}"
  572. if ! echo "${key}" | grep -Eq '^[a-zA-Z0-9_]+$'; then
  573. return 1
  574. fi
  575. sed -E -n -e \
  576. "/^[[:space:]]*${key}[[:space:]]*=/{
  577. s:^[^=]+=[[:space:]]*::
  578. s:[[:space:]]+$::
  579. p
  580. }" "${lsbfile}"
  581. }
  582. install_crouton() {
  583. # check if crouuton is already installed. if so, prompt the user to delete their old chroot
  584. if [ -f /mnt/stateful_partition/crouton_installed ] ; then
  585. read -p "Crouton is already installed. Would you like to delete your old chroot and create a new one? (y/N) " yn
  586. case $yn in
  587. [yY] ) doas "rm -rf /mnt/stateful_partition/crouton/chroots && rm -f /mnt/stateful_partition/crouton_installed";;
  588. [nN] ) return;;
  589. * ) return;;
  590. esac
  591. fi
  592. echo "Installing Crouton..."
  593. # if this is before v107, then we don't want to use the silence branch - audio is still supported
  594. local local_version=$(lsbval GOOGLE_RELEASE)
  595. if (( ${local_version%%\.*} <= 107 )); then
  596. doas "bash <(curl -SLk https://goo.gl/fd3zc) -r bullseye -t xfce"
  597. else
  598. # theoretically we could copy or link the includes for cras, but im not entirely sure how to do that
  599. # CROUTON_BRANCH=longliveaudiotools supports audio at the versions we're looking at, but it's experimental and tends to be broken
  600. # ig we can prompt the user?
  601. echo "Your version of ChromeOS is too recent to support the current main branch of Crouton. You can either install Crouton without audio support, or install the experimental audio branch. Which would you like to do?"
  602. echo "1. Install without audio support"
  603. echo "2. Install with experimental audio support (may be extremely broken)"
  604. read -r -p "> (1-2): " choice
  605. if [ "$choice" == "1" ]; then
  606. doas "CROUTON_BRANCH=silence bash <(curl -SLk https://goo.gl/fd3zc) -r bullseye -t xfce"
  607. elif [ "$choice" == "2" ]; then
  608. doas "CROUTON_BRANCH=longliveaudiotools bash <(curl -SLk https://goo.gl/fd3zc) -r bullseye -t xfce"
  609. else
  610. echo "Invalid option, defaulting to silence branch"
  611. doas "CROUTON_BRANCH=silence bash <(curl -SLk https://goo.gl/fd3zc) -r bullseye -t xfce"
  612. fi
  613. fi
  614. doas "bash <(echo 'touch /mnt/stateful_partition/crouton_installed')" # idfk about the syntax but it seems to work so im not complaining
  615. }
  616. run_crouton() {
  617. if [ -f /mnt/stateful_partition/crouton_installed ] ; then
  618. echo "Use Crtl+Shift+Alt+Forward and Ctrl+Shift+Alt+Back to toggle between desktops"
  619. doas "startxfce4"
  620. else
  621. echo "Install Crouton first!"
  622. read -p "Press enter to continue."
  623. fi
  624. }
  625. get_booted_kernnum() {
  626. if doas "((\$(cgpt show -n \"$dst\" -i 2 -P) > \$(cgpt show -n \"$dst\" -i 4 -P)))"; then
  627. echo -n 2
  628. else
  629. echo -n 4
  630. fi
  631. }
  632. opposite_num() {
  633. if [ "$1" == "2" ]; then
  634. echo -n 4
  635. elif [ "$1" == "4" ]; then
  636. echo -n 2
  637. elif [ "$1" == "3" ]; then
  638. echo -n 5
  639. elif [ "$1" == "5" ]; then
  640. echo -n 3
  641. else
  642. return 1
  643. fi
  644. }
  645. attempt_chromeos_update(){
  646. local builds=$(curl https://chromiumdash.appspot.com/cros/fetch_serving_builds?deviceCategory=Chrome%20OS)
  647. local release_board=$(lsbval CHROMEOS_RELEASE_BOARD)
  648. local board=${release_board%%-*}
  649. local hwid=$(jq "(.builds.$board[] | keys)[0]" <<<"$builds")
  650. local hwid=${hwid:1:-1}
  651. local latest_milestone=$(jq "(.builds.$board[].$hwid.pushRecoveries | keys) | .[length - 1]" <<<"$builds")
  652. local remote_version=$(jq ".builds.$board[].$hwid[$latest_milestone].version" <<<"$builds")
  653. local remote_version=${remote_version:1:-1}
  654. local local_version=$(lsbval GOOGLE_RELEASE)
  655. if (( ${remote_version%%\.*} > ${local_version%%\.*} )); then
  656. echo "Updating to ${remote_version}. THIS MAY DELETE ALL USER DATA! Press enter to confirm, Ctrl+C to cancel."
  657. read -r
  658. echo "Dumping emergency revert backup to stateful (this might take a while)..."
  659. echo "Finding correct partitions..."
  660. local dst=/dev/$(get_largest_nvme_namespace)
  661. local tgt_kern=$(opposite_num $(get_booted_kernnum))
  662. local tgt_root=$(( $tgt_kern + 1 ))
  663. local kerndev=${dst}p${tgt_kern}
  664. local rootdev=${dst}p${tgt_root}
  665. echo "Dumping kernel..."
  666. doas dd if=$kerndev of=/mnt/stateful_partition/murkmod/kern_backup.img bs=4M status=progress
  667. echo "Dumping rootfs..."
  668. doas dd if=$rootdev of=/mnt/stateful_partition/murkmod/root_backup.img bs=4M status=progress
  669. echo "Creating restore flag..."
  670. doas touch /mnt/stateful_partition/restore-emergency-backup
  671. doas chmod 777 /mnt/stateful_partition/restore-emergency-backup
  672. echo "Backups complete, actually updating now..."
  673. # read choice
  674. local reco_dl=$(jq ".builds.$board[].$hwid.pushRecoveries[$latest_milestone]" <<< "$builds")
  675. local tmpdir=/mnt/stateful_partition/update_tmp/
  676. doas mkdir $tmpdir
  677. echo "Downloading ${remote_version} from ${reco_dl}..."
  678. curl "${reco_dl:1:-1}" | doas "dd of=$tmpdir/image.zip status=progress"
  679. echo "Unzipping update binary..."
  680. cat $tmpdir/image.zip | gunzip | doas "dd of=$tmpdir/image.bin status=progress"
  681. doas rm -f $tmpdir/image.zip
  682. echo "Invoking image patcher..."
  683. doas image_patcher.sh "$tmpdir/image.bin"
  684. local loop=$(doas losetup -f | tr -d '\r')
  685. doas losetup -P "$loop" "$tmpdir/image.bin"
  686. echo "Performing update..."
  687. printf "Overwriting partitions in 3 (this is your last chance to cancel)..."
  688. sleep 1
  689. printf "2..."
  690. sleep 1
  691. echo "1..."
  692. sleep 1
  693. echo "Installing kernel patch to ${kerndev}..."
  694. doas dd if="${loop}p4" of="$kerndev" status=progress
  695. echo "Installing root patch to ${rootdev}..."
  696. doas dd if="${loop}p3" of="$rootdev" status=progress
  697. echo "Setting kernel priority..."
  698. doas cgpt add "$dst" -i 4 -P 0
  699. doas cgpt add "$dst" -i 2 -P 0
  700. doas cgpt add "$dst" -i "$tgt_kern" -P 1
  701. echo "Setting crossystem and vpd block_devmode..."
  702. doas crossystem.old block_devmode=0
  703. doas vpd -i RW_VPD -s block_devmode=0
  704. echo "Cleaning up..."
  705. doas rm -Rf $tmpdir
  706. read -p "Done! Press enter to continue."
  707. else
  708. echo "Update not required."
  709. read -p "Press enter to continue."
  710. fi
  711. }
  712. attempt_backup_update(){
  713. local builds=$(curl https://chromiumdash.appspot.com/cros/fetch_serving_builds?deviceCategory=Chrome%20OS)
  714. local release_board=$(lsbval CHROMEOS_RELEASE_BOARD)
  715. local board=${release_board%%-*}
  716. local hwid=$(jq "(.builds.$board[] | keys)[0]" <<<"$builds")
  717. local hwid=${hwid:1:-1}
  718. local latest_milestone=$(jq "(.builds.$board[].$hwid.pushRecoveries | keys) | .[length - 1]" <<<"$builds")
  719. local remote_version=$(jq ".builds.$board[].$hwid[$latest_milestone].version" <<<"$builds")
  720. local remote_version=${remote_version:1:-1}
  721. read -p "Do you want to make a backup of your backup, just in case? (Y/n) " yn
  722. case $yn in
  723. [yY] ) do_backup=true ;;
  724. [nN] ) do_backup=false ;;
  725. * ) do_backup=true ;;
  726. esac
  727. echo "Updating to ${remote_version}. THIS CAN POSSIBLY DAMAGE YOUR EMERGENCY BACKUP! Press enter to confirm, Ctrl+C to cancel."
  728. read -r
  729. echo "Finding correct partitions..."
  730. local dst=/dev/$(get_largest_nvme_namespace)
  731. local tgt_kern=$(opposite_num $(get_booted_kernnum))
  732. local tgt_root=$(( $tgt_kern + 1 ))
  733. local kerndev=${dst}p${tgt_kern}
  734. local rootdev=${dst}p${tgt_root}
  735. if [ "$do_backup" = true ] ; then
  736. echo "Dumping emergency revert backup to stateful (this might take a while)..."
  737. echo "Dumping kernel..."
  738. doas dd if=$kerndev of=/mnt/stateful_partition/murkmod/kern_backup.img bs=4M status=progress
  739. echo "Dumping rootfs..."
  740. doas dd if=$rootdev of=/mnt/stateful_partition/murkmod/root_backup.img bs=4M status=progress
  741. echo "Backups complete, actually updating now..."
  742. fi
  743. # read choice
  744. local reco_dl=$(jq ".builds.$board[].$hwid.pushRecoveries[$latest_milestone]" <<< "$builds")
  745. local tmpdir=/mnt/stateful_partition/update_tmp/
  746. doas mkdir $tmpdir
  747. echo "Downloading ${remote_version} from ${reco_dl}..."
  748. curl "${reco_dl:1:-1}" | doas "dd of=$tmpdir/image.zip status=progress"
  749. echo "Unzipping update binary..."
  750. cat $tmpdir/image.zip | gunzip | doas "dd of=$tmpdir/image.bin status=progress"
  751. doas rm -f $tmpdir/image.zip
  752. echo "Creating loop device..."
  753. local loop=$(doas losetup -f | tr -d '\r')
  754. doas losetup -P "$loop" "$tmpdir/image.bin"
  755. printf "Overwriting backup in 3 (this is your last chance to cancel)..."
  756. sleep 1
  757. printf "2..."
  758. sleep 1
  759. echo "1..."
  760. sleep 1
  761. echo "Performing update..."
  762. echo "Installing kernel patch to ${kerndev}..."
  763. doas dd if="${loop}p4" of="$kerndev" status=progress
  764. echo "Installing root patch to ${rootdev}..."
  765. doas dd if="${loop}p3" of="$rootdev" status=progress
  766. echo "Setting crossystem and vpd block_devmode..." # idrk why, but it can't hurt to be safe
  767. doas crossystem.old block_devmode=0
  768. doas vpd -i RW_VPD -s block_devmode=0
  769. echo "Cleaning up..."
  770. doas rm -Rf $tmpdir
  771. read -p "Done! Press enter to continue."
  772. }
  773. attempt_restore_backup_backup() {
  774. echo "Looking for backup files..."
  775. dst=/dev/$(get_largest_nvme_namespace)
  776. tgt_kern=$(opposite_num $(get_booted_kernnum))
  777. tgt_root=$(( $tgt_kern + 1 ))
  778. kerndev=${dst}p${tgt_kern}
  779. rootdev=${dst}p${tgt_root}
  780. if [ -f /mnt/stateful_partition/murkmod/kern_backup.img ] && [ -f /mnt/stateful_partition/murkmod/root_backup.img ]; then
  781. echo "Backup files found!"
  782. echo "Restoring kernel..."
  783. dd if=/mnt/stateful_partition/murkmod/kern_backup.img of=$kerndev bs=4M status=progress
  784. echo "Restoring rootfs..."
  785. dd if=/mnt/stateful_partition/murkmod/root_backup.img of=$rootdev bs=4M status=progress
  786. echo "Removing backup files..."
  787. rm /mnt/stateful_partition/murkmod/kern_backup.img
  788. rm /mnt/stateful_partition/murkmod/root_backup.img
  789. echo "Restored successfully!"
  790. read -p "Press enter to continue."
  791. else
  792. echo "Missing backup image, aborting!"
  793. read -p "Press enter to continue."
  794. fi
  795. }
  796. attempt_install_chromebrew() {
  797. doas 'sudo -i -u chronos curl -Ls git.io/vddgY | bash' # kinda works now with cros_debug
  798. read -p 'Press enter to exit'
  799. }
  800. attempt_dev_install() {
  801. doas 'dev_install'
  802. }
  803. if [ "$0" = "$BASH_SOURCE" ]; then
  804. stty sane
  805. main
  806. fi