--- eclass/sys-group.eclass | 105 ++++++++++++++++++++ eclass/sys-user.eclass | 206 ++++++++++++++++++++++++++++++++++++++++ 2 files changed, 311 insertions(+) create mode 100644 eclass/sys-group.eclass create mode 100644 eclass/sys-user.eclass
diff --git a/eclass/sys-group.eclass b/eclass/sys-group.eclass new file mode 100644 index 000000000000..3960db16b5d6 --- /dev/null +++ b/eclass/sys-group.eclass @@ -0,0 +1,105 @@ +# Copyright 2019 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +# @ECLASS: sys-group.eclass +# @MAINTAINER: +# Michał Górny <mgo...@gentoo.org> +# @AUTHOR: +# Michael Orlitzky <m...@gentoo.org> +# Michał Górny <mgo...@gentoo.org> +# @BLURB: Eclass used to create and maintain a single group entry +# @DESCRIPTION: +# This eclass represents and creates a single group entry. The name +# of the group is derived from ${PN}, while (preferred) GID needs to +# be specified via SYS_GROUP_ID. Packages (and users) needing the group +# in question should depend on the package providing it. +# +# Example: +# If your package needs group 'foo', you create 'group/foo' package +# and add an ebuild with the following contents: +# +# @CODE +# EAPI=7 +# inherit sys-group +# SYS_GROUP_ID=200 +# @CODE +# +# Then you add appropriate dependency to your package. The dependency +# type(s) should be: +# - DEPEND (+ RDEPEND) if the group is already needed at build time, +# - RDEPEND if it is needed at install time (e.g. you 'fowners' files +# in pkg_preinst), +# - PDEPEND if it is only needed at runtime. + + +if [[ -z ${_SYS_GROUP_ECLASS} ]]; then +_SYS_GROUP_ECLASS=1 + +case ${EAPI:-0} in + 7) ;; + *) die "EAPI=${EAPI} not supported";; +esac + +inherit user + + +# << Eclass variables >> + +# @ECLASS-VARIABLE: SYS_GROUP_ID +# @REQUIRED +# @DESCRIPTION: +# Preferred GID for the new group. This variable is obligatory, and its +# value must be unique across all group packages. + +# @ECLASS-VARIABLE: SYS_GROUP_ENFORCE_ID +# @DESCRIPTION: +# If set to a non-null value, the eclass will require the group to have +# specified GID. If the group already exists with another GID, or +# the GID is taken by another group, the install will fail. +: ${SYS_GROUP_ENFORCE_ID:=} + + +# << Boilerplate ebuild variables >> +: ${DESCRIPTION:="System group: ${PN}"} +: ${HOMEPAGE:=https://www.gentoo.org/} +: ${SLOT:=0} +: ${KEYWORDS:=alpha amd64 arm arm64 hppa ia64 m68k ~mips ppc ppc64 ~riscv s390 sh sparc x86 ~ppc-aix ~x64-cygwin ~amd64-fbsd ~x86-fbsd ~amd64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris} +S=${WORKDIR} + + +# << Phase functions >> +EXPORT_FUNCTIONS pkg_pretend pkg_preinst + +# @FUNCTION: sys-group_pkg_pretend +# @DESCRIPTION: +# Performs sanity checks for correct eclass usage, and early-checks +# whether requested GID can be enforced. +sys-group_pkg_pretend() { + debug-print-function ${FUNCNAME} "${@}" + + # verify SYS_GROUP_ID + [[ -n ${SYS_GROUP_ID} ]] || die "Ebuild error: SYS_GROUP_ID must be set!" + [[ ${SYS_GROUP_ID} -ge 0 ]] || die "Ebuild errors: SYS_GROUP_ID=${SYS_GROUP_ID} invalid!" + + # check for SYS_GROUP_ID collisions early + if [[ -n ${SYS_GROUP_ENFORCE_ID} ]]; then + local grp=$(egetent group "${SYS_GROUP_ID}") + if [[ -n ${grp} ]]; then + eerror "The required GID is already taken by another group." + eerror " GID: ${SYS_GROUP_ID} (needed for ${PN})" + eerror " current group: ${grp}" + die "GID ${SYS_GROUP_ID} taken already" + fi + fi +} + +# @FUNCTION: sys-group_pkg_preinst +# @DESCRIPTION: +# Creates the group if it does not exist yet. +sys-group_pkg_preinst() { + debug-print-function ${FUNCNAME} "${@}" + + enewgroup -F "${PN}" "${SYS_GROUP_ID}" +} + +fi diff --git a/eclass/sys-user.eclass b/eclass/sys-user.eclass new file mode 100644 index 000000000000..de59af99a843 --- /dev/null +++ b/eclass/sys-user.eclass @@ -0,0 +1,206 @@ +# Copyright 2019 Gentoo Authors +# Distributed under the terms of the GNU General Public License v2 + +# @ECLASS: sys-user.eclass +# @MAINTAINER: +# Michał Górny <mgo...@gentoo.org> +# @AUTHOR: +# Michael Orlitzky <m...@gentoo.org> +# Michał Górny <mgo...@gentoo.org> +# @BLURB: Eclass used to create and maintain a single user entry +# @DESCRIPTION: +# This eclass represents and creates a single user entry. The name +# of the user is derived from ${PN}, while (preferred) UID needs to +# be specified via SYS_USER_ID. Additional variables are provided +# to override the default home directory, shell and add group +# membership. Packages needing the user in question should depend +# on the package providing it. +# +# Example: +# If your package needs user 'foo' belonging to same-named group, you +# create 'user/foo' package and add an ebuild with the following +# contents: +# +# @CODE +# EAPI=7 +# inherit sys-user +# SYS_USER_ID=200 +# SYS_USER_GROUPS=( foo ) +# sys-user_add_deps +# @CODE +# +# Then you add appropriate dependency to your package. The dependency +# type(s) should be: +# - DEPEND (+ RDEPEND) if the user is already needed at build time, +# - RDEPEND if it is needed at install time (e.g. you 'fowners' files +# in pkg_preinst), +# - PDEPEND if it is only needed at runtime. + +if [[ -z ${_SYS_USER_ECLASS} ]]; then +_SYS_USER_ECLASS=1 + +case ${EAPI:-0} in + 7) ;; + *) die "EAPI=${EAPI} not supported";; +esac + +inherit user + + +# << Eclass variables >> + +# @ECLASS-VARIABLE: SYS_USER_ID +# @REQUIRED +# @DESCRIPTION: +# Preferred UID for the new user. This variable is obligatory, and its +# value must be unique across all user packages. + +# @ECLASS-VARIABLE: SYS_USER_ENFORCE_ID +# @DESCRIPTION: +# If set to a non-null value, the eclass will require the user to have +# specified UID. If the user already exists with another UID, or +# the UID is taken by another user, the install will fail. +: ${SYS_USER_ENFORCE_ID:=} + +# @ECLASS-VARIABLE: SYS_USER_SHELL +# @DESCRIPTION: +# The shell to use for the new user. If not specified, a 'nologin' +# variant for the system is used. This affects only new user accounts. +: ${SYS_USER_SHELL:=-1} + +# @ECLASS-VARIABLE: SYS_USER_HOME +# @DESCRIPTION: +# The home directory for the new user. If not specified, /dev/null +# is used. This affects only new user accounts. The directory will +# be created with appropriate permissions if it does not exist. +: ${SYS_USER_HOME:=/dev/null} + +# @ECLASS-VARIABLE: SYS_USER_GROUPS +# @DEFAULT_UNSET +# @DESCRIPTION: +# List of groups the user should belong to. This must be a bash +# array. If not specified, the user is not added to any groups. +# This affects only new user accounts. +# +# If SYS_USER_GROUPS is specified, the ebuild needs to call +# sys-user_add_deps in global scope to add appropriate dependencies. + + +# << Boilerplate ebuild variables >> +: ${DESCRIPTION:="System user: ${PN}"} +: ${HOMEPAGE:=https://www.gentoo.org/} +: ${SLOT:=0} +: ${KEYWORDS:=alpha amd64 arm arm64 hppa ia64 m68k ~mips ppc ppc64 ~riscv s390 sh sparc x86 ~ppc-aix ~x64-cygwin ~amd64-fbsd ~x86-fbsd ~amd64-linux ~x86-linux ~ppc-macos ~x64-macos ~x86-macos ~m68k-mint ~sparc-solaris ~sparc64-solaris ~x64-solaris ~x86-solaris} +S=${WORKDIR} + + +# << API functions >> + +# @FUNCTION: sys-user_add_deps +# @DESCRIPTION: +# Generate appropriate RDEPEND from SYS_USER_GROUPS. This must be +# called if SYS_USER_GROUPS are set. +sys-user_add_deps() { + debug-print-function ${FUNCNAME} "${@}" + + # SYS_USER_GROUPS sanity check + if ! declare -p SYS_USER_GROUPS &>/dev/null; then + return + elif [[ $(declare -p SYS_USER_GROUPS) != "declare -a"* ]]; then + die 'SYS_USER_GROUPS must be an array.' + fi + + local g + for g in "${SYS_USER_GROUPS[@]}"; do + RDEPEND+=" group/${g}" + done + + _SYS_USER_ADD_DEPS_CALLED=1 +} + + +# << Phase functions >> +EXPORT_FUNCTIONS pkg_pretend src_install pkg_preinst pkg_prerm + +# @FUNCTION: sys-user_pkg_pretend +# @DESCRIPTION: +# Performs sanity checks for correct eclass usage, and early-checks +# whether requested UID can be enforced. +sys-user_pkg_pretend() { + debug-print-function ${FUNCNAME} "${@}" + + # verify that sys-user_add_deps() has been called + # (it verifies SYS_USER_GROUPS itself) + if [[ -z ${_SYS_USER_ADD_DEPS_CALLED} ]]; then + if declare -p SYS_USER_GROUPS &>/dev/null; then + die "Ebuild error: sys-user_add_deps must have been called in global scope!" + fi + fi + + # verify SYS_USER_ID + [[ -n ${SYS_USER_ID} ]] || die "Ebuild error: SYS_USER_ID must be set!" + [[ ${SYS_USER_ID} -ge 0 ]] || die "Ebuild errors: SYS_USER_ID=${SYS_USER_ID} invalid!" + + # check for SYS_USER_ID collisions early + if [[ -n ${SYS_USER_ENFORCE_ID} ]]; then + local pwd=$(egetent passwd "${SYS_USER_ID}") + if [[ -n ${pwd} ]]; then + eerror "The required UID is already taken by another user." + eerror " UID: ${SYS_USER_ID} (needed for ${PN})" + eerror " current user: ${pwd}" + die "UID ${SYS_USER_ID} taken already" + fi + fi +} + +# @FUNCTION: sys-user_src_install +# @DESCRIPTION: +# Installs a keep-file into the user's home directory to ensure it is +# owned by the package. +sys-user_src_install() { + debug-print-function ${FUNCNAME} "${@}" + + if [[ ${SYS_USER_HOME} != /dev/null ]]; then + # note: we can't set permissions here since the user isn't + # created yet + keepdir "${SYS_USER_HOME}" + fi +} + +# @FUNCTION: sys-user_pkg_preinst +# @DESCRIPTION: +# Creates the user if it does not exist yet. Sets permissions +# of the home directory in install image. +sys-user_pkg_preinst() { + debug-print-function ${FUNCNAME} "${@}" + + local groups=${SYS_USER_GROUPS[*]} + enewuser -F -M "${PN}" "${SYS_USER_ID}" "${SYS_USER_SHELL}" \ + "${SYS_USER_HOME}" "${groups// /,}" + + if [[ ${SYS_USER_HOME} != /dev/null ]]; then + # set ownership of homedir to user:primary-group + fowners "${SYS_USER_ID}" "${SYS_USER_HOME}" + if [[ -n ${SYS_USER_GROUPS[0]} ]]; then + fowners ":${SYS_USER_GROUPS[0]}" "${SYS_USER_HOME}" + fi + # TODO: should we fperms it too? To 0700 or is 0755 better + # for service users? + fi +} + +# @FUNCTION: sys-user_pkg_prerm +# @DESCRIPTION: +# Ensures that the user account is locked out when it is removed. +sys-user_pkg_prerm() { + debug-print-function ${FUNCNAME} "${@}" + + if [[ -z ${REPLACED_BY_VERSION} ]]; then + : + # TODO: what should we do here, exactly? we shouldn't touch + # shell, and it should be nologin anyway. we could reset + # the password but it should not be set anyway. + fi +} + +fi -- 2.22.0.rc1