mush.sh 33 KB

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