system: Separate the activation script from the boot script.
[jackhill/guix/guix.git] / guix / build / activation.scm
CommitLineData
4dfe6c58
LC
1;;; GNU Guix --- Functional package management for GNU
2;;; Copyright © 2013, 2014 Ludovic Courtès <ludo@gnu.org>
3;;;
4;;; This file is part of GNU Guix.
5;;;
6;;; GNU Guix is free software; you can redistribute it and/or modify it
7;;; under the terms of the GNU General Public License as published by
8;;; the Free Software Foundation; either version 3 of the License, or (at
9;;; your option) any later version.
10;;;
11;;; GNU Guix is distributed in the hope that it will be useful, but
12;;; WITHOUT ANY WARRANTY; without even the implied warranty of
13;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14;;; GNU General Public License for more details.
15;;;
16;;; You should have received a copy of the GNU General Public License
17;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
18
19(define-module (guix build activation)
09e028f4 20 #:use-module (guix build utils)
b4140694 21 #:use-module (guix build linux-initrd)
4dfe6c58 22 #:use-module (ice-9 ftw)
ab6a279a
LC
23 #:use-module (ice-9 match)
24 #:use-module (srfi srfi-1)
ad896f23 25 #:use-module (srfi srfi-26)
ab6a279a
LC
26 #:export (activate-users+groups
27 activate-etc
b4140694
LC
28 activate-setuid-programs
29 activate-current-system))
4dfe6c58
LC
30
31;;; Commentary:
32;;;
33;;; This module provides "activation" helpers. Activation is the process that
34;;; consists in setting up system-wide files and directories so that an
35;;; 'operating-system' configuration becomes active.
36;;;
37;;; Code:
38
ab6a279a
LC
39(define* (add-group name #:key gid password
40 (log-port (current-error-port)))
41 "Add NAME as a user group, with the given numeric GID if specified."
42 ;; Use 'groupadd' from the Shadow package.
43 (format log-port "adding group '~a'...~%" name)
44 (let ((args `(,@(if gid `("-g" ,(number->string gid)) '())
45 ,@(if password `("-p" ,password) '())
46 ,name)))
47 (zero? (apply system* "groupadd" args))))
48
49(define* (add-user name group
50 #:key uid comment home shell password
51 (supplementary-groups '())
52 (log-port (current-error-port)))
53 "Create an account for user NAME part of GROUP, with the specified
54properties. Return #t on success."
55 (format log-port "adding user '~a'...~%" name)
56
57 (if (and uid (zero? uid))
58
59 ;; 'useradd' fails with "Cannot determine your user name" if the root
60 ;; account doesn't exist. Thus, for bootstrapping purposes, create that
61 ;; one manually.
62 (begin
63 (call-with-output-file "/etc/shadow"
64 (cut format <> "~a::::::::~%" name))
65 (call-with-output-file "/etc/passwd"
66 (cut format <> "~a:x:~a:~a:~a:~a:~a~%"
67 name "0" "0" comment home shell))
68 (chmod "/etc/shadow" #o600)
69 #t)
70
71 ;; Use 'useradd' from the Shadow package.
72 (let ((args `(,@(if uid `("-u" ,(number->string uid)) '())
73 "-g" ,(if (number? group) (number->string group) group)
74 ,@(if (pair? supplementary-groups)
75 `("-G" ,(string-join supplementary-groups ","))
76 '())
77 ,@(if comment `("-c" ,comment) '())
f3b692ac
LC
78 ,@(if home
79 (if (file-exists? home)
80 `("-d" ,home) ; avoid warning from 'useradd'
81 `("-d" ,home "--create-home"))
82 '())
ab6a279a
LC
83 ,@(if shell `("-s" ,shell) '())
84 ,@(if password `("-p" ,password) '())
85 ,name)))
86 (zero? (apply system* "useradd" args)))))
87
88(define (activate-users+groups users groups)
89 "Make sure the accounts listed in USERS and the user groups listed in GROUPS
90are all available.
91
92Each item in USERS is a list of all the characteristics of a user account;
93each item in GROUPS is a tuple with the group name, group password or #f, and
94numeric gid or #f."
95 (define (touch file)
96 (call-with-output-file file (const #t)))
97
98 (define activate-user
99 (match-lambda
100 ((name uid group supplementary-groups comment home shell password)
101 (unless (false-if-exception (getpwnam name))
102 (let ((profile-dir (string-append "/var/guix/profiles/per-user/"
103 name)))
104 (add-user name group
105 #:uid uid
106 #:supplementary-groups supplementary-groups
107 #:comment comment
108 #:home home
109 #:shell shell
110 #:password password)
111
112 ;; Create the profile directory for the new account.
113 (let ((pw (getpwnam name)))
114 (mkdir-p profile-dir)
115 (chown profile-dir (passwd:uid pw) (passwd:gid pw))))))))
116
117 ;; 'groupadd' aborts if the file doesn't already exist.
118 (touch "/etc/group")
119
120 ;; Create the root account so we can use 'useradd' and 'groupadd'.
121 (activate-user (find (match-lambda
122 ((name (? zero?) _ ...) #t)
123 (_ #f))
124 users))
125
126 ;; Then create the groups.
127 (for-each (match-lambda
128 ((name password gid)
129 (add-group name #:gid gid #:password password)))
130 groups)
131
132 ;; Finally create the other user accounts.
133 (for-each activate-user users))
134
4dfe6c58
LC
135(define (activate-etc etc)
136 "Install ETC, a directory in the store, as the source of static files for
137/etc."
138
139 ;; /etc is a mixture of static and dynamic settings. Here is where we
140 ;; initialize it from the static part.
141
142 (format #t "populating /etc from ~a...~%" etc)
143 (let ((rm-f (lambda (f)
144 (false-if-exception (delete-file f)))))
145 (rm-f "/etc/static")
146 (symlink etc "/etc/static")
147 (for-each (lambda (file)
148 ;; TODO: Handle 'shadow' specially so that changed
149 ;; password aren't lost.
150 (let ((target (string-append "/etc/" file))
151 (source (string-append "/etc/static/" file)))
152 (rm-f target)
153 (symlink source target)))
154 (scandir etc
155 (lambda (file)
156 (not (member file '("." ".."))))
157
158 ;; The default is 'string-locale<?', but we don't have
159 ;; it when run from the initrd's statically-linked
160 ;; Guile.
161 string<?))
162
163 ;; Prevent ETC from being GC'd.
164 (rm-f "/var/guix/gcroots/etc-directory")
165 (symlink etc "/var/guix/gcroots/etc-directory")))
166
09e028f4
LC
167(define %setuid-directory
168 ;; Place where setuid programs are stored.
169 "/run/setuid-programs")
170
171(define (activate-setuid-programs programs)
172 "Turn PROGRAMS, a list of file names, into setuid programs stored under
173%SETUID-DIRECTORY."
174 (define (make-setuid-program prog)
175 (let ((target (string-append %setuid-directory
176 "/" (basename prog))))
177 (catch 'system-error
178 (lambda ()
179 (link prog target))
180 (lambda args
181 ;; Perhaps PROG and TARGET live in a different file system, so copy
182 ;; PROG.
183 (copy-file prog target)))
184 (chown target 0 0)
185 (chmod target #o6555)))
186
187 (format #t "setting up setuid programs in '~a'...~%"
188 %setuid-directory)
189 (if (file-exists? %setuid-directory)
ad896f23
LC
190 (for-each (compose delete-file
191 (cut string-append %setuid-directory "/" <>))
09e028f4
LC
192 (scandir %setuid-directory
193 (lambda (file)
194 (not (member file '("." ".."))))
195 string<?))
196 (mkdir-p %setuid-directory))
197
198 (for-each make-setuid-program programs))
199
b4140694
LC
200(define %current-system
201 ;; The system that is current (a symlink.) This is not necessarily the same
484a2b3a
LC
202 ;; as the system we booted (aka. /run/booted-system) because we can re-build
203 ;; a new system configuration and activate it, without rebooting.
b4140694
LC
204 "/run/current-system")
205
206(define (boot-time-system)
207 "Return the '--system' argument passed on the kernel command line."
208 (find-long-option "--system" (linux-command-line)))
209
484a2b3a
LC
210(define* (activate-current-system #:optional (system (boot-time-system)))
211 "Atomically make SYSTEM the current system."
b4140694 212 (format #t "making '~a' the current system...~%" system)
b4140694
LC
213
214 ;; Atomically make SYSTEM current.
215 (let ((new (string-append %current-system ".new")))
216 (symlink system new)
217 (rename-file new %current-system)))
218
4dfe6c58 219;;; activation.scm ends here