webapp.eclass 17 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581
  1. # Copyright 1999-2015 Gentoo Foundation
  2. # Distributed under the terms of the GNU General Public License v2
  3. # @ECLASS: webapp.eclass
  4. # @MAINTAINER:
  5. # web-apps@gentoo.org
  6. # @BLURB: functions for installing applications to run under a web server
  7. # @DESCRIPTION:
  8. # The webapp eclass contains functions to handle web applications with
  9. # webapp-config. Part of the implementation of GLEP #11
  10. # @ECLASS-VARIABLE: WEBAPP_DEPEND
  11. # @DESCRIPTION:
  12. # An ebuild should use WEBAPP_DEPEND if a custom DEPEND needs to be built, most
  13. # notably in combination with WEBAPP_OPTIONAL.
  14. WEBAPP_DEPEND=">=app-admin/webapp-config-1.50.15"
  15. # @ECLASS-VARIABLE: WEBAPP_NO_AUTO_INSTALL
  16. # @DESCRIPTION:
  17. # An ebuild sets this to `yes' if an automatic installation and/or upgrade is
  18. # not possible. The ebuild should overwrite pkg_postinst() and explain the
  19. # reason for this BEFORE calling webapp_pkg_postinst().
  20. # @ECLASS-VARIABLE: WEBAPP_OPTIONAL
  21. # @DESCRIPTION:
  22. # An ebuild sets this to `yes' to make webapp support optional, in which case
  23. # you also need to take care of USE-flags and dependencies.
  24. if [[ "${WEBAPP_OPTIONAL}" != "yes" ]]; then
  25. [[ "${WEBAPP_NO_AUTO_INSTALL}" == "yes" ]] || IUSE="vhosts"
  26. SLOT="${PVR}"
  27. DEPEND="${WEBAPP_DEPEND}"
  28. RDEPEND="${DEPEND}"
  29. fi
  30. EXPORT_FUNCTIONS pkg_postinst pkg_setup src_install pkg_prerm
  31. INSTALL_DIR="/${PN}"
  32. IS_UPGRADE=0
  33. IS_REPLACE=0
  34. INSTALL_CHECK_FILE="installed_by_webapp_eclass"
  35. SETUP_CHECK_FILE="setup_by_webapp_eclass"
  36. ETC_CONFIG="${ROOT}etc/vhosts/webapp-config"
  37. WEBAPP_CONFIG="${ROOT}usr/sbin/webapp-config"
  38. WEBAPP_CLEANER="${ROOT}usr/sbin/webapp-cleaner"
  39. # ==============================================================================
  40. # INTERNAL FUNCTIONS
  41. # ==============================================================================
  42. # Load the config file /etc/vhosts/webapp-config
  43. # Supports both the old bash version, and the new python version
  44. webapp_read_config() {
  45. debug-print-function $FUNCNAME $*
  46. if has_version '>=app-admin/webapp-config-1.50'; then
  47. ENVVAR=$(${WEBAPP_CONFIG} --query ${PN} ${PVR}) || die "Could not read settings from webapp-config!"
  48. eval ${ENVVAR}
  49. elif [[ "${WEBAPP_OPTIONAL}" != "yes" ]]; then
  50. # ETC_CONFIG might not be available
  51. . ${ETC_CONFIG} || die "Unable to read ${ETC_CONFIG}"
  52. elif [[ -f "${ETC_CONFIG}" ]]; then
  53. # WEBAPP_OPTIONAL is set to yes
  54. # and this must run only if ETC_CONFIG actually exists
  55. . ${ETC_CONFIG} || die "Unable to read ${ETC_CONFIG}"
  56. fi
  57. }
  58. # Check whether a specified file exists in the given directory (`.' by default)
  59. webapp_checkfileexists() {
  60. debug-print-function $FUNCNAME $*
  61. local my_prefix=${2:+${2}/}
  62. if [[ ! -e "${my_prefix}${1}" ]]; then
  63. msg="ebuild fault: file '${1}' not found"
  64. eerror "$msg"
  65. eerror "Please report this as a bug at https://bugs.gentoo.org/"
  66. die "$msg"
  67. fi
  68. }
  69. webapp_check_installedat() {
  70. debug-print-function $FUNCNAME $*
  71. ${WEBAPP_CONFIG} --show-installed -h localhost -d "${INSTALL_DIR}" 2> /dev/null
  72. }
  73. webapp_strip_appdir() {
  74. debug-print-function $FUNCNAME $*
  75. echo "${1#${MY_APPDIR}/}"
  76. }
  77. webapp_strip_d() {
  78. debug-print-function $FUNCNAME $*
  79. echo "${1#${D}}"
  80. }
  81. webapp_strip_cwd() {
  82. debug-print-function $FUNCNAME $*
  83. echo "${1/#.\///}"
  84. }
  85. webapp_getinstalltype() {
  86. debug-print-function $FUNCNAME $*
  87. if ! has vhosts ${IUSE} || use vhosts; then
  88. return
  89. fi
  90. local my_output
  91. my_output="$(webapp_check_installedat)"
  92. if [[ $? -eq 0 ]]; then
  93. # something is already installed there
  94. # make sure it isn't the same version
  95. local my_pn="$(echo ${my_output} | awk '{ print $1 }')"
  96. local my_pvr="$(echo ${my_output} | awk '{ print $2 }')"
  97. REMOVE_PKG="${my_pn}-${my_pvr}"
  98. if [[ "${my_pn}" == "${PN}" ]]; then
  99. if [[ "${my_pvr}" != "${PVR}" ]]; then
  100. elog "This is an upgrade"
  101. IS_UPGRADE=1
  102. # for binpkgs, reset status, var declared in global scope
  103. IS_REPLACE=0
  104. else
  105. elog "This is a re-installation"
  106. IS_REPLACE=1
  107. # for binpkgs, reset status, var declared in global scope
  108. IS_UPGRADE=0
  109. fi
  110. else
  111. elog "${my_output} is installed there"
  112. fi
  113. else
  114. # for binpkgs, reset status, var declared in global scope
  115. IS_REPLACE=0
  116. IS_UPGRADE=0
  117. elog "This is an installation"
  118. fi
  119. }
  120. # ==============================================================================
  121. # PUBLIC FUNCTIONS
  122. # ==============================================================================
  123. # @FUNCTION: need_httpd
  124. # @DESCRIPTION:
  125. # Call this function AFTER your ebuilds DEPEND line if any of the available
  126. # webservers are able to run this application.
  127. need_httpd() {
  128. DEPEND="${DEPEND}
  129. || ( virtual/httpd-basic virtual/httpd-cgi virtual/httpd-fastcgi )"
  130. }
  131. # @FUNCTION: need_httpd_cgi
  132. # @DESCRIPTION:
  133. # Call this function AFTER your ebuilds DEPEND line if any of the available
  134. # CGI-capable webservers are able to run this application.
  135. need_httpd_cgi() {
  136. DEPEND="${DEPEND}
  137. || ( virtual/httpd-cgi virtual/httpd-fastcgi )"
  138. }
  139. # @FUNCTION: need_httpd_fastcgi
  140. # @DESCRIPTION:
  141. # Call this function AFTER your ebuilds DEPEND line if any of the available
  142. # FastCGI-capabale webservers are able to run this application.
  143. need_httpd_fastcgi() {
  144. DEPEND="${DEPEND}
  145. virtual/httpd-fastcgi"
  146. }
  147. # @FUNCTION: webapp_configfile
  148. # @USAGE: <file> [more files ...]
  149. # @DESCRIPTION:
  150. # Mark a file config-protected for a web-based application.
  151. webapp_configfile() {
  152. debug-print-function $FUNCNAME $*
  153. local m
  154. for m in "$@"; do
  155. webapp_checkfileexists "${m}" "${D}"
  156. local my_file="$(webapp_strip_appdir "${m}")"
  157. my_file="$(webapp_strip_cwd "${my_file}")"
  158. elog "(config) ${my_file}"
  159. echo "${my_file}" >> ${D}/${WA_CONFIGLIST}
  160. done
  161. }
  162. # @FUNCTION: webapp_hook_script
  163. # @USAGE: <file>
  164. # @DESCRIPTION:
  165. # Install a script that will run after a virtual copy is created, and
  166. # before a virtual copy has been removed.
  167. webapp_hook_script() {
  168. debug-print-function $FUNCNAME $*
  169. webapp_checkfileexists "${1}"
  170. elog "(hook) ${1}"
  171. cp "${1}" "${D}/${MY_HOOKSCRIPTSDIR}/$(basename "${1}")" || die "Unable to install ${1} into ${D}/${MY_HOOKSCRIPTSDIR}/"
  172. chmod 555 "${D}/${MY_HOOKSCRIPTSDIR}/$(basename "${1}")"
  173. }
  174. # @FUNCTION: webapp_postinst_txt
  175. # @USAGE: <lang> <file>
  176. # @DESCRIPTION:
  177. # Install a text file containing post-installation instructions.
  178. webapp_postinst_txt() {
  179. debug-print-function $FUNCNAME $*
  180. webapp_checkfileexists "${2}"
  181. elog "(info) ${2} (lang: ${1})"
  182. cp "${2}" "${D}/${MY_APPDIR}/postinst-${1}.txt"
  183. }
  184. # @FUNCTION: webapp_postupgrade_txt
  185. # @USAGE: <lang> <file>
  186. # @DESCRIPTION:
  187. # Install a text file containing post-upgrade instructions.
  188. webapp_postupgrade_txt() {
  189. debug-print-function $FUNCNAME $*
  190. webapp_checkfileexists "${2}"
  191. elog "(info) ${2} (lang: ${1})"
  192. cp "${2}" "${D}/${MY_APPDIR}/postupgrade-${1}.txt"
  193. }
  194. # helper for webapp_serverowned()
  195. _webapp_serverowned() {
  196. debug-print-function $FUNCNAME $*
  197. webapp_checkfileexists "${1}" "${D}"
  198. local my_file="$(webapp_strip_appdir "${1}")"
  199. my_file="$(webapp_strip_cwd "${my_file}")"
  200. echo "${my_file}" >> "${D}/${WA_SOLIST}"
  201. }
  202. # @FUNCTION: webapp_serverowned
  203. # @USAGE: [-R] <file> [more files ...]
  204. # @DESCRIPTION:
  205. # Identify a file which must be owned by the webserver's user:group settings.
  206. # The ownership of the file is NOT set until the application is installed using
  207. # the webapp-config tool. If -R is given directories are handled recursively.
  208. webapp_serverowned() {
  209. debug-print-function $FUNCNAME $*
  210. local a m
  211. if [[ "${1}" == "-R" ]]; then
  212. shift
  213. for m in "$@"; do
  214. find "${D}${m}" | while read a; do
  215. a=$(webapp_strip_d "${a}")
  216. _webapp_serverowned "${a}"
  217. done
  218. done
  219. else
  220. for m in "$@"; do
  221. _webapp_serverowned "${m}"
  222. done
  223. fi
  224. }
  225. # @FUNCTION: webapp_server_configfile
  226. # @USAGE: <server> <file> [new name]
  227. # @DESCRIPTION:
  228. # Install a configuration file for the webserver. You need to specify a
  229. # webapp-config supported <server>. if no new name is given `basename $2' is
  230. # used by default. Note: this function will automagically prepend $1 to the
  231. # front of your config file's name.
  232. webapp_server_configfile() {
  233. debug-print-function $FUNCNAME $*
  234. webapp_checkfileexists "${2}"
  235. # WARNING:
  236. #
  237. # do NOT change the naming convention used here without changing all
  238. # the other scripts that also rely upon these names
  239. local my_file="${1}-${3:-$(basename "${2}")}"
  240. elog "(${1}) config file '${my_file}'"
  241. cp "${2}" "${D}/${MY_SERVERCONFIGDIR}/${my_file}"
  242. }
  243. # @FUNCTION: webapp_sqlscript
  244. # @USAGE: <db> <file> [version]
  245. # @DESCRIPTION:
  246. # Install a SQL script that creates/upgrades a database schema for the web
  247. # application. Currently supported database engines are mysql and postgres.
  248. # If a version is given the script should upgrade the database schema from
  249. # the given version to $PVR.
  250. webapp_sqlscript() {
  251. debug-print-function $FUNCNAME $*
  252. webapp_checkfileexists "${2}"
  253. dodir "${MY_SQLSCRIPTSDIR}/${1}"
  254. # WARNING:
  255. #
  256. # do NOT change the naming convention used here without changing all
  257. # the other scripts that also rely upon these names
  258. if [[ -n "${3}" ]]; then
  259. elog "(${1}) upgrade script for ${PN}-${3} to ${PVR}"
  260. cp "${2}" "${D}${MY_SQLSCRIPTSDIR}/${1}/${3}_to_${PVR}.sql"
  261. chmod 600 "${D}${MY_SQLSCRIPTSDIR}/${1}/${3}_to_${PVR}.sql"
  262. else
  263. elog "(${1}) create script for ${PN}-${PVR}"
  264. cp "${2}" "${D}/${MY_SQLSCRIPTSDIR}/${1}/${PVR}_create.sql"
  265. chmod 600 "${D}/${MY_SQLSCRIPTSDIR}/${1}/${PVR}_create.sql"
  266. fi
  267. }
  268. # @FUNCTION: webapp_src_preinst
  269. # @DESCRIPTION:
  270. # You need to call this function in src_install() BEFORE anything else has run.
  271. # For now we just create required webapp-config directories.
  272. webapp_src_preinst() {
  273. debug-print-function $FUNCNAME $*
  274. # sanity checks, to catch bugs in the ebuild
  275. if [[ ! -f "${T}/${SETUP_CHECK_FILE}" ]]; then
  276. eerror
  277. eerror "This ebuild did not call webapp_pkg_setup() at the beginning"
  278. eerror "of the pkg_setup() function"
  279. eerror
  280. eerror "Please log a bug on https://bugs.gentoo.org"
  281. eerror
  282. eerror "You should use emerge -C to remove this package, as the"
  283. eerror "installation is incomplete"
  284. eerror
  285. die "Ebuild did not call webapp_pkg_setup() - report to https://bugs.gentoo.org"
  286. fi
  287. # Hint, see the webapp_read_config() function to find where these are
  288. # defined.
  289. dodir "${MY_HTDOCSDIR}"
  290. dodir "${MY_HOSTROOTDIR}"
  291. dodir "${MY_CGIBINDIR}"
  292. dodir "${MY_ICONSDIR}"
  293. dodir "${MY_ERRORSDIR}"
  294. dodir "${MY_SQLSCRIPTSDIR}"
  295. dodir "${MY_HOOKSCRIPTSDIR}"
  296. dodir "${MY_SERVERCONFIGDIR}"
  297. }
  298. # ==============================================================================
  299. # EXPORTED FUNCTIONS
  300. # ==============================================================================
  301. # @FUNCTION: webapp_pkg_setup
  302. # @DESCRIPTION:
  303. # The default pkg_setup() for this eclass. This will gather required variables
  304. # from webapp-config and check if there is an application installed to
  305. # `${ROOT}/var/www/localhost/htdocs/${PN}/' if USE=vhosts is not set.
  306. #
  307. # You need to call this function BEFORE anything else has run in your custom
  308. # pkg_setup().
  309. webapp_pkg_setup() {
  310. debug-print-function $FUNCNAME $*
  311. # to test whether or not the ebuild has correctly called this function
  312. # we add an empty file to the filesystem
  313. #
  314. # we used to just set a variable in the shell script, but we can
  315. # no longer rely on Portage calling both webapp_pkg_setup() and
  316. # webapp_src_install() within the same shell process
  317. touch "${T}/${SETUP_CHECK_FILE}"
  318. # special case - some ebuilds *do* need to overwride the SLOT
  319. if [[ "${SLOT}+" != "${PVR}+" && "${WEBAPP_MANUAL_SLOT}" != "yes" ]]; then
  320. die "Set WEBAPP_MANUAL_SLOT=\"yes\" if you need to SLOT manually"
  321. fi
  322. # pull in the shared configuration file
  323. G_HOSTNAME="localhost"
  324. webapp_read_config
  325. local my_dir="${ROOT}${VHOST_ROOT}/${MY_HTDOCSBASE}/${PN}"
  326. # if USE=vhosts is enabled OR no application is installed we're done here
  327. if ! has vhosts ${IUSE} || use vhosts || [[ ! -d "${my_dir}" ]]; then
  328. return
  329. fi
  330. local my_output
  331. my_output="$(webapp_check_installedat)"
  332. if [[ $? -ne 0 ]]; then
  333. # okay, whatever is there, it isn't webapp-config-compatible
  334. echo
  335. ewarn
  336. ewarn "You already have something installed in ${my_dir}"
  337. ewarn
  338. ewarn "Whatever is in ${my_dir}, it's not"
  339. ewarn "compatible with webapp-config."
  340. ewarn
  341. ewarn "This ebuild may be overwriting important files."
  342. ewarn
  343. echo
  344. if has "${EAPI:-0}" 0 1 2; then
  345. ebeep 10
  346. fi
  347. elif [[ "$(echo ${my_output} | awk '{ print $1 }')" != "${PN}" ]]; then
  348. echo
  349. eerror "You already have ${my_output} installed in ${my_dir}"
  350. eerror
  351. eerror "I cannot upgrade a different application"
  352. eerror
  353. echo
  354. die "Cannot upgrade contents of ${my_dir}"
  355. fi
  356. }
  357. # @FUNCTION: webapp_src_install
  358. # @DESCRIPTION:
  359. # This is the default src_install(). For now, we just make sure that root owns
  360. # everything, and that there are no setuid files.
  361. #
  362. # You need to call this function AFTER everything else has run in your custom
  363. # src_install().
  364. webapp_src_install() {
  365. debug-print-function $FUNCNAME $*
  366. # to test whether or not the ebuild has correctly called this function
  367. # we add an empty file to the filesystem
  368. #
  369. # we used to just set a variable in the shell script, but we can
  370. # no longer rely on Portage calling both webapp_src_install() and
  371. # webapp_pkg_postinst() within the same shell process
  372. touch "${D}/${MY_APPDIR}/${INSTALL_CHECK_FILE}"
  373. chown -R "${VHOST_DEFAULT_UID}:${VHOST_DEFAULT_GID}" "${D}/"
  374. chmod -R u-s "${D}/"
  375. chmod -R g-s "${D}/"
  376. keepdir "${MY_PERSISTDIR}"
  377. fowners "root:0" "${MY_PERSISTDIR}"
  378. fperms 755 "${MY_PERSISTDIR}"
  379. }
  380. # @FUNCTION: webapp_pkg_postinst
  381. # @DESCRIPTION:
  382. # The default pkg_postinst() for this eclass. This installs the web application to
  383. # `${ROOT}/var/www/localhost/htdocs/${PN}/' if USE=vhosts is not set. Otherwise
  384. # display a short notice how to install this application with webapp-config.
  385. #
  386. # You need to call this function AFTER everything else has run in your custom
  387. # pkg_postinst().
  388. webapp_pkg_postinst() {
  389. debug-print-function $FUNCNAME $*
  390. webapp_read_config
  391. # sanity checks, to catch bugs in the ebuild
  392. if [[ ! -f "${ROOT}${MY_APPDIR}/${INSTALL_CHECK_FILE}" ]]; then
  393. eerror
  394. eerror "This ebuild did not call webapp_src_install() at the end"
  395. eerror "of the src_install() function"
  396. eerror
  397. eerror "Please log a bug on https://bugs.gentoo.org"
  398. eerror
  399. eerror "You should use emerge -C to remove this package, as the"
  400. eerror "installation is incomplete"
  401. eerror
  402. die "Ebuild did not call webapp_src_install() - report to https://bugs.gentoo.org"
  403. fi
  404. if has vhosts ${IUSE}; then
  405. if ! use vhosts; then
  406. echo
  407. elog "vhosts USE flag not set - auto-installing using webapp-config"
  408. G_HOSTNAME="localhost"
  409. webapp_read_config
  410. local my_mode=-I
  411. webapp_getinstalltype
  412. if [[ "${IS_REPLACE}" == "1" ]]; then
  413. elog "${PN}-${PVR} is already installed - replacing"
  414. my_mode=-I
  415. elif [[ "${IS_UPGRADE}" == "1" ]]; then
  416. elog "${REMOVE_PKG} is already installed - upgrading"
  417. my_mode=-U
  418. else
  419. elog "${PN}-${PVR} is not installed - using install mode"
  420. fi
  421. my_cmd="${WEBAPP_CONFIG} -h localhost -u root -d ${INSTALL_DIR} ${my_mode} ${PN} ${PVR}"
  422. elog "Running ${my_cmd}"
  423. ${my_cmd}
  424. echo
  425. local cleaner="${WEBAPP_CLEANER} -p -C ${CATEGORY}/${PN}"
  426. einfo "Running ${cleaner}"
  427. ${cleaner}
  428. else
  429. elog
  430. elog "The 'vhosts' USE flag is switched ON"
  431. elog "This means that Portage will not automatically run webapp-config to"
  432. elog "complete the installation."
  433. elog
  434. elog "To install ${PN}-${PVR} into a virtual host, run the following command:"
  435. elog
  436. elog " webapp-config -h <host> -d ${PN} -I ${PN} ${PVR}"
  437. elog
  438. elog "For more details, see the webapp-config(8) man page"
  439. fi
  440. else
  441. elog
  442. elog "This ebuild does not support the 'vhosts' USE flag."
  443. elog "This means that Portage will not automatically run webapp-config to"
  444. elog "complete the installation."
  445. elog
  446. elog "To install ${PN}-${PVR} into a virtual host, run the following command:"
  447. elog
  448. elog " webapp-config -h <host> -d ${PN} -I ${PN} ${PVR}"
  449. elog
  450. elog "For more details, see the webapp-config(8) man page"
  451. fi
  452. }
  453. # @FUNCTION: webapp_pkg_prerm
  454. # @DESCRIPTION:
  455. # This is the default pkg_prerm() for this eclass. If USE=vhosts is not set
  456. # remove all installed copies of this web application. Otherwise instruct the
  457. # user to manually remove those copies. See bug #136959.
  458. webapp_pkg_prerm() {
  459. debug-print-function $FUNCNAME $*
  460. local my_output=
  461. my_output="$(${WEBAPP_CONFIG} --list-installs ${PN} ${PVR})"
  462. [[ $? -ne 0 ]] && return
  463. local x
  464. if has vhosts ${IUSE} && ! use vhosts; then
  465. echo "${my_output}" | while read x; do
  466. if [[ -f "${x}"/.webapp ]]; then
  467. . "${x}"/.webapp
  468. if [[ -n "${WEB_HOSTNAME}" && -n "${WEB_INSTALLDIR}" ]]; then
  469. ${WEBAPP_CONFIG} -h ${WEB_HOSTNAME} -d ${WEB_INSTALLDIR} -C ${PN} ${PVR}
  470. fi
  471. else
  472. ewarn "Cannot find file ${x}/.webapp"
  473. fi
  474. done
  475. elif [[ "${my_output}" != "" ]]; then
  476. echo
  477. ewarn
  478. ewarn "Don't forget to use webapp-config to remove any copies of"
  479. ewarn "${PN}-${PVR} installed in"
  480. ewarn
  481. echo "${my_output}" | while read x; do
  482. if [[ -f "${x}"/.webapp ]]; then
  483. ewarn " ${x}"
  484. else
  485. ewarn "Cannot find file ${x}/.webapp"
  486. fi
  487. done
  488. ewarn
  489. echo
  490. fi
  491. }