freedombone-utils-selector 19KB

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