freedombone-utils-selector 19KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637
  1. #!/bin/bash
  2. #
  3. # .---. . .
  4. # | | |
  5. # |--- .--. .-. .-. .-.| .-. .--.--. |.-. .-. .--. .-.
  6. # | | (.-' (.-' ( | ( )| | | | )( )| | (.-'
  7. # ' ' --' --' -' - -' ' ' -' -' -' ' - --'
  8. #
  9. # Freedom in the Cloud
  10. #
  11. # Functions for selecting which apps to install or remove
  12. #
  13. # License
  14. # =======
  15. #
  16. # Copyright (C) 2015-2018 Bob Mottram <bob@freedombone.net>
  17. #
  18. # This program is free software: you can redistribute it and/or modify
  19. # it under the terms of the GNU Affero General Public License as published by
  20. # the Free Software Foundation, either version 3 of the License, or
  21. # (at your option) any later version.
  22. #
  23. # This program is distributed in the hope that it will be useful,
  24. # but WITHOUT ANY WARRANTY; without even the implied warranty of
  25. # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  26. # GNU Affero General Public License for more details.
  27. #
  28. # You should have received a copy of the GNU Affero General Public License
  29. # along with this program. If not, see <http://www.gnu.org/licenses/>.
  30. # Array containing names of available apps
  31. APPS_AVAILABLE=()
  32. # Array containing 1 or 0 indicating installed apps
  33. APPS_INSTALLED=()
  34. # Apps selected with checklist
  35. APPS_CHOSEN=()
  36. # A list of the names of installed apps
  37. APPS_INSTALLED_NAMES=()
  38. # file containing a list of removed apps
  39. REMOVED_APPS_FILE=/root/removed
  40. INSTALLED_APPS_LIST=/usr/share/${PROJECT_NAME}/installed.txt
  41. # keep a list of which users have been added to which apps
  42. # so that when a new app is added existing users can be added
  43. APP_USERS_FILE=$HOME/app_users.txt
  44. if [ ! "$COMPLETION_FILE" ]; then
  45. COMPLETION_FILE="$HOME/${PROJECT_NAME}-completed.txt"
  46. fi
  47. # Loads variables defined at the beginning of an app script
  48. function app_load_variables {
  49. app_name=$1
  50. config_var_name=${app_name}_variables
  51. # shellcheck disable=SC2086
  52. if [ ! ${!config_var_name} ]; then
  53. echo $"${app_name}_variables was not found"
  54. return
  55. fi
  56. #shellcheck disable=SC1087,SC2125,SC2178
  57. configvarname=$config_var_name[@]
  58. #shellcheck disable=SC2206
  59. configvarname=( ${!configvarname} )
  60. for v in "${configvarname[@]}"
  61. do
  62. read_config_param "$v"
  63. done
  64. }
  65. # Saves variables for a given app script
  66. function app_save_variables {
  67. app_name=$1
  68. config_var_name=${app_name}_variables
  69. #shellcheck disable=SC2086
  70. if [ ! ${!config_var_name} ]; then
  71. return
  72. fi
  73. #shellcheck disable=SC1087,SC2125,SC2178
  74. configvarname=$config_var_name[@]
  75. #shellcheck disable=SC2206
  76. configvarname=( ${!configvarname} )
  77. for v in "${configvarname[@]}"
  78. do
  79. write_config_param "$v" "${!v}"
  80. done
  81. }
  82. # gets the variants list from an app script
  83. function app_variants {
  84. filename=$1
  85. variants_line=$(grep 'VARIANTS=' "${filename}")
  86. if [[ "$variants_line" == *"'"* ]]; then
  87. variants_list=$(echo "$variants_line" | awk -F '=' '{print $2}' | awk -F "'" '{print $2}')
  88. else
  89. variants_list=$(echo "$variants_line" | awk -F '=' '{print $2}' | awk -F '"' '{print $2}')
  90. fi
  91. echo "$variants_list"
  92. }
  93. # whether a given item is in an array
  94. function item_in_array {
  95. local e
  96. for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
  97. return 1
  98. }
  99. # returns a list of available system variants
  100. # based upon the variants string in each app script
  101. function available_system_variants {
  102. function_check item_in_array
  103. FILES="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*"
  104. new_available_variants_list=()
  105. for filename in $FILES
  106. do
  107. system_variants_list=$(app_variants "$filename")
  108. # shellcheck disable=SC2206
  109. variants_array=($system_variants_list)
  110. for variant_str in "${variants_array[@]}"
  111. do
  112. if ! item_in_array "${variant_str}" "${new_available_variants_list[@]}"; then
  113. new_available_variants_list+=("$variant_str")
  114. fi
  115. done
  116. done
  117. # shellcheck disable=SC2207
  118. available_variants_list=($(sort <<<"${new_available_variants_list[*]}"))
  119. }
  120. function is_valid_variant {
  121. sys_type="$1"
  122. available_variants_list=()
  123. function_check available_system_variants
  124. available_system_variants
  125. for variant_str in "${available_variants_list[@]}"
  126. do
  127. if [[ "$sys_type" == "$variant_str" ]]; then
  128. return "1"
  129. fi
  130. done
  131. return "0"
  132. }
  133. function show_available_variants {
  134. available_variants_list=()
  135. function_check available_system_variants
  136. available_system_variants
  137. for variant_str in "${available_variants_list[@]}"
  138. do
  139. echo " $variant_str"
  140. done
  141. }
  142. # mark a given app as having been removed so that it doesn't get reinstalled on updates
  143. function remove_app {
  144. app_name=$1
  145. if [ ! -f $REMOVED_APPS_FILE ]; then
  146. touch $REMOVED_APPS_FILE
  147. fi
  148. if ! grep -Fxq "_${app_name}_" $REMOVED_APPS_FILE; then
  149. echo "_${app_name}_" >> $REMOVED_APPS_FILE
  150. fi
  151. if grep -Fxq "install_${app_name}" "$COMPLETION_FILE"; then
  152. sed -i "/install_${app_name}/d" "$COMPLETION_FILE"
  153. fi
  154. if grep -Fxq "install_${app_name}" "$INSTALLED_APPS_LIST"; then
  155. sed -i "/install_${app_name}/d" "$INSTALLED_APPS_LIST"
  156. fi
  157. }
  158. # returns 1 if an app has been marked as removed
  159. function app_is_removed {
  160. app_name="$1"
  161. if [ ! -f $REMOVED_APPS_FILE ]; then
  162. echo "0"
  163. return
  164. fi
  165. if ! grep -Fxq "_${app_name}_" $REMOVED_APPS_FILE; then
  166. echo "0"
  167. else
  168. echo "1"
  169. fi
  170. }
  171. # Allows an app to be reinstalled even if it was previously marked as being removed
  172. function reinstall_app {
  173. app_name=$1
  174. if [ ! -f $REMOVED_APPS_FILE ]; then
  175. return
  176. fi
  177. if [[ $(app_is_removed "$app_name") == "1" ]]; then
  178. sed -i "/_${app_name}_/d" $REMOVED_APPS_FILE
  179. fi
  180. }
  181. # returns 1 if an app is installed
  182. function app_is_installed {
  183. app_name="$1"
  184. # Why does this secondary file exist, apart from COMPLETION_FILE ?
  185. # It's so that it is visible to unprivileged users from the user control panel
  186. if [ -f "$INSTALLED_APPS_LIST" ]; then
  187. if ! grep -Fxq "install_${app_name}" "$INSTALLED_APPS_LIST"; then
  188. echo "0"
  189. else
  190. echo "1"
  191. fi
  192. return
  193. fi
  194. # check the completion file to see if it was installed
  195. if [ ! -f "$COMPLETION_FILE" ]; then
  196. echo "0"
  197. return
  198. fi
  199. if ! grep -Fxq "install_${app_name}" "$COMPLETION_FILE"; then
  200. echo "0"
  201. else
  202. echo "1"
  203. fi
  204. }
  205. # called at the end of the install section of an app script
  206. function install_completed {
  207. if [ ! "${1}" ]; then
  208. exit 673935
  209. fi
  210. if ! grep -Fxq "install_${1}" "$COMPLETION_FILE"; then
  211. echo "install_${1}" >> "$COMPLETION_FILE"
  212. fi
  213. }
  214. # populates an array of "0" or "1" for whether apps are installed
  215. function get_apps_installed {
  216. for a in "${APPS_AVAILABLE[@]}"
  217. do
  218. APPS_INSTALLED+=("$(app_is_installed "$a")")
  219. done
  220. }
  221. # populates an array of installed app names
  222. function get_apps_installed_names {
  223. APPS_INSTALLED_NAMES=()
  224. for a in "${APPS_AVAILABLE[@]}"
  225. do
  226. if [[ $(app_is_installed "$a") == "1" ]]; then
  227. APPS_INSTALLED_NAMES+=("$a")
  228. fi
  229. done
  230. }
  231. # detects what apps are available
  232. function detect_apps {
  233. FILES="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*"
  234. function_check item_in_array
  235. APPS_AVAILABLE=()
  236. APPS_CHOSEN=()
  237. # for all the app scripts
  238. for filename in $FILES
  239. do
  240. app_name=$(echo "${filename}" | awk -F '-app-' '{print $2}')
  241. if ! item_in_array "${app_name}" "${APPS_AVAILABLE[@]}"; then
  242. APPS_AVAILABLE+=("${app_name}")
  243. APPS_CHOSEN+=("0")
  244. fi
  245. done
  246. function_check get_apps_installed
  247. get_apps_installed
  248. get_apps_installed_names
  249. }
  250. # detects what apps are available and can be installed
  251. # If the variants list within an app script is an empty string then
  252. # it is considered to be too experimental to be installable
  253. function detect_installable_apps {
  254. FILES="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*"
  255. APPS_AVAILABLE=()
  256. APPS_CHOSEN=()
  257. APPS_INSTALLED=()
  258. APPS_INSTALLED_NAMES=()
  259. function_check app_variants
  260. function_check app_is_installed
  261. function_check item_in_array
  262. # for all the app scripts
  263. for filename in $FILES
  264. do
  265. app_name=$(echo "${filename}" | awk -F '-app-' '{print $2}')
  266. if ! item_in_array "${app_name}" "${APPS_AVAILABLE[@]}"; then
  267. variants_list=$(app_variants "$filename")
  268. # check for empty string
  269. if [ ${#variants_list} -gt 0 ]; then
  270. APPS_AVAILABLE+=("${app_name}")
  271. APPS_CHOSEN+=("0")
  272. APPS_INSTALLED+=("$(app_is_installed "$app_name")")
  273. if [[ $(app_is_installed "$app_name") == "1" ]]; then
  274. APPS_INSTALLED_NAMES+=("$app_name")
  275. fi
  276. fi
  277. fi
  278. done
  279. }
  280. function detect_installed_apps {
  281. FILES="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*"
  282. APPS_AVAILABLE=()
  283. APPS_INSTALLED=()
  284. APPS_INSTALLED_NAMES=()
  285. function_check app_variants
  286. function_check app_is_installed
  287. function_check item_in_array
  288. # for all the app scripts
  289. for filename in $FILES
  290. do
  291. app_name=$(echo "${filename}" | awk -F '-app-' '{print $2}')
  292. if [[ $(app_is_installed "$app_name") == "1" ]]; then
  293. if ! item_in_array "${app_name}" "${APPS_AVAILABLE[@]}"; then
  294. variants_list=$(app_variants "$filename")
  295. if [ ${#variants_list} -gt 0 ]; then
  296. APPS_AVAILABLE+=("${app_name}")
  297. APPS_INSTALLED_NAMES+=("$app_name")
  298. fi
  299. fi
  300. fi
  301. done
  302. }
  303. # creates the APPS_AVAILABLE and APPS_CHOSEN arrays based on
  304. # the given variant name
  305. function choose_apps_for_variant {
  306. variant_name="$1"
  307. function_check item_in_array
  308. function_check app_variants
  309. function_check app_is_removed
  310. if [ ${#variant_name} -eq 0 ]; then
  311. echo $"No variant name for choosing apps"
  312. exit 237567
  313. fi
  314. FILES="/usr/share/${PROJECT_NAME}/apps/${PROJECT_NAME}-app-*"
  315. APPS_CHOSEN=()
  316. # for all the app scripts
  317. for filename in $FILES
  318. do
  319. app_name=$(echo "${filename}" | awk -F '-app-' '{print $2}')
  320. if item_in_array "${app_name}" "${APPS_AVAILABLE[@]}"; then
  321. if grep -q "VARIANTS=" "${filename}"; then
  322. variants_list=$(app_variants "$filename")
  323. if [[ "${variants_list}" == 'all'* || \
  324. "${variants_list}" == "$variant_name" || \
  325. "${variants_list}" == "$variant_name "* || \
  326. "${variants_list}" == *" $variant_name "* || \
  327. "${variants_list}" == *" $variant_name" ]]; then
  328. if [[ $(app_is_removed "${a}") == "0" ]]; then
  329. #echo $"${app_name} chosen"
  330. APPS_CHOSEN+=("1")
  331. else
  332. APPS_CHOSEN+=("0")
  333. fi
  334. else
  335. APPS_CHOSEN+=("0")
  336. fi
  337. else
  338. APPS_CHOSEN+=("0")
  339. fi
  340. fi
  341. done
  342. function_check get_apps_installed
  343. get_apps_installed
  344. }
  345. # show a list of apps which have been chosen
  346. function list_chosen_apps {
  347. app_index=0
  348. for a in "${APPS_AVAILABLE[@]}"
  349. do
  350. if [[ ${APPS_CHOSEN[$app_index]} == "1" ]]; then
  351. echo $"${a}"
  352. fi
  353. app_index=$((app_index+1))
  354. done
  355. }
  356. function remove_apps {
  357. app_index=0
  358. for a in "${APPS_AVAILABLE[@]}"
  359. do
  360. if [[ ${APPS_INSTALLED[$app_index]} == "1" ]]; then
  361. if [[ ${APPS_CHOSEN[$app_index]} == "0" ]]; then
  362. echo $"Removing users for application: ${a}"
  363. function_check remove_users_for_app
  364. remove_users_for_app "${a}"
  365. echo $"Removing application: ${a}"
  366. function_check app_load_variables
  367. app_load_variables "${a}"
  368. function_check remove_app
  369. remove_app "${a}"
  370. function_check "remove_${a}"
  371. "remove_${a}"
  372. echo $"${a} was removed"
  373. fi
  374. fi
  375. app_index=$((app_index+1))
  376. done
  377. update_installed_apps_list
  378. }
  379. function install_apps_interactive {
  380. echo $"Interactive installer"
  381. app_index=0
  382. for a in "${APPS_AVAILABLE[@]}"
  383. do
  384. if [[ ${APPS_INSTALLED[$app_index]} == "0" ]]; then
  385. if [[ ${APPS_CHOSEN[$app_index]} == "1" ]]; then
  386. # interactively obtain settings for this app
  387. if [[ $(function_exists "install_interactive_${a}") == "1" ]]; then
  388. "install_interactive_${a}"
  389. fi
  390. fi
  391. fi
  392. app_index=$((app_index+1))
  393. done
  394. echo $"Interactive settings complete"
  395. }
  396. function user_added_to_app {
  397. user_name="$1"
  398. app_name="$2"
  399. if [[ $(is_valid_user "$user_name") == "1" ]]; then
  400. if [[ $(function_exists "add_user_${app_name}") == "1" ]]; then
  401. if grep -Fxq "${app_name}_${user_name}" "$APP_USERS_FILE"; then
  402. echo "1"
  403. return
  404. fi
  405. fi
  406. fi
  407. echo "0"
  408. }
  409. function add_users_after_install {
  410. app_name="$1"
  411. read_config_param MY_USERNAME
  412. # ensure a minimum password length
  413. if [ ! "$MINIMUM_PASSWORD_LENGTH" ]; then
  414. MINIMUM_PASSWORD_LENGTH=20
  415. fi
  416. if [ ${#MINIMUM_PASSWORD_LENGTH} -lt 20 ]; then
  417. MINIMUM_PASSWORD_LENGTH=20
  418. fi
  419. ADMIN_USERNAME=$(get_completion_param "Admin user")
  420. if [ ! "$ADMIN_USERNAME" ]; then
  421. ADMIN_USERNAME=$MY_USERNAME
  422. fi
  423. for d in /home/*/ ; do
  424. USERNAME=$(echo "$d" | awk -F '/' '{print $3}')
  425. if [[ $(is_valid_user "$USERNAME") == "1" ]]; then
  426. if [[ "$USERNAME" != "$ADMIN_USERNAME" ]]; then
  427. if [[ $(user_added_to_app "${USERNAME}" "${app_name}") == "0" ]]; then
  428. valstr=$"Login for user ${USERNAME}="
  429. app_password="$(create_password ${MINIMUM_PASSWORD_LENGTH})"
  430. "add_user_${app_name}" "${USERNAME}" "${app_password}"
  431. echo "${app_name}_${USERNAME}" >> "$APP_USERS_FILE"
  432. fi
  433. fi
  434. fi
  435. done
  436. }
  437. function remove_users_for_app {
  438. app_name="$1"
  439. read_config_param MY_USERNAME
  440. for d in /home/*/ ; do
  441. USERNAME=$(echo "$d" | awk -F '/' '{print $3}')
  442. if [[ $(is_valid_user "$USERNAME") == "1" ]]; then
  443. if [[ "$USERNAME" != "$MY_USERNAME" ]]; then
  444. if [[ $(user_added_to_app "${USERNAME}" "${app_name}") == "1" ]]; then
  445. if [[ $(function_exists "remove_user_${app_name}") == "1" ]]; then
  446. "remove_user_${app_name}" "${USERNAME}"
  447. fi
  448. sed -i "/${app_name}_${USERNAME}/d" "$APP_USERS_FILE"
  449. fi
  450. fi
  451. fi
  452. done
  453. }
  454. function install_apps {
  455. is_interactive=$1
  456. APP_INSTALLED_SUCCESS=1
  457. # interactive install configuration for each app
  458. if [ "${is_interactive}" ]; then
  459. install_apps_interactive
  460. fi
  461. # now install the apps
  462. app_index=0
  463. for a in "${APPS_AVAILABLE[@]}"
  464. do
  465. if [[ ${APPS_INSTALLED[$app_index]} == "0" ]]; then
  466. if [[ ${APPS_CHOSEN[$app_index]} == "1" ]]; then
  467. # remove any temp files
  468. rm -rf /tmp/*
  469. if [ "${is_interactive}" ]; then
  470. # clears any removal indicator
  471. function_check reinstall_app
  472. reinstall_app "${a}"
  473. function_check app_load_variables
  474. app_load_variables "${a}"
  475. if [[ $(app_is_installed "${a}") == "1" ]]; then
  476. echo $"Upgrading application from interactive: ${a}"
  477. "upgrade_${a}"
  478. echo $"${a} was upgraded from interactive"
  479. else
  480. echo $"Installing application from interactive: ${a}"
  481. APP_INSTALLED=
  482. "install_${a}"
  483. if [ $APP_INSTALLED ]; then
  484. function_check app_save_variables
  485. app_save_variables "${a}"
  486. function_check add_users_after_install
  487. add_users_after_install "${a}"
  488. function_check lockdown_permissions
  489. lockdown_permissions
  490. function_check install_completed
  491. install_completed "${a}"
  492. echo $"${a} was installed from interactive"
  493. else
  494. echo "Failed to install: ${a}" >> "/var/log/${PROJECT_NAME}.log"
  495. APP_INSTALLED_SUCCESS=
  496. echo $"${a} was not installed from interactive"
  497. fi
  498. fi
  499. else
  500. # check if the app was removed
  501. if [[ $(app_is_removed "${a}") == "0" ]]; then
  502. function_check app_load_variables
  503. app_load_variables "${a}"
  504. if [[ $(app_is_installed "${a}") == "1" ]]; then
  505. echo $"Upgrading application: ${a}"
  506. "upgrade_${a}"
  507. echo $"${a} was upgraded"
  508. else
  509. echo $"Installing application: ${a}"
  510. APP_INSTALLED=
  511. "install_${a}"
  512. if [ $APP_INSTALLED ]; then
  513. function_check app_save_variables
  514. app_save_variables "${a}"
  515. function_check add_users_after_install
  516. add_users_after_install "${a}"
  517. function_check lockdown_permissions
  518. lockdown_permissions
  519. function_check install_completed
  520. install_completed "${a}"
  521. echo $"${a} was installed"
  522. else
  523. echo "Failed to install: ${a}" >> "/var/log/${PROJECT_NAME}.log"
  524. APP_INSTALLED_SUCCESS=
  525. echo $"${a} was not installed"
  526. fi
  527. fi
  528. else
  529. echo $"${a} has been removed and so will not be reinstalled"
  530. fi
  531. fi
  532. fi
  533. fi
  534. app_index=$((app_index+1))
  535. done
  536. function_check update_installed_apps_list
  537. update_installed_apps_list
  538. }
  539. # NOTE: deliberately no exit 0