freedombone-utils-selector 19KB

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