environment.scm 52 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226
  1. ;;; GNU Guix --- Functional package management for GNU
  2. ;;; Copyright © 2014, 2015, 2018 David Thompson <davet@gnu.org>
  3. ;;; Copyright © 2015-2023 Ludovic Courtès <ludo@gnu.org>
  4. ;;; Copyright © 2018 Mike Gerwitz <mtg@gnu.org>
  5. ;;; Copyright © 2022, 2023 John Kehayias <john.kehayias@protonmail.com>
  6. ;;;
  7. ;;; This file is part of GNU Guix.
  8. ;;;
  9. ;;; GNU Guix is free software; you can redistribute it and/or modify it
  10. ;;; under the terms of the GNU General Public License as published by
  11. ;;; the Free Software Foundation; either version 3 of the License, or (at
  12. ;;; your option) any later version.
  13. ;;;
  14. ;;; GNU Guix is distributed in the hope that it will be useful, but
  15. ;;; WITHOUT ANY WARRANTY; without even the implied warranty of
  16. ;;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
  17. ;;; GNU General Public License for more details.
  18. ;;;
  19. ;;; You should have received a copy of the GNU General Public License
  20. ;;; along with GNU Guix. If not, see <http://www.gnu.org/licenses/>.
  21. (define-module (guix scripts environment)
  22. #:use-module (guix ui)
  23. #:use-module (guix store)
  24. #:use-module (guix utils)
  25. #:use-module ((guix status) #:select (with-status-verbosity))
  26. #:use-module (guix derivations)
  27. #:use-module (guix packages)
  28. #:use-module (guix profiles)
  29. #:use-module (guix search-paths)
  30. #:use-module (guix build utils)
  31. #:use-module (guix monads)
  32. #:use-module ((guix gexp) #:select (lower-object))
  33. #:autoload (guix describe) (current-profile current-channels)
  34. #:autoload (guix channels) (guix-channel? channel-commit)
  35. #:use-module (guix scripts)
  36. #:use-module (guix scripts build)
  37. #:autoload (guix scripts pack) (symlink-spec-option-parser)
  38. #:use-module (guix transformations)
  39. #:autoload (ice-9 ftw) (scandir)
  40. #:autoload (gnu build install) (evaluate-populate-directive)
  41. #:autoload (gnu build linux-container) (call-with-container %namespaces
  42. user-namespace-supported?
  43. unprivileged-user-namespace-supported?
  44. setgroups-supported?)
  45. #:autoload (gnu build accounts) (password-entry group-entry
  46. password-entry-name password-entry-directory
  47. write-passwd write-group)
  48. #:autoload (guix build syscalls) (set-network-interface-up openpty login-tty)
  49. #:use-module (gnu system file-systems)
  50. #:autoload (gnu packages) (specification->package+output)
  51. #:autoload (gnu packages bash) (bash)
  52. #:autoload (gnu packages bootstrap) (bootstrap-executable %bootstrap-guile)
  53. #:autoload (gnu packages package-management) (guix)
  54. #:use-module (ice-9 match)
  55. #:autoload (ice-9 rdelim) (read-line)
  56. #:use-module (ice-9 vlist)
  57. #:autoload (web uri) (string->uri uri-scheme)
  58. #:use-module (srfi srfi-1)
  59. #:use-module (srfi srfi-11)
  60. #:use-module (srfi srfi-26)
  61. #:use-module (srfi srfi-37)
  62. #:use-module (srfi srfi-98)
  63. #:export (assert-container-features
  64. load-manifest
  65. guix-environment
  66. guix-environment*
  67. show-environment-options-help
  68. (%options . %environment-options)
  69. (%default-options . %environment-default-options)))
  70. (define %default-shell
  71. (or (getenv "SHELL") "/bin/sh"))
  72. (define* (show-search-paths profile manifest #:key pure?)
  73. "Display the search paths of MANIFEST applied to PROFILE. When PURE? is #t,
  74. do not augment existing environment variables with additional search paths."
  75. (for-each (match-lambda
  76. ((search-path . value)
  77. (display
  78. (search-path-definition search-path value
  79. #:kind (if pure? 'exact 'prefix)))
  80. (newline)))
  81. (profile-search-paths profile manifest)))
  82. (define (show-environment-options-help)
  83. "Print help about options shared between 'guix environment' and 'guix
  84. shell'."
  85. (display (G_ "
  86. -e, --expression=EXPR create environment for the package that EXPR
  87. evaluates to"))
  88. (display (G_ "
  89. -m, --manifest=FILE create environment with the manifest from FILE"))
  90. (display (G_ "
  91. -p, --profile=PATH create environment from profile at PATH"))
  92. (display (G_ "
  93. --check check if the shell clobbers environment variables"))
  94. (display (G_ "
  95. --pure unset existing environment variables"))
  96. (display (G_ "
  97. -E, --preserve=REGEXP preserve environment variables that match REGEXP"))
  98. (display (G_ "
  99. --search-paths display needed environment variable definitions"))
  100. (display (G_ "
  101. -r, --root=FILE make FILE a symlink to the result, and register it
  102. as a garbage collector root"))
  103. (display (G_ "
  104. -C, --container run command within an isolated container"))
  105. (display (G_ "
  106. -N, --network allow containers to access the network"))
  107. (display (G_ "
  108. -P, --link-profile link environment profile to ~/.guix-profile within
  109. an isolated container"))
  110. (display (G_ "
  111. -W, --nesting make Guix available within the container"))
  112. (display (G_ "
  113. -u, --user=USER instead of copying the name and home of the current
  114. user into an isolated container, use the name USER
  115. with home directory /home/USER"))
  116. (display (G_ "
  117. --no-cwd do not share current working directory with an
  118. isolated container"))
  119. (display (G_ "
  120. --share=SPEC for containers, share writable host file system
  121. according to SPEC"))
  122. (display (G_ "
  123. --expose=SPEC for containers, expose read-only host file system
  124. according to SPEC"))
  125. (display (G_ "
  126. -S, --symlink=SPEC for containers, add symlinks to the profile according
  127. to SPEC, e.g. \"/usr/bin/env=bin/env\"."))
  128. (display (G_ "
  129. -v, --verbosity=LEVEL use the given verbosity LEVEL"))
  130. (display (G_ "
  131. --bootstrap use bootstrap binaries to build the environment")))
  132. (define (show-help)
  133. (display (G_ "Usage: guix environment [OPTION]... PACKAGE... [-- COMMAND...]
  134. Build an environment that includes the dependencies of PACKAGE and execute
  135. COMMAND or an interactive shell in that environment.\n"))
  136. (warning (G_ "This command is deprecated in favor of 'guix shell'.\n"))
  137. (newline)
  138. ;; These two options are left out in 'guix shell'.
  139. (display (G_ "
  140. -l, --load=FILE create environment for the package that the code within
  141. FILE evaluates to"))
  142. (display (G_ "
  143. --ad-hoc include all specified packages in the environment instead
  144. of only their inputs"))
  145. (show-environment-options-help)
  146. (newline)
  147. (show-build-options-help)
  148. (newline)
  149. (show-native-build-options-help)
  150. (newline)
  151. (show-transformation-options-help)
  152. (newline)
  153. (display (G_ "
  154. -h, --help display this help and exit"))
  155. (display (G_ "
  156. -V, --version display version information and exit"))
  157. (newline)
  158. (show-bug-report-information))
  159. (define %default-options
  160. `((system . ,(%current-system))
  161. (substitutes? . #t)
  162. (symlinks . ())
  163. (offload? . #t)
  164. (graft? . #t)
  165. (print-build-trace? . #t)
  166. (print-extended-build-trace? . #t)
  167. (multiplexed-build-output? . #t)
  168. (debug . 0)
  169. (verbosity . 1)))
  170. (define (tag-package-arg opts arg)
  171. "Return a two-element list with the form (TAG ARG) that tags ARG with either
  172. 'ad-hoc' in OPTS has the 'ad-hoc?' key set to #t, or 'inputs' otherwise."
  173. ;; Normally, the transitive inputs to a package are added to an environment,
  174. ;; but the ad-hoc? flag changes the meaning of a package argument such that
  175. ;; the package itself is added to the environment instead.
  176. (if (assoc-ref opts 'ad-hoc?)
  177. `(ad-hoc-package ,arg)
  178. `(package ,arg)))
  179. (define %options
  180. ;; Specification of the command-line options.
  181. (cons* (option '(#\h "help") #f #f
  182. (lambda args
  183. (show-help)
  184. (exit 0)))
  185. (option '(#\V "version") #f #f
  186. (lambda args
  187. (show-version-and-exit "guix environment")))
  188. (option '("check") #f #f
  189. (lambda (opt name arg result)
  190. (alist-cons 'check? #t result)))
  191. (option '("pure") #f #f
  192. (lambda (opt name arg result)
  193. (alist-cons 'pure #t result)))
  194. (option '(#\E "preserve") #t #f
  195. (lambda (opt name arg result)
  196. (alist-cons 'inherit-regexp
  197. (make-regexp* arg)
  198. result)))
  199. (option '("inherit") #t #f ;deprecated
  200. (lambda (opt name arg result)
  201. (warning (G_ "'--inherit' is deprecated, \
  202. use '--preserve' instead~%"))
  203. (alist-cons 'inherit-regexp
  204. (make-regexp* arg)
  205. result)))
  206. (option '("search-paths") #f #f
  207. (lambda (opt name arg result)
  208. (alist-cons 'search-paths #t result)))
  209. (option '(#\l "load") #t #f
  210. (lambda (opt name arg result)
  211. (alist-cons 'load
  212. (tag-package-arg result arg)
  213. result)))
  214. (option '(#\e "expression") #t #f
  215. (lambda (opt name arg result)
  216. (alist-cons 'expression
  217. (tag-package-arg result arg)
  218. result)))
  219. (option '(#\m "manifest") #t #f
  220. (lambda (opt name arg result)
  221. (alist-cons 'manifest
  222. arg
  223. result)))
  224. (option '("ad-hoc") #f #f
  225. (lambda (opt name arg result)
  226. (alist-cons 'ad-hoc? #t result)))
  227. (option '(#\n "dry-run") #f #f
  228. (lambda (opt name arg result)
  229. (alist-cons 'dry-run? #t result)))
  230. (option '(#\C "container") #f #f
  231. (lambda (opt name arg result)
  232. (alist-cons 'container? #t result)))
  233. (option '(#\N "network") #f #f
  234. (lambda (opt name arg result)
  235. (alist-cons 'network? #t result)))
  236. (option '(#\W "nesting") #f #f
  237. (lambda (opt name arg result)
  238. (alist-cons 'nesting? #t result)))
  239. (option '(#\P "link-profile") #f #f
  240. (lambda (opt name arg result)
  241. (alist-cons 'link-profile? #t result)))
  242. (option '(#\p "profile") #t #f
  243. (lambda (opt name arg result)
  244. (alist-cons 'profile arg
  245. (alist-delete 'profile result eq?))))
  246. (option '(#\u "user") #t #f
  247. (lambda (opt name arg result)
  248. (alist-cons 'user arg
  249. (alist-delete 'user result eq?))))
  250. (option '("no-cwd") #f #f
  251. (lambda (opt name arg result)
  252. (alist-cons 'no-cwd? #t result)))
  253. (option '("share") #t #f
  254. (lambda (opt name arg result)
  255. (alist-cons 'file-system-mapping
  256. (specification->file-system-mapping arg #t)
  257. result)))
  258. (option '("expose") #t #f
  259. (lambda (opt name arg result)
  260. (alist-cons 'file-system-mapping
  261. (specification->file-system-mapping arg #f)
  262. result)))
  263. (option '(#\S "symlink") #t #f
  264. (lambda (opt name arg result)
  265. ;; Delay call to avoid auto-loading (guix scripts pack)
  266. ;; when unnecessary.
  267. (symlink-spec-option-parser opt name arg result)))
  268. (option '(#\r "root") #t #f
  269. (lambda (opt name arg result)
  270. (alist-cons 'gc-root arg result)))
  271. (option '(#\v "verbosity") #t #f
  272. (lambda (opt name arg result)
  273. (let ((level (string->number* arg)))
  274. (alist-cons 'verbosity level
  275. (alist-delete 'verbosity result)))))
  276. (option '("bootstrap") #f #f
  277. (lambda (opt name arg result)
  278. (alist-cons 'bootstrap? #t result)))
  279. (append %transformation-options
  280. %standard-build-options
  281. %standard-native-build-options)))
  282. (define (pick-all alist key)
  283. "Return a list of values in ALIST associated with KEY."
  284. (define same-key? (cut eq? key <>))
  285. (fold (lambda (pair memo)
  286. (match pair
  287. (((? same-key? k) . v)
  288. (cons v memo))
  289. (_ memo)))
  290. '() alist))
  291. (define (load-manifest file) ;TODO: factorize
  292. "Load the user-profile manifest (Scheme code) from FILE and return it."
  293. (let ((user-module (make-user-module '((guix profiles) (gnu)))))
  294. (load* file user-module)))
  295. (define (options/resolve-packages store opts)
  296. "Return OPTS with package specification strings replaced by manifest entries
  297. for the corresponding packages."
  298. (define (manifest-entry=? e1 e2)
  299. (and (eq? (manifest-entry-item e1) (manifest-entry-item e2))
  300. (string=? (manifest-entry-output e1)
  301. (manifest-entry-output e2))))
  302. (define transform
  303. (options->transformation opts))
  304. (define* (package->manifest-entry* package #:optional (output "out"))
  305. (package->manifest-entry (transform package) output))
  306. (define (packages->outputs packages mode)
  307. (match packages
  308. ((? package? package)
  309. (if (eq? mode 'ad-hoc-package)
  310. (list (package->manifest-entry* package))
  311. (manifest-entries (package->development-manifest package))))
  312. (((? package? package) (? string? output))
  313. (if (eq? mode 'ad-hoc-package)
  314. (list (package->manifest-entry* package output))
  315. (manifest-entries (package->development-manifest package))))
  316. ((lst ...)
  317. (append-map (cut packages->outputs <> mode) lst))))
  318. (manifest
  319. (delete-duplicates
  320. (append-map (match-lambda
  321. (('package 'ad-hoc-package (? string? spec))
  322. (let-values (((package output)
  323. (specification->package+output spec)))
  324. (list (package->manifest-entry* package output))))
  325. (('package 'package (? string? spec))
  326. (manifest-entries
  327. (package->development-manifest
  328. (transform (specification->package+output spec)))))
  329. (('expression mode str)
  330. ;; Add all the outputs of the package STR evaluates to.
  331. (packages->outputs (read/eval str) mode))
  332. (('load mode file)
  333. ;; Add all the outputs of the package defined in FILE.
  334. (let ((module (make-user-module '())))
  335. (packages->outputs (load* file module) mode)))
  336. (('manifest . file)
  337. (manifest-entries (load-manifest file)))
  338. (('nesting? . #t)
  339. (if (assoc-ref opts 'profile)
  340. '()
  341. (let ((profile (and=> (current-profile) readlink*)))
  342. (if (or (not profile) (not (store-path? profile)))
  343. (begin
  344. (warning (G_ "\
  345. could not add current Guix to the profile~%"))
  346. '())
  347. (list (manifest-entry
  348. (name "guix")
  349. (version
  350. (or (any (lambda (channel)
  351. (and (guix-channel? channel)
  352. (channel-commit channel)))
  353. (current-channels))
  354. "0"))
  355. (item profile)
  356. (search-paths
  357. (package-native-search-paths guix))))))))
  358. (_ '()))
  359. opts)
  360. manifest-entry=?)))
  361. (define (manifest->derivation manifest system bootstrap?)
  362. "Return the derivation for a profile of MANIFEST.
  363. BOOTSTRAP? specifies whether to use the bootstrap Guile to build the profile."
  364. (profile-derivation manifest
  365. #:system system
  366. ;; Packages can have conflicting inputs, or explicit
  367. ;; inputs that conflict with implicit inputs (e.g., gcc,
  368. ;; gzip, etc.). Thus, do not error out when we
  369. ;; encounter collision.
  370. #:allow-collisions? #t
  371. #:hooks (if bootstrap?
  372. '()
  373. %default-profile-hooks)
  374. #:locales? (not bootstrap?)))
  375. (define requisites* (store-lift requisites))
  376. (define (inputs->requisites inputs)
  377. "Convert INPUTS, a list of input tuples or store path strings, into a set of
  378. requisite store items i.e. the union closure of all the inputs."
  379. (define (input->requisites input)
  380. (requisites*
  381. (match input
  382. ((drv output)
  383. (list (derivation->output-path drv output)))
  384. ((drv)
  385. (list (derivation->output-path drv)))
  386. ((? direct-store-path? path)
  387. (list path)))))
  388. (mlet %store-monad ((reqs (mapm %store-monad
  389. input->requisites inputs)))
  390. (return (delete-duplicates (concatenate reqs)))))
  391. (define (setup-fhs profile)
  392. "Setup the FHS container by creating and linking expected directories from
  393. PROFILE (other bind mounts are done in LAUNCH-ENVIRONMENT/CONTAINER),
  394. providing a symlink for CC if GCC is in the container PROFILE, and writing
  395. /etc/ld.so.conf."
  396. ;; Additional symlinks for an FHS container.
  397. (define fhs-symlinks
  398. `(("/lib" . "/usr/lib")
  399. ,(if (target-64bit?)
  400. '("/lib" . "/lib64")
  401. '("/lib" . "/lib32"))
  402. ("/bin" . "/usr/bin")
  403. ("/sbin" . "/usr/sbin")))
  404. ;; A procedure to symlink the contents (at the top level) of a directory,
  405. ;; excluding the directory itself and parent, along with any others provided
  406. ;; in EXCLUDE.
  407. (define* (link-contents dir #:key (exclude '()))
  408. (for-each (lambda (file)
  409. (symlink (string-append profile dir "/" file)
  410. (string-append dir "/" file)))
  411. (scandir (string-append profile dir)
  412. (negate (cut member <>
  413. (append exclude '("." ".." )))))))
  414. ;; The FHS container sets up the expected filesystem through MAPPINGS with
  415. ;; FHS-MAPPINGS (in LAUNCH-ENVIRONMENT/CONTAINER), the symlinks through
  416. ;; FHS-SYMLINKS, and linking the contents of PROFILE/bin and PROFILE/etc
  417. ;; using LINK-CONTENTS, as these both have or will have contents for a
  418. ;; non-FHS container so must be handled separately.
  419. (mkdir-p "/usr")
  420. (for-each (lambda (link)
  421. (if (file-exists? (car link))
  422. (symlink (car link) (cdr link))))
  423. fhs-symlinks)
  424. (link-contents "/bin" #:exclude '("sh"))
  425. (mkdir-p "/etc")
  426. (link-contents "/etc")
  427. ;; Provide a frequently expected 'cc' symlink to gcc (in case it is in
  428. ;; PROFILE), though this could also be done by the user in the container,
  429. ;; e.g. in $HOME/.local/bin and adding that to $PATH. Note: we do this in
  430. ;; /bin since that already has the sh symlink and the other (optional) FHS
  431. ;; bin directories will link to /bin.
  432. (let ((gcc-path (string-append profile "/bin/gcc")))
  433. (if (file-exists? gcc-path)
  434. (symlink gcc-path "/bin/cc")))
  435. ;; Guix's ldconfig doesn't search in FHS default locations, so provide a
  436. ;; minimal ld.so.conf.
  437. (call-with-output-file "/etc/ld.so.conf"
  438. (lambda (port)
  439. (for-each (lambda (directory)
  440. (display directory port)
  441. (newline port))
  442. ;; /lib/nss is needed as Guix's nss puts libraries
  443. ;; there rather than in the lib directory.
  444. '("/lib" "/lib/nss")))))
  445. (define (status->exit-code status)
  446. "Compute the exit code made from STATUS, a value as returned by 'waitpid',
  447. and suitable for 'exit'."
  448. ;; See <bits/waitstatus.h>.
  449. (or (status:exit-val status)
  450. (logior #x80 (status:term-sig status))))
  451. (define exit/status (compose exit status->exit-code))
  452. (define primitive-exit/status (compose primitive-exit status->exit-code))
  453. (define* (launch-environment command profile manifest
  454. #:key pure? (white-list '())
  455. emulate-fhs?)
  456. "Load the environment of PROFILE, which corresponds to MANIFEST, and execute
  457. COMMAND. When PURE?, pre-existing environment variables are cleared before
  458. setting the new ones, except those matching the regexps in WHITE-LIST. When
  459. EMULATE-FHS?, first set up an FHS environment with $PATH and generate the LD
  460. cache."
  461. ;; Properly handle SIGINT, so pressing C-c in an interactive terminal
  462. ;; application works.
  463. (sigaction SIGINT SIG_DFL)
  464. (load-profile profile manifest
  465. #:pure? pure? #:white-list-regexps white-list)
  466. ;; Give users a way to know that they're in 'guix environment', so they can
  467. ;; adjust 'PS1' accordingly, for instance. Set it to PROFILE so users can
  468. ;; conveniently access its contents.
  469. (setenv "GUIX_ENVIRONMENT" profile)
  470. (match command
  471. ((program . args)
  472. (catch 'system-error
  473. (lambda ()
  474. (when emulate-fhs?
  475. ;; When running in a container with EMULATE-FHS?, augment $PATH
  476. ;; (optional, but to better match FHS expectations), and generate
  477. ;; /etc/ld.so.cache.
  478. (setenv "PATH" (string-append "/bin:/usr/bin:/sbin:/usr/sbin"
  479. (if (getenv "PATH")
  480. (string-append ":" (getenv "PATH"))
  481. "")))
  482. (invoke "ldconfig" "-X"))
  483. (apply execlp program program args))
  484. (lambda _
  485. ;; Report the error from here because the parent process cannot
  486. ;; distinguish between the conventional 127 exit code and a process
  487. ;; that exited with 127 for other reasons (e.g., "sh -c xyz").
  488. (report-error (G_ "~a: command not found~%") program)
  489. (suggest-command-name profile command)
  490. ;; Following established convention, exit with 127 (aka. EX_NOTFOUND)
  491. ;; upon ENOENT.
  492. (primitive-_exit 127))))))
  493. (define (child-shell-environment shell profile manifest)
  494. "Create a child process, load PROFILE and MANIFEST, and then run SHELL in
  495. interactive mode in it. Return a name/value vhash for all the variables shown
  496. by running 'set' in the shell."
  497. (define-values (controller inferior)
  498. (openpty))
  499. (define script
  500. ;; Script to obtain the list of environment variable values. On a POSIX
  501. ;; shell we can rely on 'set', but on fish we have to use 'env' (fish's
  502. ;; 'set' truncates values and prints them in a different format.)
  503. "env || /usr/bin/env || set; echo GUIX-CHECK-DONE; read x; exit\n")
  504. (define lines
  505. (match (primitive-fork)
  506. (0
  507. (catch #t
  508. (lambda ()
  509. (load-profile profile manifest #:pure? #t)
  510. ;; Mark the terminal as "unknown" do avoid ANSI escape codes such
  511. ;; as bracketed paste that would mess up the output of the script.
  512. (setenv "TERM" "")
  513. (setenv "GUIX_ENVIRONMENT" profile)
  514. (close-fdes controller)
  515. (login-tty inferior)
  516. (execl shell shell))
  517. (lambda _
  518. (primitive-exit 127))))
  519. (pid
  520. (close-fdes inferior)
  521. (let* ((port (fdopen controller "r+l"))
  522. (result (begin
  523. (display script port)
  524. (let loop ((lines '()))
  525. (match (read-line port)
  526. ((? eof-object?) (reverse lines))
  527. ("GUIX-CHECK-DONE\r"
  528. (display "done\n" port)
  529. (reverse lines))
  530. (line
  531. ;; Drop the '\r' from LINE.
  532. (loop (cons (string-drop-right line 1)
  533. lines))))))))
  534. (close-port port)
  535. (waitpid pid)
  536. result))))
  537. (fold (lambda (line table)
  538. ;; Note: 'set' in fish outputs "NAME VALUE" instead of "NAME=VALUE"
  539. ;; but it also truncates values anyway, so don't try to support it.
  540. (let ((index (string-index line #\=)))
  541. (if index
  542. (vhash-cons (string-take line index)
  543. (string-drop line (+ 1 index))
  544. table)
  545. table)))
  546. vlist-null
  547. lines))
  548. (define* (validate-child-shell-environment profile manifest
  549. #:optional (shell %default-shell))
  550. "Run SHELL in interactive mode in an environment for PROFILE and MANIFEST
  551. and report clobbered environment variables."
  552. (define warned? #f)
  553. (define-syntax-rule (warn exp ...)
  554. (begin
  555. (set! warned? #t)
  556. (warning exp ...)))
  557. (info (G_ "checking the environment variables visible from shell '~a'...~%")
  558. shell)
  559. (let ((actual (child-shell-environment shell profile manifest)))
  560. (when (vlist-null? actual)
  561. (leave (G_ "failed to determine environment of shell '~a'~%")
  562. shell))
  563. (for-each (match-lambda
  564. ((spec . expected)
  565. (let ((name (search-path-specification-variable spec)))
  566. (match (vhash-assoc name actual)
  567. (#f
  568. (warn (G_ "variable '~a' is missing from shell \
  569. environment~%")
  570. name))
  571. ((_ . actual)
  572. (cond ((string=? expected actual)
  573. #t)
  574. ((string-prefix? expected actual)
  575. (warn (G_ "variable '~a' has unexpected \
  576. suffix '~a'~%")
  577. name
  578. (string-drop actual
  579. (string-length expected))))
  580. (else
  581. (warn (G_ "variable '~a' is clobbered: '~a'~%")
  582. name actual))))))))
  583. (profile-search-paths profile manifest))
  584. ;; Special case.
  585. (match (vhash-assoc "GUIX_ENVIRONMENT" actual)
  586. (#f
  587. (warn (G_ "'GUIX_ENVIRONMENT' is missing from the shell \
  588. environment~%")))
  589. ((_ . value)
  590. (unless (string=? value profile)
  591. (warn (G_ "'GUIX_ENVIRONMENT' is set to '~a' instead of '~a'~%")
  592. value profile))))
  593. ;; Check the prompt unless we have more important warnings.
  594. (unless warned?
  595. (match (vhash-assoc "PS1" actual)
  596. (#f #f)
  597. ((_ . str)
  598. (when (and (getenv "PS1") (string=? str (getenv "PS1"))
  599. ;; 'PS1' might be conditional on 'GUIX_ENVIRONMENT', as
  600. ;; shown in the hint below.
  601. (not (or (string-contains str "$GUIX_ENVIRONMENT")
  602. (string-contains str "${GUIX_ENVIRONMENT"))))
  603. (warning (G_ "'PS1' is the same in sub-shell~%"))
  604. (display-hint (G_ "Consider setting a different prompt for
  605. environment shells to make them distinguishable.
  606. If you are using Bash, you can do that by adding these lines to
  607. @file{~/.bashrc}:
  608. @example
  609. PS1='\\u@@\\h \\w${GUIX_ENVIRONMENT:+ [env]}\\$ '
  610. @end example
  611. "))))))
  612. (if warned?
  613. (begin
  614. (display-hint (G_ "One or more environment variables have a
  615. different value in the shell than the one we set. This means that you may
  616. find yourself running code in an environment different from the one you asked
  617. Guix to prepare.
  618. This usually indicates that your shell startup files are unexpectedly
  619. modifying those environment variables. For example, if you are using Bash,
  620. make sure that environment variables are set or modified in
  621. @file{~/.bash_profile} and @emph{not} in @file{~/.bashrc}. For more
  622. information on Bash startup files, run:
  623. @example
  624. info \"(bash) Bash Startup Files\"
  625. @end example
  626. Alternatively, you can avoid the problem by passing the @option{--container}
  627. or @option{-C} option. That will give you a fully isolated environment
  628. running in a \"container\", immune to the issue described above."))
  629. (exit 1))
  630. (info (G_ "All is good! The shell gets correct environment \
  631. variables.~%")))))
  632. (define (suggest-command-name profile command)
  633. "COMMAND was not found in PROFILE so display a hint suggesting the closest
  634. command name."
  635. (define not-dot?
  636. (match-lambda
  637. ((or "." "..") #f)
  638. (_ #t)))
  639. (match (scandir (string-append profile "/bin") not-dot?)
  640. ((or #f ()) #f)
  641. (available
  642. (match command
  643. ((executable _ ...)
  644. ;; Look for a suggestion with a high threshold: a suggestion is
  645. ;; usually better than no suggestion.
  646. (let ((closest (string-closest executable available
  647. #:threshold 12)))
  648. (unless (or (not closest) (string=? closest executable))
  649. (display-hint (G_ "Did you mean '~a'?~%")
  650. closest))))))))
  651. (define* (launch-environment/fork command profile manifest
  652. #:key pure? (white-list '()))
  653. "Run COMMAND in a new process with an environment containing PROFILE, with
  654. the search paths specified by MANIFEST. When PURE?, pre-existing environment
  655. variables are cleared before setting the new ones, except those matching the
  656. regexps in WHITE-LIST."
  657. (match (primitive-fork)
  658. (0 (launch-environment command profile manifest
  659. #:pure? pure?
  660. #:white-list white-list))
  661. (pid (match (waitpid pid)
  662. ((_ . status)
  663. status)))))
  664. (define* (launch-environment/container #:key command bash user user-mappings
  665. profile manifest link-profile? network?
  666. map-cwd? emulate-fhs? nesting?
  667. (setup-hook #f)
  668. (symlinks '()) (white-list '()))
  669. "Run COMMAND within a container that features the software in PROFILE.
  670. Environment variables are set according to the search paths of MANIFEST. The
  671. global shell is BASH, a file name for a GNU Bash binary in the store. When
  672. NETWORK?, access to the host system network is permitted. USER-MAPPINGS, a
  673. list of file system mappings, contains the user-specified host file systems to
  674. mount inside the container. If USER is not #f, each target of USER-MAPPINGS
  675. will be re-written relative to '/home/USER', and USER will be used for the
  676. passwd entry.
  677. When EMULATE-FHS?, set up the container to follow the Filesystem Hierarchy
  678. Standard and provide a glibc that reads the cache from /etc/ld.so.cache.
  679. SETUP-HOOK is an additional setup procedure to be called, currently only used
  680. with the EMULATE-FHS? option.
  681. When NESTING? is true, share all the store with the container and add Guix to
  682. its profile, allowing its use from within the container.
  683. LINK-PROFILE? creates a symbolic link from ~/.guix-profile to the
  684. environment profile.
  685. SYMLINKS must be a list of (SOURCE -> TARGET) tuples denoting symlinks to be
  686. added to the container.
  687. Preserve environment variables whose name matches the one of the regexps in
  688. WHILE-LIST."
  689. (define (optional-mapping->fs mapping)
  690. (and (file-exists? (file-system-mapping-source mapping))
  691. (file-system-mapping->bind-mount mapping)))
  692. ;; File system mappings for an FHS container, where the entire directory can
  693. ;; be mapped. Others (bin and etc) will already have contents and need to
  694. ;; use LINK-CONTENTS (defined in SETUP-FHS) to symlink the directory
  695. ;; contents.
  696. (define fhs-mappings
  697. (map (lambda (mapping)
  698. (file-system-mapping
  699. (source (string-append profile (car mapping)))
  700. (target (cdr mapping))))
  701. '(("/lib" . "/lib")
  702. ("/include" . "/usr/include")
  703. ("/sbin" . "/sbin")
  704. ("/libexec" . "/usr/libexec")
  705. ("/share" . "/usr/share"))))
  706. (define (nesting-mappings)
  707. ;; Files shared with the host when enabling nesting.
  708. (cons* (file-system-mapping
  709. (source (%store-prefix))
  710. (target source))
  711. (file-system-mapping
  712. (source (cache-directory))
  713. (target source)
  714. (writable? #t))
  715. (let ((uri (string->uri (%daemon-socket-uri))))
  716. (if (or (not uri) (eq? 'file (uri-scheme uri)))
  717. (list (file-system-mapping
  718. (source (%daemon-socket-uri))
  719. (target source)))
  720. '()))))
  721. (mlet %store-monad ((reqs (if nesting?
  722. (return '())
  723. (inputs->requisites
  724. (list (direct-store-path bash) profile)))))
  725. (return
  726. (let* ((cwd (getcwd))
  727. (home (getenv "HOME"))
  728. (uid (if user 1000 (getuid)))
  729. (gid (if user 1000 (getgid)))
  730. ;; On a foreign distro, the name service switch might be
  731. ;; dysfunctional and 'getpwuid' throws. Don't let that hamper
  732. ;; operations.
  733. (passwd (let ((pwd (false-if-exception (getpwuid (getuid)))))
  734. (password-entry
  735. (name (or user
  736. (and=> pwd passwd:name)
  737. (getenv "USER")
  738. "charlie"))
  739. (real-name (if (or user (not pwd))
  740. ""
  741. (passwd:gecos pwd)))
  742. (uid uid) (gid gid) (shell bash)
  743. (directory (if (or user (not pwd))
  744. (string-append "/home/" user)
  745. (passwd:dir pwd))))))
  746. (groups (list (group-entry (name "users") (gid gid))
  747. (group-entry (gid 65534) ;the overflow GID
  748. (name "overflow"))))
  749. (home-dir (password-entry-directory passwd))
  750. (logname (password-entry-name passwd))
  751. (environ (filter (match-lambda
  752. ((variable . value)
  753. (find (cut regexp-exec <> variable)
  754. white-list)))
  755. (get-environment-variables)))
  756. ;; Bind-mount all requisite store items, user-specified mappings,
  757. ;; /bin/sh, the current working directory, and possibly networking
  758. ;; configuration files within the container.
  759. (mappings
  760. (append
  761. (override-user-mappings
  762. user home
  763. (append
  764. ;; Share current working directory, unless asked not to.
  765. (if map-cwd?
  766. (list (file-system-mapping
  767. (source cwd)
  768. (target cwd)
  769. (writable? #t)))
  770. '())
  771. ;; Add the user mappings *after* the current working directory
  772. ;; so that a user can layer bind mounts on top of it.
  773. user-mappings))
  774. ;; Mappings for the union closure of all inputs.
  775. (map (lambda (dir)
  776. (file-system-mapping
  777. (source dir)
  778. (target dir)
  779. (writable? #f)))
  780. reqs)))
  781. (file-systems (append %container-file-systems
  782. (if network?
  783. (filter-map optional-mapping->fs
  784. %network-file-mappings)
  785. '())
  786. (if emulate-fhs?
  787. (filter-map optional-mapping->fs
  788. fhs-mappings)
  789. '())
  790. (if nesting?
  791. (filter-map optional-mapping->fs
  792. (nesting-mappings))
  793. '())
  794. (map file-system-mapping->bind-mount
  795. mappings))))
  796. ;; Trigger autoload now: the child process may lack (gnu build install)
  797. ;; in its file system view.
  798. (identity evaluate-populate-directive)
  799. (exit/status
  800. (call-with-container file-systems
  801. (lambda ()
  802. ;; Setup global shell.
  803. (mkdir-p "/bin")
  804. (symlink bash "/bin/sh")
  805. ;; Set a reasonable default PS1.
  806. (setenv "PS1" "\\u@\\h \\w [env]\\$ ")
  807. ;; Setup directory for temporary files.
  808. (mkdir-p "/tmp")
  809. (for-each (lambda (var)
  810. (setenv var "/tmp"))
  811. ;; The same variables as in Nix's 'build.cc'.
  812. '("TMPDIR" "TEMPDIR" "TMP" "TEMP"))
  813. ;; Some programs expect USER and/or LOGNAME to be set.
  814. (setenv "LOGNAME" logname)
  815. (setenv "USER" logname)
  816. ;; Create a dummy home directory.
  817. (mkdir-p home-dir)
  818. (setenv "HOME" home-dir)
  819. ;; Create symlinks.
  820. (let ((symlink->directives
  821. (match-lambda
  822. ((source '-> target)
  823. `((directory ,(dirname source))
  824. (,source -> ,(string-append profile "/" target)))))))
  825. (for-each (cut evaluate-populate-directive <> ".")
  826. (append-map symlink->directives symlinks)))
  827. ;; Call an additional setup procedure, if provided.
  828. (when setup-hook
  829. (setup-hook profile))
  830. ;; If requested, link $GUIX_ENVIRONMENT to $HOME/.guix-profile;
  831. ;; this allows programs expecting that path to continue working as
  832. ;; expected within a container.
  833. (when link-profile? (link-environment profile home-dir))
  834. ;; Create a dummy /etc/passwd to satisfy applications that demand
  835. ;; to read it, such as 'git clone' over SSH, a valid use-case when
  836. ;; sharing the host's network namespace.
  837. (mkdir-p "/etc")
  838. (write-passwd (list passwd))
  839. (write-group groups)
  840. (unless network?
  841. ;; When isolated from the network, provide a minimal /etc/hosts
  842. ;; to resolve "localhost".
  843. (call-with-output-file "/etc/hosts"
  844. (lambda (port)
  845. (display "127.0.0.1 localhost\n" port)))
  846. ;; Allow local AF_INET communications.
  847. (set-network-interface-up "lo"))
  848. ;; For convenience, start in the user's current working
  849. ;; directory or, if unmapped, the home directory.
  850. (chdir (if map-cwd?
  851. (override-user-dir user home cwd)
  852. home-dir))
  853. ;; Set environment variables that match WHITE-LIST.
  854. (for-each (match-lambda
  855. ((variable . value)
  856. (setenv variable value)))
  857. environ)
  858. (primitive-exit/status
  859. ;; A container's environment is already purified, so no need to
  860. ;; request it be purified again.
  861. (launch-environment command
  862. (if link-profile?
  863. (string-append home-dir "/.guix-profile")
  864. profile)
  865. manifest #:pure? #f
  866. #:emulate-fhs? emulate-fhs?)))
  867. #:guest-uid uid
  868. #:guest-gid gid
  869. #:namespaces (if network?
  870. (delq 'net %namespaces) ; share host network
  871. %namespaces)))))))
  872. (define (user-override-home user)
  873. "Return home directory for override user USER."
  874. (string-append "/home/" user))
  875. (define (override-user-mappings user home mappings)
  876. "If a username USER is provided, rewrite each HOME prefix in file system
  877. mappings MAPPINGS to a home directory determined by 'override-user-dir';
  878. otherwise, return MAPPINGS."
  879. (if (not user)
  880. mappings
  881. (map (lambda (mapping)
  882. (let ((target (file-system-mapping-target mapping)))
  883. (if (string-prefix? home target)
  884. (file-system-mapping
  885. (source (file-system-mapping-source mapping))
  886. (target (override-user-dir user home target))
  887. (writable? (file-system-mapping-writable? mapping)))
  888. mapping)))
  889. mappings)))
  890. (define (override-user-dir user home dir)
  891. "If username USER is provided, overwrite string prefix HOME in DIR with a
  892. directory determined by 'user-override-home'; otherwise, return DIR."
  893. (if (and user (string-prefix? home dir))
  894. (string-append (user-override-home user)
  895. (substring dir (string-length home)))
  896. dir))
  897. (define (link-environment profile home-dir)
  898. "Create a symbolic link from HOME-DIR/.guix-profile to PROFILE."
  899. (let ((profile-dir (string-append home-dir "/.guix-profile")))
  900. (catch 'system-error
  901. (lambda ()
  902. (symlink profile profile-dir))
  903. (lambda args
  904. (if (= EEXIST (system-error-errno args))
  905. (leave (G_ "cannot link profile: '~a' already exists within container~%")
  906. profile-dir)
  907. (apply throw args))))))
  908. (define (environment-bash container? bootstrap? system)
  909. "Return a monadic value in the store monad for the version of GNU Bash
  910. needed in the environment for SYSTEM, if any. If CONTAINER? is #f, return #f.
  911. If CONTAINER? and BOOTSTRAP?, return the store path for the bootstrap Bash.
  912. Otherwise, return the derivation for the Bash package."
  913. (with-monad %store-monad
  914. (cond
  915. ((and container? (not bootstrap?))
  916. (package->derivation bash))
  917. ;; Use the bootstrap Bash instead.
  918. ((and container? bootstrap?)
  919. (lower-object (bootstrap-executable "bash" system)))
  920. (else
  921. (return #f)))))
  922. (define (parse-args args)
  923. "Parse the list of command line arguments ARGS."
  924. (define (handle-argument arg result)
  925. (alist-cons 'package (tag-package-arg result arg) result))
  926. ;; The '--' token is used to separate the command to run from the rest of
  927. ;; the operands.
  928. (let-values (((args command) (break (cut string=? "--" <>) args)))
  929. (let ((opts (parse-command-line args %options (list %default-options)
  930. #:argument-handler handle-argument)))
  931. (match command
  932. (() opts)
  933. (("--") opts)
  934. (("--" command ...) (alist-cons 'exec command opts))))))
  935. (define (assert-container-features)
  936. "Check if containers can be created and exit with an informative error
  937. message if any test fails."
  938. (unless (user-namespace-supported?)
  939. (report-error (G_ "cannot create container: user namespaces unavailable\n"))
  940. (leave (G_ "is your kernel version < 3.10?\n")))
  941. (unless (unprivileged-user-namespace-supported?)
  942. (report-error (G_ "cannot create container: unprivileged user cannot create user namespaces\n"))
  943. (leave (G_ "please set /proc/sys/kernel/unprivileged_userns_clone to \"1\"\n")))
  944. (unless (setgroups-supported?)
  945. (report-error (G_ "cannot create container: /proc/self/setgroups does not exist\n"))
  946. (leave (G_ "is your kernel version < 3.19?\n"))))
  947. (define (register-gc-root target root)
  948. "Make ROOT an indirect root to TARGET. This is procedure is idempotent."
  949. (let* ((root (if (string-prefix? "/" root)
  950. root
  951. (string-append (canonicalize-path (dirname root))
  952. "/" (basename root)))))
  953. (catch 'system-error
  954. (lambda ()
  955. (symlink target root)
  956. ((store-lift add-indirect-root) root))
  957. (lambda args
  958. (if (and (= EEXIST (system-error-errno args))
  959. (equal? (false-if-exception (readlink root)) target))
  960. (with-monad %store-monad
  961. (return #t))
  962. (apply throw args))))))
  963. ;;;
  964. ;;; Entry point.
  965. ;;;
  966. (define-command (guix-environment . args)
  967. (category development)
  968. (synopsis "spawn one-off software environments (deprecated)")
  969. (with-error-handling
  970. (guix-environment* (parse-args args))))
  971. (define (guix-environment* opts)
  972. "Run the 'guix environment' command on OPTS, an alist resulting for
  973. command-line option processing with 'parse-command-line'."
  974. (let* ((pure? (assoc-ref opts 'pure))
  975. (container? (assoc-ref opts 'container?))
  976. (link-prof? (assoc-ref opts 'link-profile?))
  977. (symlinks (assoc-ref opts 'symlinks))
  978. (network? (assoc-ref opts 'network?))
  979. (no-cwd? (assoc-ref opts 'no-cwd?))
  980. (emulate-fhs? (assoc-ref opts 'emulate-fhs?))
  981. (nesting? (assoc-ref opts 'nesting?))
  982. (user (assoc-ref opts 'user))
  983. (bootstrap? (assoc-ref opts 'bootstrap?))
  984. (system (assoc-ref opts 'system))
  985. (profile (assoc-ref opts 'profile))
  986. (command (or (assoc-ref opts 'exec)
  987. ;; Spawn a shell if the user didn't specify
  988. ;; anything in particular.
  989. (if container?
  990. ;; The user's shell is likely not available
  991. ;; within the container.
  992. '("/bin/sh")
  993. (list %default-shell))))
  994. (mappings (pick-all opts 'file-system-mapping))
  995. (white-list (pick-all opts 'inherit-regexp)))
  996. (define store-needed?
  997. ;; Whether connecting to the daemon is needed.
  998. (or container? (not profile)))
  999. (define-syntax-rule (with-store/maybe store exp ...)
  1000. ;; Evaluate EXP... with STORE bound to a connection, unless
  1001. ;; STORE-NEEDED? is false, in which case STORE is bound to #f.
  1002. (let ((proc (lambda (store) exp ...)))
  1003. (if store-needed?
  1004. (with-store s
  1005. (set-build-options-from-command-line s opts)
  1006. (with-build-handler (build-notifier #:use-substitutes?
  1007. (assoc-ref opts 'substitutes?)
  1008. #:verbosity
  1009. (assoc-ref opts 'verbosity)
  1010. #:dry-run?
  1011. (assoc-ref opts 'dry-run?))
  1012. (proc s)))
  1013. (proc #f))))
  1014. (when container? (assert-container-features))
  1015. (when (not container?)
  1016. (when link-prof?
  1017. (leave (G_ "'--link-profile' cannot be used without '--container'~%")))
  1018. (when user
  1019. (leave (G_ "'--user' cannot be used without '--container'~%")))
  1020. (when no-cwd?
  1021. (leave (G_ "--no-cwd cannot be used without '--container'~%")))
  1022. (when emulate-fhs?
  1023. (leave (G_ "'--emulate-fhs' cannot be used without '--container~%'")))
  1024. (when nesting?
  1025. (leave (G_ "'--nesting' cannot be used without '--container~%'")))
  1026. (when (pair? symlinks)
  1027. (leave (G_ "'--symlink' cannot be used without '--container~%'"))))
  1028. (with-store/maybe store
  1029. (with-status-verbosity (assoc-ref opts 'verbosity)
  1030. (define manifest-from-opts
  1031. (options/resolve-packages store opts))
  1032. (define manifest
  1033. (if profile
  1034. (profile-manifest profile)
  1035. manifest-from-opts))
  1036. (when (and profile
  1037. (> (length (manifest-entries manifest-from-opts)) 0))
  1038. (leave (G_ "'--profile' cannot be used with package options~%")))
  1039. (when (null? (manifest-entries manifest))
  1040. (warning (G_ "no packages specified; creating an empty environment~%")))
  1041. ;; Use the bootstrap Guile when requested.
  1042. (parameterize ((%graft? (assoc-ref opts 'graft?))
  1043. (%guile-for-build
  1044. (and store-needed?
  1045. (package-derivation
  1046. store
  1047. (if bootstrap?
  1048. %bootstrap-guile
  1049. (default-guile))))))
  1050. (run-with-store store
  1051. ;; Containers need a Bourne shell at /bin/sh.
  1052. (mlet* %store-monad ((bash (environment-bash container?
  1053. bootstrap?
  1054. system))
  1055. (prof-drv (if profile
  1056. (return #f)
  1057. (manifest->derivation
  1058. manifest system bootstrap?)))
  1059. (profile -> (if profile
  1060. (readlink* profile)
  1061. (derivation->output-path prof-drv)))
  1062. (gc-root -> (assoc-ref opts 'gc-root)))
  1063. ;; First build the inputs. This is necessary even for
  1064. ;; --search-paths. Additionally, we might need to build bash for
  1065. ;; a container.
  1066. (mbegin %store-monad
  1067. (mwhen store-needed?
  1068. (built-derivations (append
  1069. (if prof-drv (list prof-drv) '())
  1070. (if (derivation? bash) (list bash) '()))))
  1071. (mwhen gc-root
  1072. (register-gc-root profile gc-root))
  1073. (mwhen (assoc-ref opts 'check?)
  1074. (return
  1075. (if container?
  1076. (warning (G_ "'--check' is unnecessary \
  1077. when using '--container'; doing nothing~%"))
  1078. (validate-child-shell-environment profile manifest))))
  1079. (cond
  1080. ((assoc-ref opts 'search-paths)
  1081. (show-search-paths profile manifest #:pure? pure?)
  1082. (return #t))
  1083. (container?
  1084. (let ((bash-binary
  1085. (if bootstrap?
  1086. (derivation->output-path bash)
  1087. (string-append (derivation->output-path bash)
  1088. "/bin/sh"))))
  1089. (launch-environment/container #:command command
  1090. #:bash bash-binary
  1091. #:user user
  1092. #:user-mappings mappings
  1093. #:profile profile
  1094. #:manifest manifest
  1095. #:white-list white-list
  1096. #:link-profile? link-prof?
  1097. #:network? network?
  1098. #:map-cwd? (not no-cwd?)
  1099. #:emulate-fhs? emulate-fhs?
  1100. #:nesting? nesting?
  1101. #:symlinks symlinks
  1102. #:setup-hook
  1103. (and emulate-fhs?
  1104. setup-fhs))))
  1105. (else
  1106. (return
  1107. (exit/status
  1108. (launch-environment/fork command profile manifest
  1109. #:white-list white-list
  1110. #:pure? pure?)))))))))))))
  1111. ;;; Local Variables:
  1112. ;;; eval: (put 'with-store/maybe 'scheme-indent-function 1)
  1113. ;;; End: