# Copyright (c) 2017 The NetBSD Foundation, Inc. # All rights reserved. # # This code is derived from software contributed to The NetBSD Foundation # by Johnny C. Lam. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in the # documentation and/or other materials provided with the distribution. # # THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS # ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED # TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR # PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS # BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR # CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF # SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS # INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN # CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) # ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # # NAME # groups.subr -- group management for packages # # SYNOPSIS # task_groups [-s] add | remove # task_groups check-add | check-remove # # DESCRIPTION # The task_groups function supports four actions: "add", "remove", # "check-add", and "check-remove". # # The available options are as follows: # # -s Silent; don't write to standard output. # # The task_groups function reads standard input line by line and # looks for lines of the form: # # # GROUP: [:] # # Only the group is required; the groupid is optional. # # The "add" action creates the group with the given name if it is # missing, with the given group ID, if ${PKG_CREATE_USERGROUP} is # "yes". A reference count for the group will be error for the # package. # # The "remove" action removes a reference count for the group by # the package. This function shall not remove any group on the # system. # # The "check-add" action will check whether the groups exist with # the optional group IDs if they are given, or otherwise writes a # message to standard output noting the missing groups. # # The "check-remove" action will check whether the groups have been # removed, or otherwise writes a message to standard output noting # the groups still exists. # # RETURN VALUES # The "add" and "remove" actions return 0 if they are successful # for all groups, and >0 if an error occurs. # # The "check-add" and "check-remove" actions return >0 if they # write informative messages, and return 0 otherwise. # # ENVIRONMENT # The following variables are used if they are set: # # PKGNAME # The name of the package. # # PKG_CREATE_USERGROUP # If ${PKG_CREATE_USERGROUP} is a "truthy" value, then the # "add" and "remove" actions are allowed to create and # remove groups from the system. # # TASK_MSG # String prepended to all normal message written to # standard output. # __task_groups__="yes" __task_groups_init__="_task_groups_init" task_load cleanup task_load echo task_load quote task_load refcount task_load truthy task_load usergroup task_load usergroup_exists task_groups() { : ${PKGNAME:=${0##*/}} : ${PKG_CREATE_USERGROUP:=yes} : ${TASK_MSG:=""} local arg local echo="task_echo" local OPTIND=1 while getopts ":s" arg "$@"; do case $arg in s) echo=":" ;; *) return 127 ;; esac done shift $(( ${OPTIND} - 1 )) [ $# -gt 0 ] || return 127 local action="$1"; shift case $action in add|remove|check-add|check-remove) : "valid action" ;; *) return 0 ;; esac local create="yes" task_is_truthy "${PKG_CREATE_USERGROUP}" || create= local result line_result local save_IFS group gid msg result=0 local hash tag entry while read hash tag entry; do # Filter for "# GROUP:". case $hash/$tag in "#/GROUP:") : "use this line" ;; *) continue ;; esac save_IFS=$IFS; IFS=: set -o noglob; set -- $entry; set +o noglob group=$1; gid=$2 IFS=$save_IFS [ -n "$group" ] || continue if [ -n "$gid" ]; then msg="$group (gid = $gid)" else msg="$group" fi line_result=0 case $action in add) if task_refcount add groups "$group"; then task_group_exists "$group" "$gid" case $? in 0) # $group exists and has gid $gid $echo "${TASK_MSG}! group already exists: $group" ;; 1) # neither $group nor $gid exist if [ -z "$create" ]; then $echo "${TASK_MSG}! group creation skipped: $msg" elif task_addgroup "$group" "$gid"; then $echo "${TASK_MSG}> group created: $msg" # Keep track of groups added in case of error later. task_quote "$group" __task_groups_error__="$__task_groups_error__ $_quoted" else $echo "${TASK_MSG}! group not created: $msg" line_result=1 fi ;; 2) $echo "${TASK_MSG}! group conflict: $msg" result=1 break ;; *) $echo "${TASK_MSG}! group not created: $msg" line_result=1 ;; esac else # add refcount failed; skip to next line $echo "${TASK_MSG}! refcount add failure: groups $group" result=1 continue fi ;; remove) if task_refcount remove groups "$group"; then if task_refcount exists groups "$group"; then : "refcount is not zero" else # delete the reference count task_refcount delete groups "$group" fi else # remove refcount failed $echo "${TASK_MSG}! refcount remove failure: groups $group" line_result=1 fi ;; check-add) if task_group_exists "$group" "$gid"; then : "group already exists" else task_echo "!!! INFO: ${PKGNAME}: Create group: $msg" line_result=1 fi ;; check-remove) if task_group_exists "$group" "$gid"; then task_echo "!!! INFO: ${PKGNAME}: Remove group if unused: $group" line_result=1 fi ;; esac [ $line_result -eq 0 ] || result=1 done # Clear groups to remove in case of error if all groups added # successfully. # [ $result -gt 0 ] || __task_groups_error__= return $result } _task_groups_cleanup() { eval set -- $__task_groups_error__ local group for group; do if task_group_exists "$group"; then task_echo "!!! ERROR: ${PKGNAME}: Group created before error: $group" fi done __task_groups_error__= } _task_groups_init() { task_cleanup_add_hook _task_groups_cleanup } # Static variable for groups that should be removed if an error occurs. __task_groups_error__=