gnu: Add ghc-aeson-pretty.
[jackhill/guix/guix.git] / gnu / system / shadow.scm
1 ;;; GNU Guix --- Functional package management for GNU
2 ;;; Copyright © 2013, 2014, 2015, 2016 Ludovic Courtès <ludo@gnu.org>
3 ;;; Copyright © 2016 Alex Griffin <a@ajgrf.com>
4 ;;;
5 ;;; This file is part of GNU Guix.
6 ;;;
7 ;;; GNU Guix is free software; you can redistribute it and/or modify it
8 ;;; under the terms of the GNU General Public License as published by
9 ;;; the Free Software Foundation; either version 3 of the License, or (at
10 ;;; your option) any later version.
11 ;;;
12 ;;; GNU Guix is distributed in the hope that it will be useful, but
13 ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
14 ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 ;;; GNU General Public License for more details.
16 ;;;
17 ;;; You should have received a copy of the GNU General Public License
18 ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
19
20 (define-module (gnu system shadow)
21 #:use-module (guix records)
22 #:use-module (guix gexp)
23 #:use-module (guix store)
24 #:use-module (guix sets)
25 #:use-module (guix ui)
26 #:use-module (gnu services)
27 #:use-module ((gnu system file-systems)
28 #:select (%tty-gid))
29 #:use-module ((gnu packages admin)
30 #:select (shadow))
31 #:use-module (gnu packages bash)
32 #:use-module (gnu packages guile-wm)
33 #:use-module (srfi srfi-1)
34 #:use-module (srfi srfi-26)
35 #:use-module (srfi srfi-34)
36 #:use-module (srfi srfi-35)
37 #:export (user-account
38 user-account?
39 user-account-name
40 user-account-password
41 user-account-uid
42 user-account-group
43 user-account-supplementary-groups
44 user-account-comment
45 user-account-home-directory
46 user-account-shell
47 user-account-system?
48
49 user-group
50 user-group?
51 user-group-name
52 user-group-password
53 user-group-id
54 user-group-system?
55
56 default-skeletons
57 skeleton-directory
58 %base-groups
59 %base-user-accounts
60
61 account-service-type
62 account-service))
63
64 ;;; Commentary:
65 ;;;
66 ;;; Utilities for configuring the Shadow tool suite ('login', 'passwd', etc.)
67 ;;;
68 ;;; Code:
69
70 (define-record-type* <user-account>
71 user-account make-user-account
72 user-account?
73 (name user-account-name)
74 (password user-account-password (default #f))
75 (uid user-account-uid (default #f))
76 (group user-account-group) ; number | string
77 (supplementary-groups user-account-supplementary-groups
78 (default '())) ; list of strings
79 (comment user-account-comment (default ""))
80 (home-directory user-account-home-directory)
81 (shell user-account-shell ; gexp
82 (default #~(string-append #$bash "/bin/bash")))
83 (system? user-account-system? ; Boolean
84 (default #f)))
85
86 (define-record-type* <user-group>
87 user-group make-user-group
88 user-group?
89 (name user-group-name)
90 (password user-group-password (default #f))
91 (id user-group-id (default #f))
92 (system? user-group-system? ; Boolean
93 (default #f)))
94
95
96 (define %base-groups
97 ;; Default set of groups.
98 (let-syntax ((system-group (syntax-rules ()
99 ((_ args ...)
100 (user-group (system? #t) args ...)))))
101 (list (system-group (name "root") (id 0))
102 (system-group (name "wheel")) ; root-like users
103 (system-group (name "users")) ; normal users
104 (system-group (name "nogroup")) ; for daemons etc.
105
106 ;; The following groups are conventionally used by things like udev to
107 ;; control access to hardware devices.
108 (system-group (name "tty") (id %tty-gid))
109 (system-group (name "dialout"))
110 (system-group (name "kmem"))
111 (system-group (name "input")) ; input devices, from udev
112 (system-group (name "video"))
113 (system-group (name "audio"))
114 (system-group (name "netdev")) ; used in avahi-dbus.conf
115 (system-group (name "lp"))
116 (system-group (name "disk"))
117 (system-group (name "floppy"))
118 (system-group (name "cdrom"))
119 (system-group (name "tape"))
120 (system-group (name "kvm"))))) ; for /dev/kvm
121
122 (define %base-user-accounts
123 ;; List of standard user accounts. Note that "root" is a special case, so
124 ;; it's not listed here.
125 (list (user-account
126 (name "nobody")
127 (uid 65534)
128 (group "nogroup")
129 (home-directory "/var/empty")
130 (system? #t))))
131
132 (define (default-skeletons)
133 "Return the default skeleton files for /etc/skel. These files are copied by
134 'useradd' in the home directory of newly created user accounts."
135 (define fonts.conf-content
136 ;; SXML for ~/.config/fontconfig/fonts.conf. This works around the fact
137 ;; that Fontconfig currently does not such this directory by default,
138 ;; thereby ignoring fonts installed system-wide (FIXME).
139 `(fontconfig (dir "/run/current-system/profile/share/fonts")))
140
141 (define copy-guile-wm
142 #~(begin
143 (use-modules (guix build utils))
144 (copy-file (car (find-files #$guile-wm "wm-init-sample.scm"))
145 #$output)))
146
147 (let ((profile (plain-file "bash_profile" "\
148 # Honor per-interactive-shell startup file
149 if [ -f ~/.bashrc ]; then . ~/.bashrc; fi\n"))
150 (bashrc (plain-file "bashrc" "\
151 # Bash initialization for interactive non-login shells and
152 # for remote shells (info \"(bash) Bash Startup Files\").
153
154 # Export 'SHELL' to child processes. Programs such as 'screen'
155 # honor it and otherwise use /bin/sh.
156 export SHELL
157
158 if [ -n \"$SSH_CLIENT\" -a -z \"`type -P cat`\" ]
159 then
160 # We are being invoked from a non-interactive SSH session
161 # (as in \"ssh host command\") but 'cat' cannot be found
162 # in $PATH. Source /etc/profile so we get $PATH and other
163 # essential variables.
164 source /etc/profile
165 fi
166
167 # Adjust the prompt depending on whether we're in 'guix environment'.
168 if [ -n \"$GUIX_ENVIRONMENT\" ]
169 then
170 PS1='\\u@\\h \\w [env]\\$ '
171 else
172 PS1='\\u@\\h \\w\\$ '
173 fi
174 alias ls='ls -p --color'
175 alias ll='ls -l'\n"))
176 (zlogin (plain-file "zlogin" "\
177 # Honor system-wide environment variables
178 source /etc/profile\n"))
179 (guile-wm (computed-file "guile-wm" copy-guile-wm
180 #:modules '((guix build utils))))
181 (xdefaults (plain-file "Xdefaults" "\
182 XTerm*utf8: always
183 XTerm*metaSendsEscape: true\n"))
184 (fonts.conf (computed-file
185 "fonts.conf"
186 #~(begin
187 (use-modules (guix build utils)
188 (sxml simple))
189
190 (define dir
191 (string-append #$output
192 "/fontconfig"))
193
194 (mkdir-p dir)
195 (call-with-output-file (string-append dir
196 "/fonts.conf")
197 (lambda (port)
198 (sxml->xml '#$fonts.conf-content port))))
199 #:modules '((guix build utils))))
200 (gdbinit (plain-file "gdbinit" "\
201 # Tell GDB where to look for separate debugging files.
202 set debug-file-directory ~/.guix-profile/lib/debug\n")))
203 `((".bash_profile" ,profile)
204 (".bashrc" ,bashrc)
205 (".zlogin" ,zlogin)
206 (".Xdefaults" ,xdefaults)
207 (".guile-wm" ,guile-wm)
208 (".config" ,fonts.conf)
209 (".gdbinit" ,gdbinit))))
210
211 (define (skeleton-directory skeletons)
212 "Return a directory containing SKELETONS, a list of name/derivation tuples."
213 (computed-file "skel"
214 #~(begin
215 (use-modules (ice-9 match)
216 (guix build utils))
217
218 (mkdir #$output)
219 (chdir #$output)
220
221 ;; Note: copy the skeletons instead of symlinking
222 ;; them like 'file-union' does, because 'useradd'
223 ;; would just copy the symlinks as is.
224 (for-each (match-lambda
225 ((target source)
226 (copy-recursively source target)))
227 '#$skeletons)
228 #t)
229 #:modules '((guix build utils))))
230
231 (define (assert-valid-users/groups users groups)
232 "Raise an error if USERS refer to groups not listed in GROUPS."
233 (let ((groups (list->set (map user-group-name groups))))
234 (define (validate-supplementary-group user group)
235 (unless (set-contains? groups group)
236 (raise (condition
237 (&message
238 (message
239 (format #f (_ "supplementary group '~a' \
240 of user '~a' is undeclared")
241 group
242 (user-account-name user))))))))
243
244 (for-each (lambda (user)
245 (unless (set-contains? groups (user-account-group user))
246 (raise (condition
247 (&message
248 (message
249 (format #f (_ "primary group '~a' \
250 of user '~a' is undeclared")
251 (user-account-group user)
252 (user-account-name user)))))))
253
254 (for-each (cut validate-supplementary-group user <>)
255 (user-account-supplementary-groups user)))
256 users)))
257
258 \f
259 ;;;
260 ;;; Service.
261 ;;;
262
263 (define (user-group->gexp group)
264 "Turn GROUP, a <user-group> object, into a list-valued gexp suitable for
265 'active-groups'."
266 #~(list #$(user-group-name group)
267 #$(user-group-password group)
268 #$(user-group-id group)
269 #$(user-group-system? group)))
270
271 (define (user-account->gexp account)
272 "Turn ACCOUNT, a <user-account> object, into a list-valued gexp suitable for
273 'activate-users'."
274 #~`(#$(user-account-name account)
275 #$(user-account-uid account)
276 #$(user-account-group account)
277 #$(user-account-supplementary-groups account)
278 #$(user-account-comment account)
279 #$(user-account-home-directory account)
280 ,#$(user-account-shell account) ; this one is a gexp
281 #$(user-account-password account)
282 #$(user-account-system? account)))
283
284 (define (account-activation accounts+groups)
285 "Return a gexp that activates ACCOUNTS+GROUPS, a list of <user-account> and
286 <user-group> objects. Raise an error if a user account refers to a undefined
287 group."
288 (define accounts
289 (filter user-account? accounts+groups))
290
291 (define user-specs
292 (map user-account->gexp accounts))
293
294 (define groups
295 (filter user-group? accounts+groups))
296
297 (define group-specs
298 (map user-group->gexp groups))
299
300 (assert-valid-users/groups accounts groups)
301
302 ;; Add users and user groups.
303 #~(begin
304 (setenv "PATH"
305 (string-append #$(@ (gnu packages admin) shadow) "/sbin"))
306 (activate-users+groups (list #$@user-specs)
307 (list #$@group-specs))))
308
309 (define (shells-file shells)
310 "Return a file-like object that builds a shell list for use as /etc/shells
311 based on SHELLS. /etc/shells is used by xterm, polkit, and other programs."
312 (computed-file "shells"
313 #~(begin
314 (use-modules (srfi srfi-1))
315
316 (define shells
317 (delete-duplicates (list #$@shells)))
318
319 (call-with-output-file #$output
320 (lambda (port)
321 (display "\
322 /bin/sh
323 /run/current-system/profile/bin/sh
324 /run/current-system/profile/bin/bash\n" port)
325 (for-each (lambda (shell)
326 (display shell port)
327 (newline port))
328 shells))))))
329 (define (etc-files arguments)
330 "Filter out among ARGUMENTS things corresponding to skeletons, and return
331 the /etc/skel directory for those."
332 (let ((skels (filter pair? arguments))
333 (users (filter user-account? arguments)))
334 `(("skel" ,(skeleton-directory skels))
335 ("shells" ,(shells-file (map user-account-shell users))))))
336
337 (define account-service-type
338 (service-type (name 'account)
339
340 ;; Concatenate <user-account>, <user-group>, and skeleton
341 ;; lists.
342 (compose concatenate)
343 (extend append)
344
345 (extensions
346 (list (service-extension activation-service-type
347 account-activation)
348 (service-extension etc-service-type
349 etc-files)))))
350
351 (define (account-service accounts+groups skeletons)
352 "Return a <service> that takes care of user accounts and user groups, with
353 ACCOUNTS+GROUPS as its initial list of accounts and groups."
354 (service account-service-type
355 (append skeletons accounts+groups)))
356
357 ;;; shadow.scm ends here