user.eclass 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462
  1. # Copyright 1999-2014 Gentoo Foundation
  2. # Distributed under the terms of the GNU General Public License v2
  3. # @ECLASS: user.eclass
  4. # @MAINTAINER:
  5. # base-system@gentoo.org (Linux)
  6. # @BLURB: user management in ebuilds
  7. # @DESCRIPTION:
  8. # The user eclass contains a suite of functions that allow ebuilds
  9. # to quickly make sure users in the installed system are sane.
  10. if [[ -z ${_USER_ECLASS} ]]; then
  11. _USER_ECLASS=1
  12. # @FUNCTION: _assert_pkg_ebuild_phase
  13. # @INTERNAL
  14. # @USAGE: <calling func name>
  15. _assert_pkg_ebuild_phase() {
  16. case ${EBUILD_PHASE} in
  17. setup|preinst|postinst) ;;
  18. *)
  19. eerror "'$1()' called from '${EBUILD_PHASE}' phase which is not OK:"
  20. eerror "You may only call from pkg_{setup,preinst,postinst} functions."
  21. eerror "Package fails at QA and at life. Please file a bug."
  22. die "Bad package! $1 is only for use in some pkg_* functions!"
  23. esac
  24. }
  25. # @FUNCTION: egetent
  26. # @USAGE: <database> <key>
  27. # @DESCRIPTION:
  28. # Small wrapper for getent (Linux), nidump (< Mac OS X 10.5),
  29. # dscl (Mac OS X 10.5), and pw (FreeBSD) used in enewuser()/enewgroup().
  30. #
  31. # Supported databases: group passwd
  32. egetent() {
  33. local db=$1 key=$2
  34. [[ $# -ge 3 ]] && die "usage: egetent <database> <key>"
  35. case ${db} in
  36. passwd|group) ;;
  37. *) die "sorry, database '${db}' not yet supported; file a bug" ;;
  38. esac
  39. case ${CHOST} in
  40. *-darwin[678])
  41. case ${key} in
  42. *[!0-9]*) # Non numeric
  43. nidump ${db} . | awk -F: "(\$1 ~ /^${key}\$/) {print;exit;}"
  44. ;;
  45. *) # Numeric
  46. nidump ${db} . | awk -F: "(\$3 == ${key}) {print;exit;}"
  47. ;;
  48. esac
  49. ;;
  50. *-darwin*)
  51. local mykey
  52. case ${db} in
  53. passwd) db="Users" mykey="UniqueID" ;;
  54. group) db="Groups" mykey="PrimaryGroupID" ;;
  55. esac
  56. case ${key} in
  57. *[!0-9]*) # Non numeric
  58. dscl . -read /${db}/${key} 2>/dev/null |grep RecordName
  59. ;;
  60. *) # Numeric
  61. dscl . -search /${db} ${mykey} ${key} 2>/dev/null
  62. ;;
  63. esac
  64. ;;
  65. *-freebsd*|*-dragonfly*)
  66. case ${db} in
  67. passwd) db="user" ;;
  68. *) ;;
  69. esac
  70. # lookup by uid/gid
  71. local opts
  72. if [[ ${key} == [[:digit:]]* ]] ; then
  73. [[ ${db} == "user" ]] && opts="-u" || opts="-g"
  74. fi
  75. pw show ${db} ${opts} "${key}" -q
  76. ;;
  77. *-netbsd*|*-openbsd*)
  78. grep "${key}:\*:" /etc/${db}
  79. ;;
  80. *)
  81. # ignore output if nscd doesn't exist, or we're not running as root
  82. nscd -i "${db}" 2>/dev/null
  83. getent "${db}" "${key}"
  84. ;;
  85. esac
  86. }
  87. # @FUNCTION: enewuser
  88. # @USAGE: <user> [uid] [shell] [homedir] [groups]
  89. # @DESCRIPTION:
  90. # Same as enewgroup, you are not required to understand how to properly add
  91. # a user to the system. The only required parameter is the username.
  92. # Default uid is (pass -1 for this) next available, default shell is
  93. # /bin/false, default homedir is /dev/null, and there are no default groups.
  94. enewuser() {
  95. _assert_pkg_ebuild_phase ${FUNCNAME}
  96. # get the username
  97. local euser=$1; shift
  98. if [[ -z ${euser} ]] ; then
  99. eerror "No username specified !"
  100. die "Cannot call enewuser without a username"
  101. fi
  102. # lets see if the username already exists
  103. if [[ -n $(egetent passwd "${euser}") ]] ; then
  104. return 0
  105. fi
  106. einfo "Adding user '${euser}' to your system ..."
  107. # options to pass to useradd
  108. local opts=()
  109. # handle uid
  110. local euid=$1; shift
  111. if [[ -n ${euid} && ${euid} != -1 ]] ; then
  112. if [[ ${euid} -gt 0 ]] ; then
  113. if [[ -n $(egetent passwd ${euid}) ]] ; then
  114. euid="next"
  115. fi
  116. else
  117. eerror "Userid given but is not greater than 0 !"
  118. die "${euid} is not a valid UID"
  119. fi
  120. else
  121. euid="next"
  122. fi
  123. if [[ ${euid} == "next" ]] ; then
  124. for ((euid = 101; euid <= 999; euid++)); do
  125. [[ -z $(egetent passwd ${euid}) ]] && break
  126. done
  127. fi
  128. opts+=( -u ${euid} )
  129. einfo " - Userid: ${euid}"
  130. # handle shell
  131. local eshell=$1; shift
  132. if [[ ! -z ${eshell} ]] && [[ ${eshell} != "-1" ]] ; then
  133. if [[ ! -e ${ROOT}${eshell} ]] ; then
  134. eerror "A shell was specified but it does not exist !"
  135. die "${eshell} does not exist in ${ROOT}"
  136. fi
  137. if [[ ${eshell} == */false || ${eshell} == */nologin ]] ; then
  138. eerror "Do not specify ${eshell} yourself, use -1"
  139. die "Pass '-1' as the shell parameter"
  140. fi
  141. else
  142. for eshell in /sbin/nologin /usr/sbin/nologin /bin/false /usr/bin/false /dev/null ; do
  143. [[ -x ${ROOT}${eshell} ]] && break
  144. done
  145. if [[ ${eshell} == "/dev/null" ]] ; then
  146. eerror "Unable to identify the shell to use, proceeding with userland default."
  147. case ${USERLAND} in
  148. GNU) eshell="/bin/false" ;;
  149. BSD) eshell="/sbin/nologin" ;;
  150. Darwin) eshell="/usr/sbin/nologin" ;;
  151. *) die "Unable to identify the default shell for userland ${USERLAND}"
  152. esac
  153. fi
  154. fi
  155. einfo " - Shell: ${eshell}"
  156. opts+=( -s "${eshell}" )
  157. # handle homedir
  158. local ehome=$1; shift
  159. if [[ -z ${ehome} ]] || [[ ${ehome} == "-1" ]] ; then
  160. ehome="/dev/null"
  161. fi
  162. einfo " - Home: ${ehome}"
  163. opts+=( -d "${ehome}" )
  164. # handle groups
  165. local egroups=$1; shift
  166. local g egroups_arr
  167. IFS="," read -r -a egroups_arr <<<"${egroups}"
  168. if [[ ${#egroups_arr[@]} -gt 0 ]] ; then
  169. local defgroup exgroups
  170. for g in "${egroups_arr[@]}" ; do
  171. if [[ -z $(egetent group "${g}") ]] ; then
  172. eerror "You must add group ${g} to the system first"
  173. die "${g} is not a valid GID"
  174. fi
  175. if [[ -z ${defgroup} ]] ; then
  176. defgroup=${g}
  177. else
  178. exgroups+=",${g}"
  179. fi
  180. done
  181. opts+=( -g "${defgroup}" )
  182. if [[ ! -z ${exgroups} ]] ; then
  183. opts+=( -G "${exgroups:1}" )
  184. fi
  185. fi
  186. einfo " - Groups: ${egroups:-(none)}"
  187. # handle extra args
  188. if [[ $# -gt 0 ]] ; then
  189. die "extra arguments no longer supported; please file a bug"
  190. else
  191. local comment="added by portage for ${PN}"
  192. opts+=( -c "${comment}" )
  193. einfo " - GECOS: ${comment}"
  194. fi
  195. # add the user
  196. case ${CHOST} in
  197. *-darwin*)
  198. ### Make the user
  199. dscl . create "/users/${euser}" uid ${euid}
  200. dscl . create "/users/${euser}" shell "${eshell}"
  201. dscl . create "/users/${euser}" home "${ehome}"
  202. dscl . create "/users/${euser}" realname "added by portage for ${PN}"
  203. ### Add the user to the groups specified
  204. for g in "${egroups_arr[@]}" ; do
  205. dscl . merge "/groups/${g}" users "${euser}"
  206. done
  207. ;;
  208. *-freebsd*|*-dragonfly*)
  209. pw useradd "${euser}" "${opts[@]}" || die
  210. ;;
  211. *-netbsd*)
  212. useradd "${opts[@]}" "${euser}" || die
  213. ;;
  214. *-openbsd*)
  215. # all ops the same, except the -g vs -g/-G ...
  216. useradd -u ${euid} -s "${eshell}" \
  217. -d "${ehome}" -g "${egroups}" "${euser}" || die
  218. ;;
  219. *)
  220. useradd -r "${opts[@]}" "${euser}" || die
  221. ;;
  222. esac
  223. if [[ ! -e ${ROOT}/${ehome} ]] ; then
  224. einfo " - Creating ${ehome} in ${ROOT}"
  225. mkdir -p "${ROOT}/${ehome}"
  226. chown "${euser}" "${ROOT}/${ehome}"
  227. chmod 755 "${ROOT}/${ehome}"
  228. fi
  229. }
  230. # @FUNCTION: enewgroup
  231. # @USAGE: <group> [gid]
  232. # @DESCRIPTION:
  233. # This function does not require you to understand how to properly add a
  234. # group to the system. Just give it a group name to add and enewgroup will
  235. # do the rest. You may specify the gid for the group or allow the group to
  236. # allocate the next available one.
  237. enewgroup() {
  238. _assert_pkg_ebuild_phase ${FUNCNAME}
  239. # get the group
  240. local egroup=$1; shift
  241. if [[ -z ${egroup} ]] ; then
  242. eerror "No group specified !"
  243. die "Cannot call enewgroup without a group"
  244. fi
  245. # see if group already exists
  246. if [[ -n $(egetent group "${egroup}") ]] ; then
  247. return 0
  248. fi
  249. einfo "Adding group '${egroup}' to your system ..."
  250. # handle gid
  251. local egid=$1; shift
  252. if [[ ! -z ${egid} ]] ; then
  253. if [[ ${egid} -gt 0 ]] ; then
  254. if [[ -n $(egetent group ${egid}) ]] ; then
  255. egid="next available; requested gid taken"
  256. fi
  257. else
  258. eerror "Groupid given but is not greater than 0 !"
  259. die "${egid} is not a valid GID"
  260. fi
  261. else
  262. egid="next available"
  263. fi
  264. einfo " - Groupid: ${egid}"
  265. # handle extra
  266. if [[ $# -gt 0 ]] ; then
  267. die "extra arguments no longer supported; please file a bug"
  268. fi
  269. # Some targets need to find the next available GID manually
  270. _enewgroup_next_gid() {
  271. if [[ ${egid} == *[!0-9]* ]] ; then
  272. # Non numeric
  273. for ((egid = 101; egid <= 999; egid++)) ; do
  274. [[ -z $(egetent group ${egid}) ]] && break
  275. done
  276. fi
  277. }
  278. # add the group
  279. case ${CHOST} in
  280. *-darwin*)
  281. _enewgroup_next_gid
  282. dscl . create "/groups/${egroup}" gid ${egid}
  283. dscl . create "/groups/${egroup}" passwd '*'
  284. ;;
  285. *-freebsd*|*-dragonfly*)
  286. _enewgroup_next_gid
  287. pw groupadd "${egroup}" -g ${egid} || die
  288. ;;
  289. *-netbsd*)
  290. _enewgroup_next_gid
  291. groupadd -g ${egid} "${egroup}" || die
  292. ;;
  293. *)
  294. local opts
  295. if [[ ${egid} == *[!0-9]* ]] ; then
  296. # Non numeric; let groupadd figure out a GID for us
  297. opts=""
  298. else
  299. opts="-g ${egid}"
  300. fi
  301. # We specify -r so that we get a GID in the system range from login.defs
  302. groupadd -r ${opts} "${egroup}" || die
  303. ;;
  304. esac
  305. }
  306. # @FUNCTION: egethome
  307. # @USAGE: <user>
  308. # @DESCRIPTION:
  309. # Gets the home directory for the specified user.
  310. egethome() {
  311. local pos
  312. [[ $# -eq 1 ]] || die "usage: egethome <user>"
  313. case ${CHOST} in
  314. *-darwin*|*-freebsd*|*-dragonfly*)
  315. pos=9
  316. ;;
  317. *) # Linux, NetBSD, OpenBSD, etc...
  318. pos=6
  319. ;;
  320. esac
  321. egetent passwd "$1" | cut -d: -f${pos}
  322. }
  323. # @FUNCTION: egetshell
  324. # @USAGE: <user>
  325. # @DESCRIPTION:
  326. # Gets the shell for the specified user.
  327. egetshell() {
  328. local pos
  329. [[ $# -eq 1 ]] || die "usage: egetshell <user>"
  330. case ${CHOST} in
  331. *-darwin*|*-freebsd*|*-dragonfly*)
  332. pos=10
  333. ;;
  334. *) # Linux, NetBSD, OpenBSD, etc...
  335. pos=7
  336. ;;
  337. esac
  338. egetent passwd "$1" | cut -d: -f${pos}
  339. }
  340. # @FUNCTION: esethome
  341. # @USAGE: <user> <homedir>
  342. # @DESCRIPTION:
  343. # Update the home directory in a platform-agnostic way.
  344. # Required parameters is the username and the new home directory.
  345. # Specify -1 if you want to set home to the enewuser default
  346. # of /dev/null.
  347. # If the new home directory does not exist, it is created.
  348. # Any previously existing home directory is NOT moved.
  349. esethome() {
  350. _assert_pkg_ebuild_phase ${FUNCNAME}
  351. # get the username
  352. local euser=$1; shift
  353. if [[ -z ${euser} ]] ; then
  354. eerror "No username specified !"
  355. die "Cannot call esethome without a username"
  356. fi
  357. # lets see if the username already exists
  358. if [[ -z $(egetent passwd "${euser}") ]] ; then
  359. ewarn "User does not exist, cannot set home dir -- skipping."
  360. return 1
  361. fi
  362. # handle homedir
  363. local ehome=$1; shift
  364. if [[ -z ${ehome} ]] ; then
  365. eerror "No home directory specified !"
  366. die "Cannot call esethome without a home directory or '-1'"
  367. fi
  368. if [[ ${ehome} == "-1" ]] ; then
  369. ehome="/dev/null"
  370. fi
  371. # exit with no message if home dir is up to date
  372. if [[ $(egethome "${euser}") == ${ehome} ]]; then
  373. return 0
  374. fi
  375. einfo "Updating home for user '${euser}' ..."
  376. einfo " - Home: ${ehome}"
  377. # ensure home directory exists, otherwise update will fail
  378. if [[ ! -e ${ROOT}/${ehome} ]] ; then
  379. einfo " - Creating ${ehome} in ${ROOT}"
  380. mkdir -p "${ROOT}/${ehome}"
  381. chown "${euser}" "${ROOT}/${ehome}"
  382. chmod 755 "${ROOT}/${ehome}"
  383. fi
  384. # update the home directory
  385. case ${CHOST} in
  386. *-darwin*)
  387. dscl . change "/users/${euser}" home "${ehome}"
  388. ;;
  389. *-freebsd*|*-dragonfly*)
  390. pw usermod "${euser}" -d "${ehome}" && return 0
  391. [[ $? == 8 ]] && eerror "${euser} is in use, cannot update home"
  392. eerror "There was an error when attempting to update the home directory for ${euser}"
  393. eerror "Please update it manually on your system:"
  394. eerror "\t pw usermod \"${euser}\" -d \"${ehome}\""
  395. ;;
  396. *)
  397. usermod -d "${ehome}" "${euser}" && return 0
  398. [[ $? == 8 ]] && eerror "${euser} is in use, cannot update home"
  399. eerror "There was an error when attempting to update the home directory for ${euser}"
  400. eerror "Please update it manually on your system (as root):"
  401. eerror "\t usermod -d \"${ehome}\" \"${euser}\""
  402. ;;
  403. esac
  404. }
  405. fi