]> gitweb.michael.orlitzky.com - mjo-overlay.git/blob - eclass/sys-user.eclass
Add proof-of-concept sys-user eclass for installing users/groups.
[mjo-overlay.git] / eclass / sys-user.eclass
1 # Copyright 1999-2017 Gentoo Foundation
2 # Distributed under the terms of the GNU General Public License v2
3 # $Id$
4
5 # @ECLASS: sys-user.eclass
6 # @MAINTAINER:
7 # Michael Orlitzky <mjo@gentoo.org>
8 # @BLURB: handle installation and removal of system users.
9 # @DESCRIPTION:
10 # This eclass does most of the work for the sys-user/ packages that
11 # supply system user accounts.
12
13 inherit user
14
15 EXPORT_FUNCTIONS src_unpack src_prepare src_configure src_compile src_install src_test pkg_preinst pkg_postinst pkg_prerm
16
17 # This is hard-coded to the package name. If you want a different
18 # username, use a different package name. This is a nice way to prevent
19 # different people from claiming the same username.
20 SYS_USER_NAME="${PN}"
21
22 # @ECLASS-VARIABLE: SYS_USER_GROUPS
23 # @DESCRIPTION:
24 # etc.
25 : ${SYS_USER_GROUPS:=${PN}}
26
27 # @ECLASS-VARIABLE: SYS_USER_UID
28 # @DESCRIPTION:
29 # etc. (use -1 to get next available using user.eclass)
30 : ${SYS_USER_UID:=-1}
31
32 # In many cases, if the UID of a user changes, packages depending on it
33 # will want to rebuild. We always use SLOT=0, because you can't install
34 # the same user twice. Then we use the UID as our subslot so that
35 # subslot deps can be used to rebuild packages when our UID changes.
36 SLOT="0/${SYS_USER_UID}"
37
38 # @ECLASS-VARIABLE: SYS_USER_HOME
39 # @DESCRIPTION:
40 # etc. (use -1 to get user.eclass default)
41 : ${SYS_USER_HOME:=-1}
42
43 # @ECLASS-VARIABLE: SYS_USER_SHELL
44 # @DESCRIPTION:
45 # etc. (use -1 to get user.eclass default)
46 : ${SYS_USER_SHELL:=-1}
47
48 case ${EAPI} in
49 6) ;;
50 *)
51 die "${ECLASS} is not compatible with EAPI=${EAPI}"
52 esac
53
54 # Depend on any groups we might need.
55 for _group in ${SYS_USER_GROUPS}; do
56 DEPEND+=" sys-group/${_group} "
57 RDEPEND+=" sys-group/${_group}:= "
58 done
59 unset _group
60
61 S="${WORKDIR}"
62
63 sys-user_src_unpack() { :; }
64 sys-user_src_prepare() { :; }
65 sys-user_src_configure() { :; }
66 sys-user_src_compile() { :; }
67 sys-user_src_test() { :; }
68
69 sys-user_next_uid() {
70 local euid;
71 for (( euid = 101; euid <= 999; euid++ )); do
72 [[ -z $(egetent passwd "${euid}") ]] && break
73 done
74 if (( "${euid}" == 999 )); then
75 die "out of available UIDs!"
76 else
77 echo "${euid}"
78 fi
79 }
80
81 sys-user_src_prepare() {
82 eapply_user # whatever
83
84 if [[ -n $(egetent passwd "${SYS_USER_NAME}") ]]; then
85 # UPGRADE PATH: This user already exists, so if the eclass
86 # consumer doesn't care about some settings, we can reuse the
87 # pre-existing ones.
88 #
89 # This is also useful for sys-user package upgrades, becaused it
90 # prevents us from incrementing the UID pointlessly on a
91 # reinstall. Usually that will prevent rebuilds of depending
92 # packages, and is crucial to our ability to use subslot deps to
93 # cause rebuilds when the UID changes. We don't want the UID to
94 # change if the subslot doesn't change, and the subslot for "I
95 # don't care about the UID" will always be "-1", so the UID
96 # shouldn't generally change either when SYS_USER_UID=-1.
97 if (( "${SYS_USER_UID}" == -1 )); then
98 SYS_USER_UID=$(id --real --user "${SYS_USER_NAME}")
99 fi
100
101 if (( "${SYS_USER_HOME}" == -1 )); then
102 SYS_USER_HOME=$(egethome "${SYS_USER_NAME}")
103 fi
104
105 if (( "${SYS_USER_SHELL}" == -1 )); then
106 SYS_USER_SHELL=$(egetshell "${SYS_USER_NAME}")
107
108 if [[ ${SYS_USER_SHELL} == */false ]] || \
109 [[ ${SYS_USER_SHELL} == */nologin ]]; then
110 # WHYYYYY? enewuser complains if we try to set a default
111 # shell explicitly.
112 SYS_USER_SHELL="-1"
113 fi
114 fi
115 elif (( "${SYS_USER_UID}" == -1 )); then
116 # There is no pre-existing user (i.e. this isn't along the
117 # upgrade path), and the consumer says he doesn't care about the
118 # UID, so pick the next one.
119 SYS_USER_UID=$(sys-user_next_uid)
120 fi
121
122 # We do something with this in src_install.
123 touch "${T}/${SYS_USER_UID}" || die
124 }
125
126 sys-user_src_install() {
127 # Install a placeholder file to /var/lib/sys-user/$uid. This will
128 # cause collisions if two packages try to install users with the
129 # same UID. The same problem potentially exists with the username,
130 # but as long as SYS_USER_NAME is hard-coded to $PN, that shouldn't
131 # be possible.
132 #
133 # Beware, this only works if SYS_USER_UID is guaranteed to have a
134 # real UID and not, for example, -1. That is taken care of in
135 # src_prepare() for now.
136 insinto "/var/lib/sys-user"
137 doins "${T}/${SYS_USER_UID}"
138
139 # TODO: do we want to try to create the user's home directory within
140 # the package manager so that it can be cleaned up later? The
141 # obvious problem with that plan is that we need to be careful not
142 # to give the new user ownership of e.g. /dev/null.
143 }
144
145 sys-user_pkg_preinst() {
146 if [[ -z $(egetent passwd "${SYS_USER_NAME}") ]]; then
147 # The user does not already exist. This is the nice and easy
148 # case because no matter how we got here, we want to go ahead
149 # and create the (new) user.
150 enewuser "${SYS_USER_NAME}" \
151 "${SYS_USER_UID}" \
152 "${SYS_USER_SHELL}" \
153 "${SYS_USER_HOME}" \
154 "${SYS_USER_GROUPS}" \
155 || die "failed to add user ${SYS_USER_NAME}"
156 elif [[ -n "${REPLACING_VERSIONS}" ]]; then
157 # This is an upgrade from an existing sys-user package. This
158 # case is a little bit weird. If we do it in preinst(), then it
159 # will happen before the "old" user is removed in
160 # pkg_prerm(). Except the old user and the new user are the
161 # same, so if we overwrite the existing user here, then prerm
162 # for the version that created it will clobber our new entry.
163 #
164 # We also can't just LEAVE the old user there, because then no
165 # upgrade happens.
166 #
167 # Uh, let's do this case in pkg_postinst so that it happens
168 # after the old version's prerm.
169 :
170 else
171 # UPGRADE PATH: Ok, the user exists but this isn't an upgrade of
172 # a sys-user package. This is the upgrade path from the old
173 # style of user/group management to the new style. What can we
174 # do? We could make it policy that old users must be compatible
175 # with the new ones, but that entails hard-coding UIDs that
176 # don't need to be hard-coded.
177 #
178 # Instead lets see if the new user is compatible with the old
179 # (it usually will be), and then only bail out if there's a real
180 # problem.
181 local oldhome=$(egethome "${SYS_USER_NAME}")
182 local oldshell=$(egetshell "${SYS_USER_NAME}")
183 local olduid=$(id --real --user "${SYS_USER_NAME}")
184
185 if [[ "${oldhome}" -ne "${SYS_USER_HOME}" ]]; then
186 die "home directory conflict for new user ${SYS_USER_HOME}"
187 fi
188
189 if [[ "${oldhshell}" -ne "${SYS_USER_SHELL}" ]]; then
190 die "shell conflict for new user ${SYS_USER_HOME}"
191 fi
192
193 if [[ "${olduid}" -ne "${SYS_USER_UID}" ]]; then
194 die "UID conflict for new user ${SYS_USER_NAME}"
195 fi
196
197 # The user already exists, so all we have left to do is to try
198 # to append SYS_USER_GROUPS to the existing groups. The "usermod"
199 # tool expects a comma-separated list, so change our spaces to
200 # commas. This does succeed if you append duplicates.
201 usermod --append --groups "${SYS_USER_GROUPS// /,}" \
202 || die "failed to append groups to existing user ${SYS_USER_NAME}"
203 fi
204 }
205
206 sys-user_pkg_postinst() {
207 if [[ -n "${REPLACING_VERSIONS}" ]]; then
208 # This is an upgrade from a previous version of a sys-user
209 # package. This case has to be handled carefully to make sure
210 # that the pkg_prerm() of the old version doesn't remove the user
211 # that this new version is going to add. At this point, in our
212 # pkg_postinst(), the old version's pkg_prerm() phase should have
213 # already happened.
214 if [[ -n $(egetent passwd "${SYS_USER_NAME}") ]]; then
215 die "User ${SYS_USER_NAME} already exists during an upgrade."
216 else
217 enewuser "${SYS_USER_NAME}" \
218 "${SYS_USER_UID}" \
219 "${SYS_USER_SHELL}" \
220 "${SYS_USER_HOME}" \
221 "${SYS_USER_GROUPS}" \
222 || die "failed to add user ${SYS_USER_NAME}"
223 fi
224 fi
225 }
226
227 sys-user_pkg_prerm() {
228 if [[ -z $(egetent passwd "${SYS_USER_NAME}") ]]; then
229 # We have successfully done nothing.
230 ewarn "Tried to remove nonexistent user ${SYS_USER_NAME}."
231 else
232 userdel "${SYS_USER_NAME}" || \
233 die "failed to remove user ${SYS_USER_NAME}"
234 einfo "Removed user ${SYS_USER_NAME} from the system."
235 fi
236 }