Compare commits

...

247 commits

Author SHA1 Message Date
Filippo Squillace
0037f96e15 Add warning box for Ubuntu restriction 2024-10-12 11:53:52 +02:00
Filippo Squillace
0242749f8e Replace yay with yay-git 2024-10-12 11:34:38 +02:00
Filippo Squillace
e2d9517a92 Test enabling back the check scripts 2024-10-08 19:47:29 +02:00
Filippo Squillace
452a69cff3 Do not remove gzip during build 2024-10-08 01:10:24 +02:00
Filippo Squillace
7af01ba481 Fix pacman 7 usage during build 2024-10-08 01:04:11 +02:00
Filippo Squillace
e68c24dec6 7.4.9 2024-08-01 20:29:07 +02:00
Filippo Squillace
fd44972137 Disable the checks and fix shellcheck 2024-08-01 20:21:34 +02:00
Filippo Squillace
4d2d0fcb84 7.4.8 2024-01-09 16:41:45 +01:00
Filippo Squillace
c91e013cd4 Copy pacman.conf file if does not exist 2024-01-09 16:32:29 +01:00
Filippo Squillace
2eaf739a29
Merge pull request #339 from cosmojg/patch-2
Explain installation before quickstart/setup
2023-11-15 22:33:52 +01:00
Cosmo
e680325f5a
Explain installation before quickstart/setup
This should clear up some of the confusion that new users experience when discovering JuNest for the first time
2023-11-13 15:29:05 -05:00
Filippo Squillace
6ae9f72dd5 Add Readme update 2023-07-22 16:34:44 +02:00
Filippo Squillace
d19d2f07b6 Add buymeacoffee badge 2023-07-22 16:26:21 +02:00
Filippo Squillace
2dd84efecb 7.4.7 2023-05-31 15:07:39 +02:00
Filippo Squillace
cc1287825d
Merge pull request #332 from fsquillace/userns-check
#328 Check if `user` file exist in `ns` directory
2023-05-31 15:06:25 +02:00
Filippo Squillace
b3565e093d #328 Check if user file exist in ns directory 2023-05-31 14:57:57 +02:00
Filippo Squillace
9a06d47f76 Add shellcheck disable 2023-05-29 12:00:15 +02:00
Filippo Squillace
daa60b05e3 Fix PATH when running trap 2023-05-29 11:47:18 +02:00
Filippo Squillace
507502dc1a 7.4.5 2023-05-07 16:27:17 +02:00
Filippo Squillace
c440252f2a
Merge pull request #326 from fsquillace/wrapper-custom-path
Wrapper custom path
2023-05-07 16:26:22 +02:00
Filippo Squillace
3742c16ce0 Add documentation and unit test 2023-05-07 16:13:10 +02:00
Filippo Squillace
c2dae9021d Add integ test 2023-05-07 14:15:55 +02:00
Filippo Squillace
39508ca8a9 First commit 2023-05-07 13:59:00 +02:00
Filippo Squillace
ae365215fb 7.4.4 2023-05-06 01:38:52 +02:00
Filippo Squillace
874f52f0f8 Remove PATH setting for chroot mode 2023-05-06 00:47:14 +02:00
Filippo Squillace
71397b942b
Merge pull request #325 from escape0707/patch-1
Update README.md to accommodate that sudo-fake provides sudo
2023-05-06 00:27:47 +02:00
Escape0707
296c9e47b4 Update README.md to accommodate that sudo-fake provides sudo
Since junest/sudo-fake now provides sudo, users don't need to ignore core/sudo during the installation of base-devel. They only need to make sure not to install core/sudo manually.
2023-05-06 00:58:20 +09:00
Filippo Squillace
9b00c5c4c5 Reduce PATH when accessing to Junest session 2023-04-30 13:20:54 +02:00
Filippo Squillace
ba0ddcc524 7.4.3 2023-02-18 23:47:20 +01:00
Filippo Squillace
4db85bcf1f #318: Use multi-call wrapper 2023-02-18 00:49:54 +01:00
Filippo Squillace
369cda8dc6 7.4.2 2023-02-18 00:03:19 +01:00
Filippo Squillace
b976f767d5 Add checks for fakeroot chown 2023-02-17 23:49:06 +01:00
Filippo Squillace
95f97a62b7 #305: Add fakeroot variable to bypass chown commands 2023-02-17 18:26:13 +01:00
Filippo Squillace
6048b43218 7.4.1 2023-01-31 22:09:43 +01:00
Filippo Squillace
3fb7676277 Merge branch 'master' into dev 2023-01-31 22:08:36 +01:00
Filippo Squillace
978f64ea0a
Merge pull request #316 from cosmojg/patch-2
Fix deprecated find -perm syntax
2023-01-31 21:51:08 +01:00
Filippo Squillace
48b20848c8
Merge pull request #317 from cosmojg/patch-3
Fix grammar and improve style
2023-01-31 21:50:52 +01:00
Cosmo
632b416955
Fix grammar and improve style 2023-01-31 00:30:41 -05:00
Cosmo
0b34458fb5
Fix deprecated find -perm syntax 2023-01-30 23:51:50 -05:00
Filippo Squillace
2492583877 7.4.0 2023-01-15 23:08:40 +01:00
Filippo Squillace
a9174267b3 Add unit tests 2023-01-15 22:28:08 +01:00
Filippo Squillace
65c45517e1 Bind /run/user directory 2023-01-15 22:03:27 +01:00
Filippo Squillace
20f42f6cb7 7.3.20 2022-12-31 11:17:45 +00:00
Filippo Squillace
76c1644c63 Add discord server link 2022-12-31 11:13:25 +00:00
Filippo Squillace
1c16425b88 7.3.19 2022-12-19 23:15:38 +00:00
Filippo Squillace
fab5b0df2e Remove arch-travis and directly use docker 2022-12-19 23:12:02 +00:00
Filippo Squillace
c052e660ee 7.3.18 2022-12-18 13:43:00 +00:00
Filippo Squillace
30967bc15f Remove -d option in pacstrap 2022-12-18 13:24:29 +00:00
Filippo Squillace
60c7d1c5d0 Update keyring 2022-12-18 12:43:56 +00:00
Filippo Squillace
91f95ae4b5 Change URL to CloudFlare provider 2022-12-18 10:51:00 +00:00
Filippo Squillace
2f6e137e7e 7.3.17 2022-11-05 13:02:35 +01:00
Filippo Squillace
991aa5d7fe
Merge pull request #302 from schance995/extend-path
Extend $PATH in common.sh with system $PATH.
2022-11-05 12:53:53 +01:00
Spencer Skylar Chan
e5f14c3735 Extend $PATH in common.sh with system $PATH.
This lets junest work on non-FHS systems such as GNU Guix.
2022-10-30 00:18:58 -04:00
Filippo Squillace
f0e82bf8bf 7.3.16 2022-10-15 12:50:44 +02:00
Filippo Squillace
56761f6e63 Change url 2022-10-15 12:49:49 +02:00
Filippo Squillace
77acbf5214 7.3.15 2022-08-05 18:06:47 +02:00
Filippo Squillace
799d3bdd42 Install keyring first during the build preparation 2022-08-05 18:06:29 +02:00
Filippo Squillace
a9f5304279 #297 Create bin wrapper for symlinks 2022-08-05 18:06:29 +02:00
Filippo Squillace
df2fec0a36 7.3.14 2022-04-24 20:14:12 +00:00
Filippo Squillace
73b8bec8db Fix wrapper when passing multiple arguments and add unit tests
This change come from the PR #289
2022-04-24 19:54:32 +00:00
Filippo Squillace
98775b538f
Merge pull request #294 from fsquillace/sudoj
Default to ns mode (not fakeroot) for bin wrappers and add sudoj command
2022-04-23 19:32:10 +02:00
Filippo Squillace
0e6aa260ad Default to ns mode (not fakeroot) for bin wrappers and add sudoj command
This is related to #292
2022-04-23 17:11:55 +00:00
Filippo Squillace
21b5e3fabb 7.3.13 2022-03-01 00:43:02 +01:00
Filippo Squillace
0295f37758 Add shellcheck 2022-03-01 00:05:09 +01:00
Filippo Squillace
e7d7056e9c
Merge pull request #291 from fsquillace/create-wrappers
Create wrappers
2022-02-17 00:27:52 +01:00
Filippo Squillace
e794a6150c Add more unit tests 2022-02-16 22:55:40 +01:00
Filippo Squillace
2b9f1839f1 Add create-bin-wrappers command 2022-02-13 20:07:13 +01:00
Filippo Squillace
5630a0f035
Merge pull request #287 from neiser/master
Fix quoting in wrapper script
2022-02-13 11:06:05 +01:00
Andreas Grub
bc543a9285 Fix quoting in wrapper script
The 'eval' introduced in #262 unfortunately breaks command
invocations were a parameter contains spaces, such as

rg "my pattern with spaces"

as it would be called as `rg my pattern with
spaces`. This can be fixed using printf. Using
parameter extension quoting with ${@@Q} only
works with bash >4.4 (but only >4.0 is required).

At least the above invocation then works correctly!
2022-02-12 06:31:18 +01:00
Filippo Squillace
5f7eaff50e 7.3.12 2022-01-08 11:57:53 +01:00
Filippo Squillace
7d8c619ee0 #284 Fix copy command 2022-01-05 23:47:55 +01:00
Filippo Squillace
e6bae0647c Merge branch 'dev' into master 2021-12-31 18:21:42 +01:00
Filippo Squillace
6efc4d0f52 Update VERSION 2021-12-31 18:20:27 +01:00
Filippo Squillace
de63bdf96a Change url main repo 2021-12-31 18:09:10 +01:00
Filippo Squillace
4a1c11d4ed Fix deploy script 2021-12-20 21:19:54 +00:00
Filippo Squillace
a9d11fb4d5
Merge pull request #280 from fsquillace/dev
Dev
2021-12-20 21:57:42 +01:00
Filippo Squillace
5e97524338 Update README with proot updates 2021-12-20 18:49:22 +00:00
Filippo Squillace
14d51d9856 Merge branch 'master' into dev 2021-12-20 18:07:00 +00:00
Filippo Squillace
46cc4aad48
Merge pull request #274 from ayaka14732/patch-2
Fix installation guide in README
2021-11-03 10:34:52 +01:00
Ayaka Mikazuki
4036a7737b
Update README.md 2021-11-03 11:18:42 +08:00
Filippo Squillace
6e872c5f26 Update version and README 2021-10-13 22:33:09 +02:00
Filippo Squillace
7fe9c0e9ec Fix the --backend-command option 2021-10-13 12:23:41 +02:00
Filippo Squillace
38ab252f98 Enable all capabilities in ns fakeroot mode 2021-09-21 14:21:48 +02:00
Filippo Squillace
9ecabcd524 Change mirrorlist, update the initial message, do not inherit PATH variable and use precompiled yay during build phase 2021-09-11 14:44:35 +02:00
Filippo Squillace
86277fd589 Put the original mirror back 2021-09-11 14:44:35 +02:00
Filippo Squillace
e45d76651e First commit 2021-09-11 14:44:33 +02:00
Filippo Squillace
fc3b54e97f Authenticate to Docker hub 2021-06-19 16:46:04 +02:00
Filippo Squillace
e9406123b9 Test travis 2021-06-19 16:08:49 +02:00
Filippo Squillace
08553a447e Remove @ from wrapper 2021-03-24 11:54:17 +01:00
Filippo Squillace
6af7bae418 Fix wrappers using eval #262 2021-03-24 11:49:28 +01:00
Filippo Squillace
700b809fbe 7.3.6 2021-02-16 18:18:24 +01:00
Filippo Squillace
8e560b4a8a Make namespace check more robust and update README 2021-02-16 18:13:27 +01:00
Filippo Squillace
79e4107196 Update VERSION 2021-01-10 13:26:07 +01:00
Filippo Squillace
b8ad2182be Update README 2021-01-10 13:25:49 +01:00
Filippo Squillace
6548e19eb5 Use proper proc,dev,sys binds for ns mode 2021-01-10 13:16:48 +01:00
Filippo Squillace
8d4217492a 7.3.4 2021-01-04 21:10:31 +01:00
Filippo Squillace
f0b6f0962e Use true fakechroot/fakeroot in ns fakeroot mode 2021-01-04 19:21:07 +01:00
Filippo Squillace
4527297be1 Remove warnings when using pacman 2020-12-31 12:57:33 +01:00
Filippo Squillace
6291e5cc83 Use the Junest repo to install package deps 2020-12-30 13:35:21 +01:00
Filippo Squillace
f98fa897bf 7.3.1 2020-12-30 00:59:00 +01:00
Filippo Squillace
d3f9f13ccd Fix deploy 2020-12-30 00:58:24 +01:00
Filippo Squillace
2242585778 Merge branch 'master' into dev 2020-12-29 19:04:38 +01:00
Filippo Squillace
8844f7b5d8
Merge pull request #258 from fsquillace/archlinux-travis
Archlinux travis
2020-12-29 19:03:05 +01:00
Filippo Squillace
d25ae30158 Update README 2020-12-29 19:02:14 +01:00
Filippo Squillace
2aeb23b882 Add tests for wrappers 2020-12-29 13:09:35 +01:00
Filippo Squillace
3d16ee2583 First commit for wrappers 2020-12-29 10:46:59 +01:00
Filippo Squillace
9d2e6e4bbf Add deploy feature 2020-12-28 12:07:19 +01:00
Filippo Squillace
b75faac48f Add remaining junest tests in travis 2020-12-27 18:59:08 +01:00
Filippo Squillace
95479ba41f Use arch-travis in CI 2020-12-26 12:23:11 +01:00
Filippo Squillace
78c4f72bc1
Merge pull request #257 from hodapp512/patch-1
Update README.md for updated bind usage
2020-12-11 11:00:46 +01:00
hodapp512
26e58aaeec
Update README.md for updated bind usage 2020-12-10 11:27:15 -06:00
Filippo Squillace
8609300b20
Merge pull request #254 from fsquillace/zst
Zst
2020-09-15 19:43:07 +02:00
Filippo Squillace
440dde6564 7.2.2 2020-09-15 19:40:16 +02:00
Filippo Squillace
84f4d20370 Disable integ test for ARM 2020-09-15 19:31:24 +02:00
Filippo Squillace
98107ac07f Change format package from xz 2020-09-15 14:06:03 +02:00
Filippo Squillace
7b3685941d 7.2.1 2020-03-28 18:48:51 +01:00
Filippo Squillace
6d7bc4efa4 Add all arch in proot-static package 2020-03-28 16:54:46 +01:00
Filippo Squillace
b0169a8e5f 7.2.0 2020-03-28 15:55:08 +01:00
Filippo Squillace
12e6f6214e Add arm arch to proot 2020-03-28 13:16:40 +01:00
Filippo Squillace
500546a0e2 Fix build and proot 2020-03-28 12:57:03 +01:00
Filippo Squillace
09b9c644d9 Add option to specify location of backend command 2020-03-27 18:39:58 +01:00
Filippo Squillace
348b73ce73 Remove groot content 2020-03-24 21:18:25 +01:00
Filippo Squillace
6592bc9bd2 Separate proot, qemu, groot into packages 2020-03-22 17:53:47 +01:00
Filippo Squillace
d1285908ae
Merge pull request #240 from fsquillace/dev
Fix travis for ARM tests
2020-03-17 14:54:12 +01:00
Filippo Squillace
1580ba9c8e Fix travis for ARM tests 2020-03-16 21:34:29 +01:00
Filippo Squillace
628ac3c655 7.1.0 2020-03-16 20:12:27 +01:00
Filippo Squillace
4b5b7b300b Add test for bwrap command 2020-03-16 18:59:01 +01:00
Filippo Squillace
385dd8c68c Add script to check all tests 2020-03-15 23:49:45 +01:00
Filippo Squillace
6e55e0c648 See mirror URL only for non arm arch 2020-03-15 23:26:25 +01:00
Filippo Squillace
121f2c74f6 Improve build for ARM arch 2020-03-15 17:10:49 +01:00
Filippo Squillace
9bfd7e047d Add bwrap as backend for ns 2020-03-15 13:41:06 +01:00
Filippo Squillace
538e87f036 Add FUNDING file 2020-02-21 23:07:59 +01:00
Filippo Squillace
5b60382ca4
Merge pull request #237 from fsquillace/dev
Dev
2019-12-15 15:46:40 +01:00
Filippo Squillace
7acf16cc73
Merge pull request #236 from fsquillace/fix
Remove interface lo when checking iftop
2019-12-15 15:46:20 +01:00
Filippo Squillace
11a439499b Remove interface lo when checking iftop 2019-12-15 14:31:11 +00:00
Filippo Squillace
e0dd3257a8 Add support for ARM arch during build and check 2019-12-15 11:55:27 +00:00
Filippo Squillace
4bb9b60638 Merge branch 'dev' 2019-12-01 17:11:10 +01:00
Filippo Squillace
606353047c 7.0.2 2019-12-01 17:10:01 +01:00
Filippo Squillace
493b105faa Remove socket filename for gpg agent during build check 2019-12-01 17:06:12 +01:00
Filippo Squillace
85aeda4ac9 Update the check for building image 2019-12-01 17:06:12 +01:00
Filippo Squillace
2e8f5905c7 7.0.1 2019-12-01 17:06:12 +01:00
Filippo Squillace
82b146a57e Add rw permission to files in image 2019-12-01 17:06:12 +01:00
Filippo Squillace
e12cd8d8ff
Merge pull request #233 from soraxas/master
Fix typos in --help message
2019-11-17 10:01:52 +01:00
Oscar Lai
8d91e18ab0 fixed typos in --help message 2019-11-10 02:22:42 +11:00
Filippo Squillace
c5847b8583 7.0.1 2019-10-25 20:48:20 +02:00
Filippo Squillace
27f92d0b3f Add rw permission to files in image 2019-10-25 20:46:27 +02:00
Filippo Squillace
97d19da268
Merge pull request #231 from fsquillace/new-junest
New junest
2019-10-22 22:27:52 +02:00
Filippo Squillace
632aad46bd Small fixes 2019-10-22 22:23:12 +02:00
Filippo Squillace
de0bec9bc5 Remove yaourt package and allow not to copy common etc files 2019-10-20 22:45:40 +02:00
Filippo Squillace
3341187cf6 Change the CLI and make ns the default backend 2019-10-20 16:15:31 +02:00
Filippo Squillace
ef4bf7cd79 6.0.10 2019-08-09 23:59:18 +10:00
Filippo Squillace
00c60118fb Enable AUR tests 2019-08-09 23:42:55 +10:00
Filippo Squillace
30b743cb1b Fix keyring setup for ARM arch 2019-08-09 22:11:49 +10:00
Filippo Squillace
313ef30662 Add aurutils to the check build script 2019-08-09 21:19:37 +10:00
Filippo Squillace
691369068d Update README and change order for PATH variable 2019-06-10 17:33:31 +10:00
Filippo Squillace
fbe49acc01 6.0.9 2018-10-31 21:51:23 +11:00
Filippo Squillace
98c97ddfa7 Merge branch 'dev' 2018-10-31 21:50:34 +11:00
Filippo Squillace
6361522e40 Fix aur validation tests and include sudo-fake in codebase 2018-10-31 20:53:08 +11:00
Filippo Squillace
cc351f419d Add check for disabled unprivileged user namespace 2018-10-28 17:34:45 +11:00
Filippo Squillace
bca7e565ca
Merge pull request #210 from DroidFreak32/patch-1
Fix JuNest image URL
2018-08-11 10:26:31 +10:00
DroidFreak32
34f3458335
Fix JuNest image URL 2018-07-21 18:04:43 +05:30
Filippo Squillace
5172093fe2 6.0.8 2017-09-23 22:35:35 +10:00
Filippo Squillace
5bc4cd245a :doc: x86 (32 bit) is deprecated 2017-09-23 22:33:46 +10:00
Filippo Squillace
0d7468b516 Merge pull request #200 from fsquillace/docs_and_nested_envs
Docs and nested envs
2017-09-18 17:02:06 +02:00
Filippo Squillace
d858d577c8 Fix checks for updating Arch Linux keyrings 2017-09-18 16:59:35 +02:00
Filippo Squillace
f60bbb5c2a Issue #195: Check presence of nested env first 2017-09-18 13:48:34 +02:00
Filippo Squillace
b2ba7f9d5b Issue #197: Differences between JuNest and other systems 2017-09-18 13:45:33 +02:00
Filippo Squillace
4b6fd63cf8 Inform with a warn in case SECCOMP gets disabled 2017-06-23 14:51:29 +02:00
Filippo Squillace
9cd13c88f6 6.0.7 2017-04-25 22:57:37 +01:00
Filippo Squillace
797853bbb4 Merge pull request #192 from fsquillace/activate_aur_test_namespac
Activate aur test namespac
2017-04-25 22:56:41 +01:00
Filippo Squillace
358f92d3e0 Remove package-query as a dependency for building JuNest Image 2017-04-25 22:53:10 +01:00
Filippo Squillace
c3610ec86a Activate the aur check for namespace mode 2017-04-25 09:17:03 +01:00
Filippo Squillace
22483b9ea9 6.0.6 2017-04-25 00:26:17 +01:00
Filippo Squillace
3eada1e4f1 Merge pull request #191 from fsquillace/issue_check_script_fix
Update the system before running checks
2017-04-25 00:25:34 +01:00
Filippo Squillace
8fe650ce87 Update the system before running checks 2017-04-25 00:22:09 +01:00
Filippo Squillace
d6d6883e82 6.0.5 2017-04-24 23:11:48 +01:00
Filippo Squillace
f9f7cbcaaa Update the check script to exclude sudo from base-devel 2017-04-24 23:10:32 +01:00
Filippo Squillace
51f6765634 6.0.4 2017-04-24 21:47:48 +01:00
Filippo Squillace
c2d5985fe3 Merge pull request #190 from fsquillace/issue_184_aur_namespace
Issue #184: Add `sudo-fake` to JuNest image
2017-04-24 09:47:50 +01:00
Filippo Squillace
7ebdf628cf Issue #184: Add sudo-fake to JuNest image 2017-04-24 09:21:40 +01:00
Filippo Squillace
35fce94c4e 6.0.3 2017-04-20 20:23:18 +01:00
Filippo Squillace
73b4b85399 Merge pull request #188 from fsquillace/issue_replace_mountpoint
Replace `mountpoint` command by checking mounts file
2017-04-20 20:20:18 +01:00
Filippo Squillace
bd0e9fbbcc Replace mountpoint command by checking mounts file
`mountpoint` does not detect some directory. By checking directly
from `/proc/self/mounts` groot can be more reliable and portable.
2017-04-20 20:19:04 +01:00
Filippo Squillace
137788a98a Groot: Do not umount directories that are not mountpoint 2017-04-19 23:52:26 +01:00
Filippo Squillace
b9e34d3e96 6.0.2 2017-04-19 22:36:09 +01:00
Filippo Squillace
7b2dee540f 🐛 Fix location of the check script during image build 2017-04-19 22:35:03 +01:00
Filippo Squillace
a2c40902a2 6.0.1 2017-04-19 21:19:55 +01:00
Filippo Squillace
9849062292 Merge pull request #187 from fsquillace/issue_fix_groot
Add -i and -r options for groot command
2017-04-19 21:18:40 +01:00
Filippo Squillace
03a29590de Add -i and -r options for groot command 2017-04-19 21:04:51 +01:00
Filippo Squillace
02650b754e 6.0.0 2017-04-18 20:51:37 +01:00
Filippo Squillace
3456857d7a Merge pull request #183 from fsquillace/issue_174_namespace
Issue 174 namespace
2017-04-18 12:38:10 +01:00
Filippo Squillace
665c45b7aa Issue #182: Update doc with comparison table on the execution modes 2017-04-18 09:11:32 +01:00
Filippo Squillace
0060658726 Issue #174: Add util-linux in JuNest image
util-linux contains `unshare` which is needed for the namespace mode.
2017-04-12 20:43:35 +01:00
Filippo Squillace
b817aa8445 Issue #174: Add -g option for Groot and integ tests for user namespace 2017-04-12 20:43:30 +01:00
Filippo Squillace
fcb4a36f30 Issue #174: Add option -n in groot and umount directories in order 2017-04-12 20:43:21 +01:00
Filippo Squillace
8e5531a27a Issue #174: Add unit test for namespace 2017-04-12 20:43:12 +01:00
Filippo Squillace
0f7fd33c53 Issue #174: Add new program GRoot and change version option for JuNest!
This replaces jchroot because GRoot can be also used in a user namespace environment.
2017-04-12 20:42:56 +01:00
Filippo Squillace
426b708d2d Issue #174: Rename common functions and define skeleton for namespace functions 2017-04-12 20:42:35 +01:00
Filippo Squillace
0ec35a4088 Issue #174: Add namespace module and check for user namespace 2017-04-12 20:42:10 +01:00
Filippo Squillace
f85c62274f 5.6.12 2017-03-18 15:56:14 +00:00
Filippo Squillace
2ed75c8545 🐛 Fix jchroot according to refactor 2017-03-18 15:55:03 +00:00
Filippo Squillace
7a6ef3f0f9 5.6.11 2017-03-18 15:34:35 +00:00
Filippo Squillace
3111c4253d Merge pull request #178 from fsquillace/fix-url
Create the proper S3 bucket for JuNest
2017-03-18 15:31:52 +00:00
Filippo Squillace
72640b3ca3 Create the proper S3 bucket for JuNest 2017-03-18 14:53:48 +00:00
Filippo Squillace
b3a41d1d52 5.6.10 2017-03-16 21:34:03 +00:00
Filippo Squillace
97b4a7b40d Merge pull request #177 from fsquillace/fix-url
Move to S3 service for hosting the JuNest images
2017-03-16 21:32:25 +00:00
Filippo Squillace
adcfe7c8dc Move to S3 service for hosting the JuNest images
Dropbox Public folder became private:
https://www.dropbox.com/help/16
2017-03-16 19:40:54 +00:00
Filippo Squillace
b58c946c45 Merge pull request #176 from fsquillace/issue_174_namespace
Issue #174: 🎨 Change structure of core modules
2017-03-13 21:42:45 +00:00
Filippo Squillace
ddc7ede70a Issue #174: 🎨 Change structure of core modules
This change separate different parts of the code into several categories:
`chroot`, `proot`, `build`, `common`, `setup`. This should simplify the
maintenance of the code and will help introducing the user namespace module
in a easier way.
2017-03-13 21:06:18 +00:00
Filippo Squillace
54a09245d2 Merge pull request #175 from cfriesicke/issue_172_recommended_kernel
Recommend minimum kernel 2.6.32; add Q&A entry for private futex warning
2017-03-11 17:22:13 +00:00
Christian Friesicke
bee9e2df54 📝 Add Q&A entry about private futex warning
Since this problem can be pin-pointed to kernel versions that are older
than 2.6.22, a Q&A entry separate from the more general "Kernel too
old" problem may be justified.

This Q&A entry discusses the origin of the problem, some of the
symptoms (no symptoms for "simple" programs like vim, but warnings and
crashes for others), and concludes with a remark about possible (but
complicated) workarounds.
2017-03-11 17:45:54 +01:00
Christian Friesicke
978ab8d408 📝 Recommend kernel version 2.6.32
Reasoning: the pre-compiled binaries that are downloaded by JuNest
during installation as well as binaries that are available from the Arch
repositories are compiled for Linux kernel 2.6.32. Therefore, with
kernel version 2.6.32 on the host OS no warnings, errors, or unexpected
crashes due to kernel version incompatibilities should be expected.

The recommended kernel version is now mentioned in the "Dependencies"
section of the README. In the "Troubleshooting" section about "Kernel
too old", more specific information about the problem and the workaround
using PRoot -k option is added.

Resolves: #172
2017-03-11 17:33:22 +01:00
Filippo Squillace
615e20eff8 Merge pull request #173 from fsquillace/issue_fixes
Substitute bindings with explicit copy of files
2017-03-11 10:48:13 +00:00
Filippo Squillace
d07b06b39d Substitute bindings with explicit copy of files 2017-03-11 10:17:55 +00:00
Filippo Squillace
46221f26b1 Merge pull request #166 from fsquillace/passwd_user
Passwd user
2016-11-27 19:49:24 +00:00
Filippo Squillace
6568430add Mitigate user missing from getent enumeration
Sometimes `getent passwd` does not give the user information.
This change tries to at least get the current user via
`getent passwd $USER` since it uses
a more reliable and faster system call (getpwnam(3)).

Reference:
be748493a3
2016-11-27 12:49:09 +00:00
Filippo Squillace
6d4e5f7404 5.6.9 2016-06-09 22:14:52 +01:00
Filippo Squillace
56a5f48b32 Use the new junest-git package 2016-06-09 21:14:35 +01:00
Filippo Squillace
55a4ffd78a 5.6.8 2016-06-07 23:24:49 +01:00
Filippo Squillace
33214edcef Merge pull request #152 from fsquillace/issue_81_passwd
Issue 81 passwd
2016-06-07 22:45:37 +01:00
fsquillace
846bcc9c1f Issue #81: Use getent to build the passwd and group files 2016-06-07 22:24:28 +01:00
fsquillace
147798117b 📝 Fix path to unit tests 2016-05-31 20:27:27 +01:00
Filippo Squillace
6084218598 Merge pull request #151 from fsquillace/add_logo
📝 Add logo and refactor README.md
2016-05-31 19:00:18 +01:00
fsquillace
709ceda12f 📝 Add logo and refactor README.md 2016-05-31 18:26:43 +01:00
Filippo Squillace
e4e43f004d Merge pull request #150 from fsquillace/issue_141_refactor
Issue 141 refactor
2016-05-31 09:07:53 +01:00
fsquillace
c114590ea9 📝 Update README for installation process and change the bash version 2016-05-30 23:01:28 +01:00
fsquillace
374cb2e42e Move the unit tests to a specific folder 2016-05-30 23:01:28 +01:00
fsquillace
195d8d0069 Update travis 2016-05-30 23:01:28 +01:00
fsquillace
1f66d810cd Remove check for mtab file and lock directory 2016-05-30 23:01:28 +01:00
fsquillace
3102ff1d8a 🎨 Refactor core.sh code 2016-05-30 23:01:28 +01:00
fsquillace
19f4848a9d 🎨 Substitute tab chars with whitespace 2016-05-30 23:01:28 +01:00
fsquillace
ce3fefeab5 Issue #141: 🎨 Refactor code 2016-05-30 23:01:26 +01:00
Filippo Squillace
b7f10dc55b 📝 Update CONTRIBUTING.md and change unit tests file names 2016-05-11 22:40:12 +01:00
Filippo Squillace
360995492c Merge pull request #139 from fsquillace/issue_138_pkgfile
Issue #138: pkgfile documentation
2016-02-26 23:31:30 +00:00
Filippo Squillace
ea3af255cf Issue #138: pkgfile documentation 2016-02-26 23:21:52 +00:00
Filippo Squillace
421d8034c0 Merge pull request #136 from fsquillace/contributing
Contributing
2016-01-30 12:39:27 +00:00
Filippo Squillace
d36093b0f5 Change the name of proot variable 2016-01-30 12:14:44 +00:00
Filippo Squillace
0ecd1b297b Add CONTRIBUTING.md file 2016-01-30 12:14:36 +00:00
builder
846b47ff3a Small fix in usage function 2015-12-26 15:56:07 +00:00
builder
c10c8713bb Fix proot arguments help 2015-12-26 13:31:24 +00:00
Filippo Squillace
acfaed0dd4 Merge pull request #133 from fsquillace/issue_127_mtab
Issue #127: Do not let proot mount the mtab file
2015-12-26 12:57:03 +00:00
builder
36212d3cbf Issue #127: Do not let proot mount the mtab file
This change prevents inconsistency problems when the JuNest environment
is run simultaneously in fakeroot and normal user. The mtab file is
never mount by proot but, instead, it is symlinked to /proc/self/mounts.
2015-12-26 12:55:19 +00:00
Filippo Squillace
3beab7fc74 Merge pull request #132 from adamcavendish/patch-1
Add a message for showing proot arguments help
2015-12-13 20:19:06 +00:00
Adam Basfop Cavendish
6116585fb8 Add a message for showing proot arguments help 2015-12-14 01:14:45 +08:00
Filippo Squillace
aaa70898ad Merge pull request #129 from fsquillace/gratipay-badge
Gratipay badge and LANG fix
2015-12-01 20:35:46 +00:00
Filippo Squillace
c6bb53e2cc Issue #128: Fix the /etc/locale.conf file 2015-12-01 19:52:11 +00:00
Filippo Squillace
336652ccf6 Add gratipay badge 2015-10-30 21:37:14 +00:00
41 changed files with 4308 additions and 1743 deletions

2
.github/FUNDING.yml vendored Normal file
View file

@ -0,0 +1,2 @@
github: fsquillace
custom: https://github.com/fsquillace/junest/blob/master/README.md#donating

3
.gitignore vendored
View file

@ -1 +1,4 @@
*.swp
*pkg.tar.*
*.tar.gz
*.SRCINFO

View file

@ -1,17 +1,62 @@
language: bash
sudo: required
os: linux
cache:
directories:
- ~/.ccache
- ~/.pkg-cache
services:
- docker
env:
matrix:
- TRAVIS_BASH_VERSION="4.0"
before_install:
- ./ci/install-bash.sh "$TRAVIS_BASH_VERSION"
- sudo apt-get update
- sudo apt-get -y install awscli
install:
- PATH=$PWD/bin:$PATH
- junest -- echo "Installing JuNest (\$(uname -m))"
- JUNEST_HOME=~/.junest-arm junest -a arm -- echo "Installing JuNest (\$(uname -m))"
# TODO: Remember to enable x86 tests when fixed
#- JUNEST_HOME=~/.junest-x86 junest -a x86 -- echo "Installing JuNest (\$(uname -m))"
- PATH=$PWD/bin:$PATH
script:
- ./tests/test_all.sh
- junest --check ./bin/junest
- yes | junest --delete
- JUNEST_HOME=~/.junest-arm junest --check ./bin/junest --skip-root-tests
- yes | JUNEST_HOME=~/.junest-arm junest --delete
#######################
# Unit Tests
#######################
- bash --version
- bash ./tests/checkstyle/checkstyle.sh
- bash ./tests/unit-tests/unit-tests.sh
- shellcheck bin/junest lib/**/*.sh ci/*.sh tests/**/*.sh
# ARM with qemu does seem to work properly. Disabling integ tests for ARM for now.
#- export JUNEST_HOME=~/.junest-arm
#- junest setup --arch arm
#- junest proot --fakeroot -- echo "Installing JuNest (\$(uname -m))"
#- junest proot --fakeroot -- ${PWD}/lib/checks/check.sh --skip-aur-tests
#- junest proot -- ${PWD}/lib/checks/check.sh --skip-aur-tests --use-sudo
#- yes | junest setup --delete
#######################
# Build and validation
#######################
- echo "$DOCKER_PASSWORD" | docker login --username "$DOCKER_USERNAME" --password-stdin
- docker run --rm -v "$(pwd):/build" -v ~/.ccache:/home/travis/.ccache -v ~/.pkg-cache:/var/cache/pacman/pkg --privileged archlinux:latest bash /build/ci/build_image.sh
- "echo pacman pkg cache size: $(du -h ~/.pkg-cache|cut -f1) in $(ls ~/.pkg-cache|wc -l) files"
- ls -l
# Test the newly created JuNest image against Ubuntu host
- export JUNEST_HOME=~/.junest
- junest setup -i junest-x86_64.tar.gz
# TODO The check does not work at the moment: https://app.travis-ci.com/github/fsquillace/junest/builds/271706037
# Disabling it in order to avoid having stale version of junest images.
- ${PWD}/lib/checks/check_all.sh
- yes | junest setup --delete
after_success:
#######################
# Deploy and validation
#######################
- ./ci/deploy.sh ./junest-x86_64.tar.gz

260
CONTRIBUTING.md Normal file
View file

@ -0,0 +1,260 @@
Contributing to JuNest
=====================
First off, thanks for taking the time to contribute!
The following is a set of guidelines for contributing to JuNest.
These are just guidelines, not rules, use your best judgment and
feel free to propose changes to this document in a pull request.
**Table of Contents**
- [How Can I Contribute?](#how-can-i-contribute)
- [Reporting Bugs](#reporting-bugs)
- [Suggesting Enhancements](#suggesting-enhancements)
- [Your First Code Contribution](#your-first-code-contribution)
- [Styleguides](#styleguides)
- [Git Commit Messages](#git-commit-messages)
- [Documentation Styleguide](#documentation-styleguide)
- [Shell Styleguide](#shell-styleguide)
- [Versioning](#versioning)
## How Can I Contribute? ##
### Reporting Bugs ###
This section guides you through submitting a bug report for JuNest.
#### Before submitting a bug report ####
You might be able to find the cause of the problem and fix things yourself.
- **Check the [troubleshooting section](https://github.com/fsquillace/junest#troubleshooting)**
- **Check if you can reproduce the problem with the latest version of JuNest**
- **Check for [existing open/closed issues](https://github.com/fsquillace/junest/issues?utf8=%E2%9C%93&q=is%3Aissue)**
- If the bug has already been suggested, add a comment to the existing issue instead of opening a new one.
#### How Do I Submit A (Good) Bug Report? ####
Bugs are tracked as [GitHub issues](https://guides.github.com/features/issues/) in the [JuNest issues page](https://github.com/fsquillace/junest/issues).
Explain the problem and include additional details to help maintainers reproduce the problem:
* **Use a clear and descriptive title** for the issue to identify the problem.
* **Describe the exact steps which reproduce the problem** in as many details as possible. For example, start by explaining how you started JuNest, e.g. which command exactly you used in the terminal. When listing steps, **don't just say what you did, but explain how you did it**. For example.
* **Provide specific examples to demonstrate the steps**. Include links to files or GitHub projects, or copy/pasteable snippets, which you use in those examples. If you're providing snippets in the issue, use [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
* **Describe the behavior you observed after following the steps** and point out what exactly is the problem with that behavior.
* **Explain which behavior you expected to see instead and why.**
* **Put the bug label to the issue.**
Include details about your configuration and environment:
* **Which version of JuNest are you using?**
* **What's the name and version of the OS you're using**?
* **Are you running JuNest in a virtual machine?** If so, which VM software are you using and which operating systems and versions are used for the host and the guest?
* **Which packages do you have installed?** You can get that list by running `pacman -Qq`.
#### Template For Submitting Bug Reports ####
[Short description of problem here]
**Reproduction Steps:**
1. [First Step]
2. [Second Step]
3. [Other Steps...]
**Expected behavior:**
[Describe expected behavior here]
**Observed behavior:**
[Describe observed behavior here]
**JuNest version:** [Enter JuNest version here]
**OS and version:** [Enter OS name and version here]
**Installed packages:**
[List of installed packages here]
**Additional information:**
* Problem started happening recently, didn't happen in an older version of JuNest: [Yes/No]
* Problem can be reliably reproduced, doesn't happen randomly: [Yes/No]
### Suggesting Enhancements ###
This section guides you through submitting an enhancement suggestion for JuNest, including completely new features and minor improvements to existing functionality.
#### Before Submitting An Enhancement Suggestion ####
* **Check if you're using the latest version of JuNest**
- **Check for [existing open/closed issues](https://github.com/fsquillace/junest/issues?utf8=%E2%9C%93&q=is%3Aissue)**
- If enhancement has already been suggested, add a comment to the existing issue instead of opening a new one.
#### How Do I Submit A (Good) Enhancement Suggestion? ####
Enhancement suggestions are tracked as [GitHub issues](https://guides.github.com/features/issues/) in the [JuNest issues page](https://github.com/fsquillace/junest/issues).
Create an issue on that repository and provide the following information:
* **Use a clear and descriptive title** for the issue to identify the suggestion.
* **Provide a step-by-step description of the suggested enhancement** in as many details as possible.
* **Provide specific examples to demonstrate the steps**. Include copy/pasteable snippets which you use in those examples, as [Markdown code blocks](https://help.github.com/articles/markdown-basics/#multiple-lines).
* **Describe the current behavior** and **explain which behavior you expected to see instead** and why.
* **Specify which version of JuNest you're using.**
* **Specify the name and version of the OS you're using.**
* **Put the enanchement label to the issue.**
#### Template For Submitting Enhancement Suggestions ####
[Short description of suggestion]
**Steps which explain the enhancement**
1. [First Step]
2. [Second Step]
3. [Other Steps...]
**Current and suggested behavior**
[Describe current and suggested behavior here]
**Why would the enhancement be useful to most users**
[Explain why the enhancement would be useful to most users]
[List some other text editors or applications where this enhancement exists]
**JuNest Version:** [Enter JuNest version here]
**OS and Version:** [Enter OS name and version here]
### Your First Code Contribution ###
All JuNest issues are tracked as [GitHub issues](https://guides.github.com/features/issues/) in the [JuNest issues page](https://github.com/fsquillace/junest/issues).
#### Pull Requests ####
* Fork the repo and create your feature branch from ***dev***.
* If you make significant changes, please add tests too.
Get familiar with [shunit](https://github.com/kward/shunit2).
* If you've changed APIs, please update the documentation
* Follow the [Shell styleguide](#shell-styleguide).
* Document new code based on the
[Documentation Styleguide](#documentation-styleguide).
* End files with a newline.
* Follow the [Git commit messages](#git-commit-messages).
* Send a [GitHub Pull Request to JuNest](https://github.com/fsquillace/junest/compare/dev...) with a clear list of what you've done (read more about [pull requests](http://help.github.com/pull-requests/)).
* Put **dev as the base branch** and NOT the master one.
#### Unit Tests ####
To run unit tests:
```sh
./tests/unit-tests/unit-tests.sh
```
#### Integration Tests ####
Generally, there is no need to run integration tests locally
since [Travis](https://travis-ci.org/fsquillace/junest) will run as
soon as the pull request gets created.
## Styleguides ##
### Git Commit Messages ###
* Follow the [seven rules](http://chris.beams.io/posts/git-commit/#seven-rules) of a great Git commit message
* Reference issues and pull requests liberally
* Consider starting the commit message with an applicable emoji:
* :art: `:art:` when improving the format/structure of the code
* :racehorse: `:racehorse:` when improving performance
* :non-potable_water: `:non-potable_water:` when plugging memory leaks
* :memo: `:memo:` when writing docs
* :penguin: `:penguin:` when fixing something on Linux
* :apple: `:apple:` when fixing something on Mac OS
* :checkered_flag: `:checkered_flag:` when fixing something on Windows
* :bug: `:bug:` when fixing a bug
* :fire: `:fire:` when removing code or files
* :green_heart: `:green_heart:` when fixing the CI build
* :white_check_mark: `:white_check_mark:` when adding tests
* :lock: `:lock:` when dealing with security
* :arrow_up: `:arrow_up:` when upgrading dependencies
* :arrow_down: `:arrow_down:` when downgrading dependencies
* :shirt: `:shirt:` when removing linter warnings
* :package: `:package:` when bumping the version
### Documentation Styleguide ###
* Use [Markdown](https://daringfireball.net/projects/markdown).
### Shell Styleguide ###
* Use [google shell styleguide](https://google.github.io/styleguide/shell.xml)
#### Function documentation ####
For function documentation follows the example below:
#######################################
# Cleanup files from the backup dir.
#
# Globals:
# VAR1 (RO,bool) : `my_func` access to VAR1.
# VAR2 (WO) : `my_func` change the value of VAR2.
# VAR3 (RW) : `my_func` read and write to VAR3.
# Arguments:
# arg1 ($1,int) : Directory to cleanup.
# arg2 ($2-) : Command to execute for the cleanup.
# Returns:
# 0 : Cleanup completed successfully.
# 101 : Backup directory is not readable.
# $NOT_DIR_ERROR : Backup directory is not a directory.
# Output:
# None
#######################################
my_func() {
local arg1=$1
shift
local arg2=$@
...
}
The documentation is divided by a description of the function, a `Globals`,
`Arguments`, `Returns` and `Output` sections. If a section does not need details
use the word `None` inside of it.
`Globals` section provides all global variables that interact with the function.
`Arguments` section provides the list of arguments to pass to the function. Use
the parenthesis to indicate the position of the arguments:
- `$1` : Argument is in position one.
- `$2-` : Argument takes all args from position two up to the end.
- `$@` : Argument takes all args.
- `$3?` : Argument is optional.
Variables defined in `Globals` and `Arguments` sections can have the following
types:
- `int` : Integer variable.
- `str` : String variable (default).
- `bool` : Bool variable.
`Returns` section contains the exit status of the function.
`Output` section describe the string printed to stdout.
## Versioning ##
* JuNest uses the following [semantic versioning](http://semver.org/)
### Public API ###
The public API refers to the following parts of JuNest system:
- JuNest script CLI
Any potential code change that cause backwards incompatible changes to the
public API requires the major version to be incremented.

11
LICENSE
View file

@ -1,4 +1,6 @@
GNU GENERAL PUBLIC LICENSE
GNU GENERAL PUBLIC LICENSE
Version 3, 29 June 2007
Copyright (C) 2007 Free Software Foundation, Inc. <http://fsf.org/>
@ -631,8 +633,8 @@ to attach them to the start of each source file to most effectively
state the exclusion of warranty; and each file should have at least
the "copyright" line and a pointer to where the full notice is found.
{one line to give the program's name and a brief idea of what it does.}
Copyright (C) {year} {name of author}
JuNest: The Arch Linux based distro that runs upon any Linux distros without root access
Copyright (C) 2014 Filippo Squillace
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
@ -652,7 +654,7 @@ Also add information on how to contact you by electronic and paper mail.
If the program does terminal interaction, make it output a short
notice like this when it starts in an interactive mode:
{project} Copyright (C) {year} {fullname}
JuNest Copyright (C) 2014 Filippo Squillace
This program comes with ABSOLUTELY NO WARRANTY; for details type `show w'.
This is free software, and you are welcome to redistribute it
under certain conditions; type `show c' for details.
@ -673,3 +675,4 @@ the library. If this is what you want to do, use the GNU Lesser General
Public License instead of this License. But first, please read
<http://www.gnu.org/philosophy/why-not-lgpl.html>.

668
README.md
View file

@ -1,120 +1,320 @@
JuNest
======
The Arch Linux based distro that runs upon any Linux distros without root access.
> [!IMPORTANT]
> Starting from Ubuntu 23.10+, [unprivileged user namespaces has been restricted](https://ubuntu.com/blog/ubuntu-23-10-restricted-unprivileged-user-namespaces).
> If using JuNest within Ubuntu, you may need root privileges in order to enable it.
> Alternatively, you can access JuNest using the `proot` mode as described
> [below](#Proot-based).
The lightweight Arch Linux based distro that runs, without root privileges, on top of any other Linux distro.
<h1 align="center">
<a href="https://github.com/fsquillace/junest"><img
alt="JuNest"
width=250px
src="https://cdn.rawgit.com/fsquillace/junest-logo/master/junest.svg"></a>
</h1>
|Project Status|Donation|Communication|
|:------------:|:------:|:-----------:|
| [![Build status](https://api.travis-ci.org/fsquillace/junest.png?branch=master)](https://travis-ci.org/fsquillace/junest) [![OpenHub](https://www.openhub.net/p/junest/widgets/project_thin_badge.gif)](https://www.openhub.net/p/junest) | [![PayPal](https://img.shields.io/badge/PayPal-Donate%20a%20beer-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8LEHQKBCYTACY) | [![Join the gitter chat at https://gitter.im/fsquillace/junest](https://badges.gitter.im/Join%20Chat.svg)](https://gitter.im/fsquillace/junest?utm_source=badge&utm_medium=badge&utm_campaign=pr-badge&utm_content=badge) [![Join the IRC chat at https://webchat.freenode.net/?channels=junest](https://img.shields.io/badge/IRC-JuNest-yellow.svg)](https://webchat.freenode.net/?channels=junest) [![Join the group at https://groups.google.com/d/forum/junest](https://img.shields.io/badge/Google Groups-JuNest-red.svg)](https://groups.google.com/d/forum/junest) [![RSS](https://img.shields.io/badge/RSS-News-orange.svg)](http://fsquillace.github.io/junest-site/feed.xml) |
| [![Build status](https://api.travis-ci.com/fsquillace/junest.png?branch=master)](https://app.travis-ci.com/github/fsquillace/junest) [![OpenHub](https://www.openhub.net/p/junest/widgets/project_thin_badge.gif)](https://www.openhub.net/p/junest) | [![Github Sponsors](https://img.shields.io/badge/GitHub-Sponsors-orange.svg)](https://github.com/sponsors/fsquillace) [![PayPal](https://img.shields.io/badge/PayPal-Donation-blue.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=8LEHQKBCYTACY) [![Buy me a coffee](https://www.buymeacoffee.com/assets/img/custom_images/yellow_img.png)](https://www.buymeacoffee.com/fsquillace) | [![Join the Discord server at https://discord.gg/ttfBT7MKve](https://img.shields.io/badge/Discord-Server-blueviolet.svg)](https://discord.gg/ttfBT7MKve) |
**Table of Contents**
- [Description](#description)
- [Quickstart](#quickstart)
- [Installation](#installation)
- [Dependencies](#dependencies)
- [Usage](#usage)
- [Advanced usage](#advanced-usage)
- [Internals](#internals)
- [Troubleshooting](#troubleshooting)
- [More documentation](#more documentation)
- [License](#license)
- [Author](#author)
- [WWW](#www)
- [More documentation](#more-documentation)
- [Contributing](#contributing)
- [Donating](#donating)
- [Authors](#authors)
Description
===========
**JuNest** (Jailed User NEST) is a lightweight Arch Linux based distribution that allows to have
an isolated GNU/Linux environment inside any generic host GNU/Linux OS
and without the need to have root privileges for installing packages.
**JuNest** (Jailed User Nest) is a lightweight Arch Linux based distribution
that allows the creation of disposable and partially isolated GNU/Linux environments
within any generic GNU/Linux host OS and without requiring root
privileges to install packages.
JuNest contains mainly the package managers (called [pacman](https://wiki.archlinux.org/index.php/Pacman) and [yaourt](https://wiki.archlinux.org/index.php/Yaourt)) that allows to access
JuNest is built around [pacman](https://wiki.archlinux.org/index.php/Pacman),
the Arch Linux package manager, which allows access
to a wide range of packages from the Arch Linux repositories.
The main advantages on using JuNest are:
The main advantages of using JuNest include:
- Install packages without root privileges.
- Isolated environment in which you can install packages without affecting a production system.
- Access to a wide range of packages in particular on GNU/Linux distros that may contain limited repositories (such as CentOS and RedHat).
- Available for x86\_64, x86 and ARM architectures but you can build your own image from scratch too!
- Run on a different architecture from the host OS via QEMU
- All Arch Linux lovers can have their favourite distro everywhere!
- Create partially isolated environments in which you can install packages without risking mishaps on production systems.
- Access a wider range of packages, particularly on GNU/Linux distros with comparatively limited repositories (such as CentOS and Red Hat).
- Run on a different architecture from the host OS via QEMU.
- Available for `x86_64` and `arm` architectures but you can build your own image from scratch too!
- All Arch Linux lovers can enjoy their favourite distro everywhere!
JuNest follows the [Arch Linux philosophy](https://wiki.archlinux.org/index.php/The_Arch_Way).
Quickstart
==========
There are three different ways you can run JuNest:
How different is JuNest from Docker and Vagrant?
------------------------------------------------
Although JuNest sounds similar to a virtualisation/Linux container-like system,
JuNest is quite different from solutions like Docker or Vagrant.
In fact, the purpose of JuNest is **not** to
build a completely isolated environment but, conversely, to provide the ability to run
programs as if they were running natively from the host OS. Almost everything is shared
between the host OS and the JuNest sandbox (kernel, process subtree, network, mounting, etc)
and only the root filesystem gets isolated
(since the programs installed in JuNest need to reside elsewhere).
- As normal user - Allow to make basic operations: ```junest```
- As fakeroot - Allow to install/remove packages: ```junest -f```
- As root - Allow to have fully root privileges inside JuNest environment (you need to be root for executing this): ```junest -r```
If the JuNest image has not been downloaded yet, the script will download
the image and will place it to the default directory ~/.junest.
You can change the default directory by changing the environment variable *JUNEST\_HOME*.
If you are new on Archlinux and you are not familiar with *pacman* package manager
visit the [pacman rosetta page](https://wiki.archlinux.org/index.php/Pacman_Rosetta).
This allows interaction between processes belonging to both host OS and JuNest.
For example, you can install the `top` command in JuNest and use it to monitor
processes belonging to the host OS.
Installation
============
JuNest can works on GNU/Linux OS with kernel version greater or equal
2.6.0 (JuNest was not tested on kernel versions older than this) on 64 bit, 32 bit and ARM architectures.
## Method one (Recommended) ##
Just clone the JuNest repo somewhere (for example in ~/junest):
## Dependencies ##
JuNest comes with a very short list of dependencies in order to be installed in most
of GNU/Linux distributions.
Before installing JuNest be sure that all dependencies are properly installed in your system:
git clone git://github.com/fsquillace/junest ~/junest
export PATH=~/junest/bin:$PATH
- [bash (>=4.0)](https://www.gnu.org/software/bash/)
- [GNU coreutils](https://www.gnu.org/software/coreutils/)
## Installation from git repository ##
Just clone the JuNest repo somewhere (for example in ~/.local/share/junest):
```sh
git clone https://github.com/fsquillace/junest.git ~/.local/share/junest
export PATH=~/.local/share/junest/bin:$PATH
```
Optionally you want to use the wrappers to run commands
installed in JuNest directly from host:
```sh
export PATH="$PATH:~/.junest/usr/bin_wrappers"
```
Update your `~/.bashrc` or `~/.zshrc` to get always the wrappers available.
### Installation using AUR (Arch Linux only) ###
If you are using an Arch Linux system you can, alternatively, install JuNest from the [AUR repository](https://aur.archlinux.org/):
If you are using an Arch Linux system you can, alternatively, install JuNest from the [AUR repository](https://aur.archlinux.org/packages/junest-git/).
JuNest will be located in `/opt/junest/`
yaourt -S junest-git
export PATH=/opt/junest/bin:$PATH
Quickstart
==========
## Method two ##
Alternatively, another installation method would be to directly download the JuNest image and place it to the default directory ~/.junest:
Setup environment
-----------------
ARCH=<one of "x86_64", "x86", "arm">
mkdir ~/.junest
curl https://dl.dropboxusercontent.com/u/42449030/junest/junest-${ARCH}.tar.gz | tar -xz -C ~/.junest
export PATH=~/.junest/opt/junest/bin:$PATH
The first operation required is to install the JuNest environment in the
location of your choice via `JUNEST_HOME` environment variable
(it must contain an absolute path) which by
default is `~/.junest`:
Dependencies
============
JuNest comes with a very short list of dependencies in order to be installed in most
of GNU/Linux distributions. The needed executables in the host OS are:
```sh
junest setup
```
- bash
- chown (for root access only)
- ln
- mkdir
- rm
- tar
- uname
- wget or curl
The script will download the image from the repository and will place it to the default directory `~/.junest`.
The minimum recommended linux kernel is 2.6.0+
Access to environment
---------------------
JuNest uses the Linux namespaces (aka `ns`) as the default backend program. To access via `ns` just type:
```sh
junest
```
You can use the command `sudo` to acquire fakeroot privileges and
install/remove packages.
Alternatively, you can access fakeroot privileges without using `sudo` all the
time with the `-f` (or `--fakeroot`) option:
```sh
junest -f
```
Another execution mode is via [Proot](https://wiki.archlinux.org/index.php/Proot):
```sh
junest proot [-f]
```
There are multiple backend programs, each with its own pros/cons.
To know more about the JuNest execution modes depending on the backend program
used, see the [Usage](#usage) section below.
Run JuNest installed programs directly from host OS
---------------------------------------
Programs installed within JuNest can be accessible directly from host machine
without entering into a JuNest session
(namely, no need to call `junest` command first).
For instance, supposing the host OS is an Ubuntu distro you can directly
run `pacman` by simply updating the `PATH` variable:
```sh
export PATH="$PATH:~/.junest/usr/bin_wrappers"
sudoj pacman -S htop
htop
```
By default the wrappers use `ns` mode. To use the `ns --fakeroot` you can use the convenient command helper `sudoj`.
For more control on backend modes you can use the `JUNEST_ARGS` environment variable too.
For instance, if you want to run `iftop` with real root privileges:
```
sudoj pacman -S iftop
sudo JUNEST_ARGS="groot" iftop
```
Bin wrappers can be always recreated (e.g. in case for some reasons they get
corrupted) with:
```
junest create-bin-wrappers -f
```
Bin wrappers are automatically generated each time they get installed inside JuNest.
This only works for executables located in `/usr/bin` path.
For executables in other locations (say `/usr/mybinpath`) you can only create
wrappers manually by executing the command:
```
junest create-bin-wrappers --bin-path /usr/mybinpath
```
Obviously, to get access to the corresponding bin wrappers you will need to
update your `PATH` variable accordingly:
```
export PATH="$PATH:~/.junest/usr/mybinpath_wrappers"
```
Install packages from AUR
-------------------------
In `ns` mode, you can easily install package from [AUR](https://aur.archlinux.org/) repository
using the already available [`yay`](https://aur.archlinux.org/packages/yay/)
command. In `proot` mode, JuNest does no longer support the building of AUR packages.
**Remember** that in order to build packages from AUR, `base-devel` package group is required
first:
```sh
pacman -S base-devel
```
JuNest uses a modified version of `sudo` provided by `junest/sudo-fake`. And the original `core/sudo`
package will be ignored **(and must not be installed)** during the installation of `base-devel`.
Have fun!
---------
If you are new on Arch Linux and you are not familiar with `pacman` package manager
visit the [pacman rosetta page](https://wiki.archlinux.org/index.php/Pacman_Rosetta).
Usage
=====
There are three different ways you can run JuNest depending on the backend program you decide to use.
Linux namespaces based
----------------------
The [Linux namespaces](http://man7.org/linux/man-pages/man7/namespaces.7.html)
represents the default backend program for JuNest.
The requirements for having Linux namespaces working are:
1. Kernel starting from Linux 3.8 allows unprivileged processes to create
user and mount namespaces.
1. The Linux kernel distro must have the user namespace enabled.
In the last years, the majority of GNU/Linux distros have the user namespace
enabled by default. This means that you do not need to have root privileges to
access to the JuNest environment via this method.
This
[wiki](https://github.com/fsquillace/junest/wiki/Linux-distros-with-user-namespace-enabled-by-default)
provides the state of the user namespace on several GNU/Linux distros.
In order to run JuNest via Linux namespaces:
- As normal user - Allow to make basic operations or install/remove packages
with `sudo` command: `junest ns` or `junest`
- As fakeroot - Allow to install/remove packages: `junest ns -f` or `junest -f`
This mode is based on the fantastic
[`bubblewrap`](https://github.com/containers/bubblewrap) command.
PRoot based
-----------
[Proot](https://wiki.archlinux.org/index.php/Proot) represents a portable
solution which allows unprivileged users to execute programs inside a sandbox
and works well in most of GNU/Linux distros available.
In order to run JuNest via Proot:
- As normal user - Allow to make basic operations: `junest proot`
- As fakeroot - Allow to install/remove packages: `junest proot -f`
In `proot` mode, the minimum recommended Linux kernel for the host OS is 2.6.32 on x86 (64 bit)
and ARM architectures. It is still possible to run JuNest on lower
2.6.x host OS kernels but errors may appear, and some applications may
crash. For further information, read the [Troubleshooting](#troubleshooting)
section below.
Chroot based
------------
This solution suits only for privileged users. JuNest provides the possibility
to run the environment via `chroot` program.
In particular, it uses a special program called `GRoot`, a small and portable
version of
[arch-chroot](https://wiki.archlinux.org/index.php/Chroot)
wrapper, that allows to bind mount directories specified by the user, such as
`/proc`, `/sys`, `/dev`, `/tmp`, `/run/user/<id>` and `$HOME`, before
executing any programs inside the JuNest sandbox. In case the mounting will not
work, JuNest is even providing the possibility to run the environment directly via
the pure `chroot` command.
In order to run JuNest via `chroot` solutions:
- As root via `GRoot` - Allow to have fully root privileges inside JuNest environment (you need to be root for executing this): `junest groot`
- As root via `chroot` - Allow to have fully root privileges inside JuNest environment (you need to be root for executing this): `junest root`
Execution modes comparison table
----------------
The following table shows the capabilities that each backend program is able to perform:
| | QEMU | Root privileges required | Manage Official Packages | Manage AUR Packages | Portability | Support | User modes |
| --- | ---- | ------------------------ | ------------------------ | ------------------- | ----------- | ------- | ---------- |
| **Linux Namespaces** | NO | NO | YES | YES | Poor | YES | Normal user and `fakeroot` |
| **Proot** | YES | NO | YES | NO | YES | YES | Normal user and `fakeroot` |
| **Chroot** | NO | YES | YES | YES | YES | YES | `root` only |
Advanced usage
==============
## Build image ##
You can build a new JuNest image from scratch by running the following command:
junest -b [-n]
```sh
junest build [-n]
```
The script will create a directory containing all the essentials
files in order to make JuNest working properly (such as pacman, yaourt and proot).
The option **-n** will skip the final validation tests if they are not needed.
files in order to make JuNest working properly (such as `pacman` and `proot`).
The option `-n` will skip the final validation tests if they are not needed.
Remember that the script to build the image must run in an Arch Linux OS with
arch-install-scripts, package-query, git and the base-devel packages installed.
To change the build directory just use the *JUNEST_TEMPDIR* (by default /tmp).
arch-install-scripts and the base-devel packages installed.
To change the build directory just use the `JUNEST_TEMPDIR` (by default /tmp).
After creating the image junest-x86\_64.tar.gz you can install it by running:
After creating the image `junest-x86_64.tar.gz` you can install it by running:
junest -i junest-x86_64.tar.gz
```sh
junest setup -i junest-x86_64.tar.gz
```
For more details, you can also take a look at
[junest-builder](https://github.com/fsquillace/junest-builder)
@ -127,22 +327,36 @@ Related wiki page:
## Run JuNest using a different architecture via QEMU ##
The following command will download the ARM JuNest image and will run QEMU in
case the host OS runs on either x86\_64 or x86 architectures:
case the host OS runs on `x86_64` architecture:
$> JUNEST_HOME=~/.junest-arm junest -a arm -- uname -m
armv7l
```sh
$> export JUNEST_HOME=~/.junest-arm
$> junest setup -a arm
$> junest proot -- uname -m
armv7l
```
## Bind directories ##
To bind a host directory to a guest location, you can use proot arguments:
To bind a host directory to a guest location:
junest -p "-b /mnt/mydata:/home/user/mydata"
```sh
junest -b "--bind /home/user/mydata /mnt/mydata"
```
Check out the proot options with:
Or using proot arguments:
junest -p "--help"
```sh
junest proot -b "-b /mnt/mydata:/home/user/mydata"
```
The option `-b` to provide options to the backend program will work with PRoot, Namespace and GRoot backend programs.
Check out the backend program options by passing `--help` option:
```sh
junest [u|g|p] -b "--help"
```
## Systemd integration ##
Although JuNest has not been designed to be a complete container, it is even possible to
virtualize the process tree thanks to the [systemd container](https://wiki.archlinux.org/index.php/Systemd-nspawn).
The JuNest containter allows to run services inside the container that can be
@ -152,7 +366,9 @@ and the container can only be executed using root privileges.
To boot a JuNest container:
sudo systemd-nspawn -bD ~/.junest
```sh
sudo systemd-nspawn -bD ~/.junest
```
Related wiki page:
@ -161,32 +377,19 @@ Related wiki page:
Internals
=========
There are two main chroot jail used in JuNest.
The main one is [proot](https://wiki.archlinux.org/index.php/Proot) which
allows unprivileged users to execute programs inside a sandbox and
jchroot, a small and portable version of
[arch-chroot](https://wiki.archlinux.org/index.php/Chroot) which is an
enhanced chroot for privileged users that mounts the primary directories
(i.e. /proc, /sys, /dev and /run) before executing any programs inside
the sandbox.
##Automatic fallback to classic chroot##
If jchroot fails for some reasons in the host system (i.e. it is not able to
mount one of the directories),
JuNest automatically tries to fallback to the classic chroot.
##Automatic fallback for all the dependent host OS executables##
JuNest attempt first to run the executables in the host OS located in different
positions (/usr/bin, /bin, /usr/sbin and /sbin).
## Automatic fallback for all the dependent host OS executables ##
JuNest attempts first to run the executables in the host OS located in different
positions (`/usr/bin`, `/bin`, `/usr/sbin` and `/sbin`).
As a fallback it tries to run the same executable if it is available in the JuNest
image.
environment.
##Automatic building of the JuNest images##
The JuNest images are built every week so that you can always get the most
updated package versions.
## Automatic building of the JuNest images ##
There is a periodic automation build of the JuNest images for `x86_64` arch
only.
The JuNest image for `arm` architecture may not be always up to date because
the build is performed manually.
##Static QEMU binaries##
## Static QEMU binaries ##
There are static QEMU binaries included in JuNest image that allows to run JuNest
in a different architecture from the host system. They are located in `/opt/qemu`
directory.
@ -194,85 +397,40 @@ directory.
Troubleshooting
===============
##Cannot use AUR repository##
For Arch Linux related FAQs take a look at the [General troubleshooting page](https://wiki.archlinux.org/index.php/General_troubleshooting).
> **Q**: Why do I get the following error when I try to install a package with yaourt?
## Cannot use AUR repository ##
> **Q**: Why do I get the following error when I try to install a package?
Cannot find the gzip binary required for compressing man and info pages.
> **A**: JuNest comes with a very basic number of packages.
> In order to install packages using yaourt you may need to install the package group **base-devel**
> that contains all the essential packages for compiling source code (such as gcc, make, patch, etc):
> In order to install AUR packages you need to install the package group `base-devel` first
> that contains all the essential packages for compiling from source code (such as gcc, make, patch, etc):
pacman -S base-devel
#> pacman -S base-devel
##Kernel too old##
> Remember to not install `core/sudo` as it conflicts with `junest/sudo-fake` package.
> **Q**: Why do I get the error: "FATAL: kernel too old"?
## Can't set user and group as root
> **A**: This is because the executable from the precompiled package cannot
> properly run if the kernel is old.
> You may need to specify the PRoot *-k* option if the guest rootfs
> requires a newer kernel version:
> **Q**: In ns mode when installing package I get the following error:
junest -p "-k 3.10"
warning: warning given when extracting /usr/file... (Can't set user=0/group=0 for
/usr/file...)
> In order to check if an executable inside JuNest environment can be compatible
> with the kernel of the host OS just use the *file* command, for instance:
> **A**: This is because as fakeroot is not possible to set the owner/group of
> files as root. The package will still be installed correctly even though this
> message is showed.
file ~/.junest/usr/bin/bash
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked
(uses shared libs), for GNU/Linux 2.6.32,
BuildID[sha1]=ec37e49e7188ff4030052783e61b859113e18ca6, stripped
## Could not change the root directory in pacman
> From the output you can see what is the minimum recommended Linux kernel version.
##SUID permissions##
> **Q**: Why I do not have permissions for ping?
ping www.google.com
ping: icmp open socket: Operation not permitted
> **A**: The ping command uses *suid* permissions that allow to execute the command using
> root privileges. The fakeroot mode is not able to execute a command set with suid,
> and you may need to use root privileges. There are other few commands that
> have *suid* permission, you can list the commands from your JuNest environment
> with the following command:
find /usr/bin -perm +4000
##No characters are visible on a graphic application##
> **Q**: Why I do not see any characters in the application I have installed?
> **A**: This is probably because there are no
> [fonts](https://wiki.archlinux.org/index.php/Font_Configuration) installed in
> the system.
> To quick fix this, you can just install a fonts package:
pacman -S gnu-free-fonts
##Differences between filesystem and package ownership##
> **Q**: Why do I get warning when I install a package using root privileges?
pacman -S systat
...
warning: directory ownership differs on /usr/
filesystem: 1000:100 package: 0:0
...
> **A**: In these cases the package installation went smoothly anyway.
> This should happen every time you install package with root privileges
> since JuNest will try to preserve the JuNest environment by assigning ownership
> of the files to the real user.
##No servers configured for repository##
## No servers configured for repository ##
> **Q**: Why I cannot install packages?
pacman -S lsof
#> pacman -S lsof
Packages (1): lsof-4.88-2
Total Download Size: 0.09 MiB
@ -286,35 +444,173 @@ Troubleshooting
> **A**: You need simply to update the mirrorlist file according to your location:
# Uncomment the repository line according to your location
nano /etc/pacman.d/mirrorlist
pacman -Syy
#> nano /etc/pacman.d/mirrorlist
#> pacman -Syy
## Locate the package for a given file ##
> **Q**: How do I find which package a certain file belongs to?
> **A**: JuNest is a really small distro, therefore you frequently need to find
> the package name for a certain file. `pkgfile` is an extremely useful package
> that allows you to detect the package of a given file.
> For instance, if you want to find the package name for the command `getopt`:
#> pacman -S pkgfile
#> pkgfile --update
$> pkgfile getop
core/util-linux
> Alternatively, you can use directly `pacman` command only. Take a look [here](https://wiki.archlinux.org/index.php/General_troubleshooting#Message:_%22error_while_loading_shared_libraries%22).
## Kernel too old ##
> **Q**: Why do I get the error: "FATAL: kernel too old"?
> **A**: This is because the binaries from the precompiled package are
> compiled for Linux kernel 2.6.32. When JuNest is started without further
> options, it tries to run a shell from the JuNest chroot. The system sees that
> the host OS kernel is too old and refuses to start the shell.
> The solution is to present a higher "fake" kernel version to the JuNest
> chroot. PRoot offers the *-k* option for this, and JuNest passes this option
> on to PRoot when *-p* is prepended. For example, to fake a kernel version of
> 3.10, issue the following command:
$> junest proot -b "-k 3.10"
> As Arch Linux ships binaries for kernel version 2.6.32, the above error is
> not unique to the precompiled package from JuNest. It will also appear when
> trying to run binaries that were later installed in the JuNest chroot with
> the `pacman` command.
> In order to check if an executable inside JuNest chroot is compatible with
> the kernel of the host OS just use the `file` command, for instance:
$> file ~/.junest/usr/bin/bash
ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked
(uses shared libs), for GNU/Linux 2.6.32,
BuildID[sha1]=ec37e49e7188ff4030052783e61b859113e18ca6, stripped
> The output shows the minimum recommended Linux kernel version.
## Kernel doesn't support private futexes ##
> **Q**: Why do I get the warning: "kompat: this kernel doesn't support private
> futexes and PRoot can't emulate them."?
> **A**: This happens on older host OS kernels when the trick of showing a fake
> kernel version to the JuNest chroot is applied (see above:
> [Kernel too old](#kernel-too-old)).
> The consequence of showing a fake kernel version to the JuNest chroot is that
> in the background, PRoot needs to translate requests from applications in the
> chroot to the old kernel of the host OS. Some of the newer kernel
> functionality can be emulated, but private futexes cannot be translated.
> Private Futexes were introduced in Linux kernel 2.6.22. Therefore, the above
> problem likely appears on old Linux systems, for example RHEL5 systems, which
> are based on Linux kernel 2.6.18. Many of the core tools like `which`, `man`,
> or `vim` run without problems while others, especially XOrg-based programs,
> are more likely to show the warning. These are also more likely to crash
> unexpectedly.
> Currently, there is no (easy) workaround for this. In order to be fully
> compatible with kernels below 2.6.22, both the precompiled package from
> JuNest and all software that is installed later needs to be compiled for this
> kernel. Most likely this can only be achieved by building the needed software
> packages from source, which kind of contradicts JuNest's distro-in-a-distro
> philosophy.
## SUID permissions ##
> **Q**: Why I do not have permissions for ping?
$> ping www.google.com
ping: icmp open socket: Operation not permitted
> **A**: The ping command uses *suid* permissions that allow to execute the command using
> root privileges. The fakeroot mode is not able to execute a command set with suid,
> and you may need to use root privileges. There are other few commands that
> have *suid* permission, you can list the commands from your JuNest environment
> with the following command:
$> find /usr/bin -perm /4000
## No characters are visible on a graphic application ##
> **Q**: Why I do not see any characters in the application I have installed?
> **A**: This is probably because there are no
> [fonts](https://wiki.archlinux.org/index.php/Font_Configuration) installed in
> the system.
> To quick fix this, you can just install a fonts package:
#> pacman -S gnu-free-fonts
## Differences between filesystem and package ownership ##
> **Q**: Why do I get warning when I install a package using root privileges?
#> pacman -S systat
...
warning: directory ownership differs on /usr/
filesystem: 1000:100 package: 0:0
...
> **A**: In these cases the package installation went smoothly anyway.
> This should happen every time you install package with root privileges
> since JuNest will try to preserve the JuNest environment by assigning ownership
> of the files to the real user.
## Unprivileged user namespace disable at kernel compile time or kernel too old ##
> **Q**: Why do I get this warning when I run JuNest via Linux namespaces?
$> junest ns
Unprivileged user namespace is disabled at kernel compile time or kernel too old (<3.8). Proceeding anyway...
> **A**: This means that JuNest detected that the host OS either
> does not have a newer kernel version or the unprivileged user namespace
> is not enabled at kernel compile time.
> JuNest does not stop the execution of the program but it attempts to run it
> anyway. Try to use Proot as backend program in case is not possible to invoke namespaces.
## Unprivileged user namespace disabled
> **Q**: Why do I get this warning when I run JuNest via Linux namespaces?
$> junest ns
Unprivileged user namespace disabled. Root permissions are required to enable it: sudo sysctl kernel.unprivileged_userns_clone=1
> **A**: This means that JuNest detected that the host OS either
> does not have a newer Linux version or the user namespace is not enabled.
> JuNest does not stop the execution of the program but it attempts to run it
> anyway. If you have root permissions try to enable it, otherwise try to use
> Proot as backend program.
More documentation
==================
There are additional tutorials in the
[JuNest wiki page](https://github.com/fsquillace/junest/wiki).
License
Contributing
============
Contributions are welcome! You could help improving JuNest in the following ways:
- [Reporting Bugs](CONTRIBUTING.md#reporting-bugs)
- [Suggesting Enhancements](CONTRIBUTING.md#suggesting-enhancements)
- [Writing Code](CONTRIBUTING.md#your-first-code-contribution)
Donating
========
To sustain the project please consider funding by donations through
the [GitHub Sponsors page](https://github.com/sponsors/fsquillace/).
Authors
=======
Copyright (c) 2012-2015
JuNest was originally created in late 2014 by [Filippo Squillace (feel.sqoox@gmail.com)](https://github.com/fsquillace).
This program is free software; you can redistribute it and/or modify it
under the terms of the GNU Library General Public License as published
by the Free Software Foundation; either version 2, or (at your option)
any later version.
Here is a list of [**really appreciated contributors**](https://github.com/fsquillace/junest/graphs/contributors)!
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see <http://www.gnu.org/licenses/>.
Author
======
Filippo Squillace <feel.sqoox@gmail.com>
WWW
===
https://github.com/fsquillace/junest
[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/0)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/0)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/1)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/1)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/2)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/2)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/3)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/3)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/4)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/4)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/5)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/5)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/6)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/6)[![](https://sourcerer.io/fame/fsquillace/fsquillace/junest/images/7)](https://sourcerer.io/fame/fsquillace/fsquillace/junest/links/7)

1
VERSION Normal file
View file

@ -0,0 +1 @@
7.4.10

View file

@ -1,112 +0,0 @@
#!/bin/bash
#
# Copyright (c) 2012-2015
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# This script is the simplified and portable version of arch-chroot
# (https://wiki.archlinux.org/index.php/Change_root#Using_arch-chroot)
set -e
################################ IMPORTS ##################################
source "$(dirname $0)/../lib/util.sh"
################################ MAIN FUNCTIONS ###########################
chroot_add_mount() {
mount "$@" && CHROOT_ACTIVE_MOUNTS=("$2" "${CHROOT_ACTIVE_MOUNTS[@]}")
}
chroot_maybe_add_mount() {
local cond=$1; shift
if eval "$cond"; then
chroot_add_mount "$@"
fi
}
chroot_setup() {
CHROOT_ACTIVE_MOUNTS=()
[[ $(trap -p EXIT) ]] && die '(BUG): attempting to overwrite existing EXIT trap'
trap 'chroot_teardown' EXIT
chroot_maybe_add_mount "! mountpoint -q '$1'" "$1" "$1" --bind &&
chroot_add_mount proc "$1/proc" -t proc -o nosuid,noexec,nodev &&
chroot_add_mount sys "$1/sys" -t sysfs -o nosuid,noexec,nodev,ro &&
chroot_add_mount udev "$1/dev" -t devtmpfs -o mode=0755,nosuid &&
chroot_add_mount devpts "$1/dev/pts" -t devpts -o mode=0620,gid=5,nosuid,noexec &&
chroot_add_mount shm "$1/dev/shm" -t tmpfs -o mode=1777,nosuid,nodev &&
chroot_add_mount run "$1/run" -t tmpfs -o nosuid,nodev,mode=0755 &&
chroot_add_mount tmp "$1/tmp" -t tmpfs -o mode=1777,atime,nodev,nosuid &&
mkdir -p "$1/$HOME" &&
chroot_add_mount $HOME "$1/$HOME" --bind
mkdir -p "$1/run/lock"
}
chroot_teardown() {
umount "${CHROOT_ACTIVE_MOUNTS[@]}"
unset CHROOT_ACTIVE_MOUNTS
}
usage() {
cat <<EOF
usage: ${0##*/} chroot-dir [command]
-h Print this help message
If 'command' is unspecified, ${0##*/} will launch /bin/sh.
EOF
}
chroot_add_resolv_conf() {
local chrootdir=$1 resolv_conf=$1/etc/resolv.conf
# Handle resolv.conf as a symlink to somewhere else.
if [[ -L $chrootdir/etc/resolv.conf ]]; then
# readlink(1) should always give us *something* since we know at this point
# it's a symlink. For simplicity, ignore the case of nested symlinks.
resolv_conf=$(readlink "$chrootdir/etc/resolv.conf")
if [[ $resolv_conf = /* ]]; then
resolv_conf=$chrootdir$resolv_conf
else
resolv_conf=$chrootdir/etc/$resolv_conf
fi
# ensure file exists to bind mount over
if [[ ! -f $resolv_conf ]]; then
install -Dm644 /dev/null "$resolv_conf" || return 1
fi
fi
chroot_add_mount /etc/resolv.conf "$resolv_conf" --bind
}
if [[ -z $1 || $1 == -h || $1 == --help ]]; then
usage
exit $(( $# ? 0 : 1 ))
fi
(( EUID == 0 )) || die 'This script must be run with root privileges'
chrootdir=$1
shift
[[ -d $chrootdir ]] || die "Can't create chroot on non-directory $chrootdir"
chroot_setup "$chrootdir" || die "failed to setup chroot $chrootdir"
chroot_add_resolv_conf "$chrootdir" || die "failed to setup resolv.conf"
SHELL="/bin/sh" chroot "$chrootdir" "$@"

View file

@ -1,231 +1,322 @@
#!/usr/bin/env bash
# shellcheck disable=SC1091
#
# This file is part of JuNest (https://github.com/fsquillace/junest).
#
# Copyright (c) 2012-2015
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
JUNEST_BASE="$(dirname $0)/.."
set -e
# JUNEST_BASE can be overridden for testing purposes.
# There is no need for doing it for normal usage.
JUNEST_BASE="${JUNEST_BASE:-$(readlink -f "$(dirname "$(readlink -f "$0")")"/..)}"
source "${JUNEST_BASE}/lib/utils/utils.sh"
source "${JUNEST_BASE}/lib/core/common.sh"
source "${JUNEST_BASE}/lib/core/build.sh"
source "${JUNEST_BASE}/lib/core/setup.sh"
source "${JUNEST_BASE}/lib/core/chroot.sh"
source "${JUNEST_BASE}/lib/core/namespace.sh"
source "${JUNEST_BASE}/lib/core/proot.sh"
source "${JUNEST_BASE}/lib/core/wrappers.sh"
source "${JUNEST_BASE}/lib/core.sh"
###################################
### General functions ###
###################################
usage() {
echo -e "$NAME: $DESCRIPTION"
echo -e "Usage: $CMD [options] [--] [command]"
echo
echo -e "Setup options:"
echo -e "-i, --setup-from-file <image> Setup the $NAME image in ${JUNEST_HOME}"
echo -e "-a, --arch <arch> $NAME architecture to download (x86_64, x86, arm)."
echo -e " Defaults to the host architecture ($ARCH)"
echo -e "-d, --delete Delete $NAME from ${JUNEST_HOME}"
echo -e "$NAME (v$(cat "$JUNEST_BASE"/VERSION)): $DESCRIPTION"
echo
echo -e "Access options:"
echo -e "-f, --fakeroot Run $NAME with fakeroot privileges"
echo -e "-r, --root Run $NAME with root privileges"
echo -e "-p, --proot-args <args> Proot arguments"
echo -e "Usage: $CMD [action] [options] [--] [command]"
echo
echo -e "Building options:"
echo -e "-b, --build-image Build a $NAME image (must run in ArchLinux)"
echo -e "-n, --disable-validation Disable the $NAME image validation"
echo -e "-c, --check <${CMD}_script> Validate the env located in ${JUNEST_HOME}"
echo -e " using ${CMD}_script. This will alterate the environment."
echo -e "-s, --skip-root-tests Skip the root tests during the validation process."
echo -e "General:"
echo -e "-h, --help Show this help message"
echo -e "-V, --version Show the $NAME version"
echo
echo -e "Actions and options:"
echo -e " s[etup] Setup $NAME in ${JUNEST_HOME} either from repo or from file"
echo -e " -i, --from-file <image> Setup the $NAME image in ${JUNEST_HOME}"
echo -e " -a, --arch <arch> $NAME architecture to download (x86_64, arm)"
echo -e " Defaults to the host architecture ($ARCH)"
echo -e " -d, --delete Delete $NAME from ${JUNEST_HOME}"
echo
echo -e " n[s] Access via Linux Namespaces using BubbleWrap (Default action)"
echo -e " -f, --fakeroot Run $NAME with fakeroot privileges"
echo -e " --backend-command <cmd> Bwrap command to use"
echo -e " -b, --backend-args <args> Arguments for bwrap backend program"
echo -e " ($CMD ns -b \"--help\" to check out the bwrap options)"
echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment"
echo
echo -e " p[root] Access via PRoot"
echo -e " -f, --fakeroot Run $NAME with fakeroot privileges"
echo -e " --backend-command <cmd> PRoot command to use"
echo -e " -b, --backend-args <args> Arguments for PRoot backend program"
echo -e " ($CMD proot -b \"--help\" to check out the PRoot options)"
echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment"
echo
echo -e " g[root] Access with root privileges via GRoot"
echo -e " --backend-command <cmd> GRoot command to use"
echo -e " -b, --backend-args <args> Arguments for GRoot backend program"
echo -e " ($CMD groot -b \"--help\" to check out the GRoot options)"
echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment"
echo
echo -e " r[oot] Access with root privileges via classic chroot"
echo -e " --backend-command <cmd> Chroot command to use"
echo -e " -b, --backend-args <args> Arguments for chroot backend program"
echo -e " ($CMD root -b \"--help\" to check out the chroot options)"
echo -e " -n, --no-copy-files Do not copy common etc files into $NAME environment"
echo
echo -e " b[uild] Build a $NAME image (must run in ArchLinux)"
echo -e " -n, --disable-check Disable the $NAME image check"
echo
echo -e " create-bin-wrappers Create a bin wrappers directory according to --bin-path option"
echo -e " Default path is $JUNEST_HOME/usr/bin_wrappers"
echo -e " -f, --force Create the wrapper files even if they already exist"
echo -e " -p, --bin-path The source directory where executable are located in JuNest"
echo -e " Default value is: /usr/bin"
echo
echo -e "General options:"
echo -e "-h, --help Show this help message"
echo -e "-v, --version Show the $NAME version"
}
version() {
echo -e "$NAME $VERSION ($CODE_NAME): $DESCRIPTION"
echo -e "Copyright (c) $COPYRIGHT $AUTHOR"
echo -e "Homepage: $HOMEPAGE"
echo -e "$NAME $(cat "$JUNEST_BASE"/VERSION)"
}
check_cli(){
if $OPT_BUILD_IMAGE
then
if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_CHECK
then
die "The build image option must be used exclusively"
fi
fi
if $OPT_SKIP_ROOT_TEST
then
if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT
then
die "The skip root tests option must be used with either build image or check options"
fi
fi
if $OPT_CHECK
then
if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_BUILD_IMAGE
then
die "The validation image option must be used exclusively"
fi
fi
if $OPT_DISABLE_VALIDATION
then
if $OPT_DELETE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_CHECK
then
die "The disable validation option must be used with the build image option only"
fi
fi
if $OPT_DELETE
then
if $OPT_BUILD_IMAGE || $OPT_HELP || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION || $OPT_CHECK
then
die "The $NAME delete option must be used exclusively"
fi
fi
if $OPT_HELP
then
if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_VERSION || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION || $OPT_CHECK
then
die "The $NAME help option must be used exclusively"
fi
fi
if $OPT_VERSION
then
if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || $OPT_SETUP_FROM_FILE || \
$OPT_FAKEROOT || $OPT_ROOT || $OPT_DISABLE_VALIDATION || $OPT_CHECK
then
die "The $NAME version option must be used exclusively"
fi
fi
if $OPT_FAKEROOT && $OPT_ROOT
then
die "You must access to $NAME with either fakeroot or root permissions"
fi
if $OPT_PROOT_ARGS || $OPT_ARCH
then
if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || \
$OPT_ROOT || $OPT_VERSION || $OPT_DISABLE_VALIDATION || $OPT_CHECK
then
die "Invalid syntax: Proot and arch args are not allowed with the other options"
fi
fi
if [ "$ARGS" != "" ]
then
if $OPT_BUILD_IMAGE || $OPT_DELETE || $OPT_HELP || $OPT_SETUP_FROM_FILE || \
$OPT_VERSION || $OPT_DISABLE_VALIDATION || $OPT_CHECK
then
die "No arguments are needed. For the CLI syntax run: $CMD --help"
fi
fi
return 0
}
function parse_arguments(){
OPT_SETUP_FROM_FILE=false
IMAGE_FILE=""
OPT_FAKEROOT=false
OPT_ROOT=false
OPT_PROOT_ARGS=false
PROOT_ARGS=""
OPT_ARCH=false
ARCH_ARG=""
OPT_BUILD_IMAGE=false
OPT_DISABLE_VALIDATION=false
OPT_CHECK=false
CHECK_ARG=""
OPT_SKIP_ROOT_TEST=false
OPT_DELETE=false
OPT_HELP=false
OPT_VERSION=false
for opt in "$@"
do
case "$1" in
-i|--setup-from-file) OPT_SETUP_FROM_FILE=true ; shift ; IMAGE_FILE=$1 ; shift ;;
-f|--fakeroot) OPT_FAKEROOT=true ; shift ;;
-r|--root) OPT_ROOT=true ; shift ;;
-p|--proot-args) OPT_PROOT_ARGS=true ; shift ; PROOT_ARGS=$1; shift ;;
-a|--arch) OPT_ARCH=true ; shift ; ARCH_ARG=$1; shift ;;
-b|--build-image) OPT_BUILD_IMAGE=true ; shift ;;
-n|--disable-validation) OPT_DISABLE_VALIDATION=true ; shift ;;
-c|--check) OPT_CHECK=true ; shift ; CHECK_ARG=$1; shift ;;
-s|--skip-root-tests) OPT_SKIP_ROOT_TEST=true ; shift ;;
-d|--delete) OPT_DELETE=true ; shift ;;
-h|--help) OPT_HELP=true ; shift ;;
-v|--version) OPT_VERSION=true ; shift ;;
--) shift ; break ;;
-*) die "Invalid option $1" ;;
*) break ;;
esac
done
# Actions
ACT_SETUP=false
ACT_BUILD=false
ACT_CREATE_WRAPPERS=false
ACT_NAMESPACE=false
ACT_PROOT=false
ACT_GROOT=false
ACT_ROOT=false
ACT_HELP=false
ACT_VERSION=false
ARGS=()
for arg in "$@"
do
ARGS+=("$arg")
done
case "$1" in
s|setup) ACT_SETUP=true ; shift ;;
b|build) ACT_BUILD=true ; shift ;;
create-bin-wrappers) ACT_CREATE_WRAPPERS=true ; shift ;;
n|ns) ACT_NAMESPACE=true ; shift ;;
p|proot) ACT_PROOT=true ; shift ;;
g|groot) ACT_GROOT=true ; shift ;;
r|root) ACT_ROOT=true ; shift ;;
-h|--help) ACT_HELP=true ; shift ;;
-V|--version) ACT_VERSION=true ; shift ;;
*) ACT_NAMESPACE=true ;;
esac
if $ACT_SETUP
then
_parse_setup_opts "$@"
elif $ACT_BUILD
then
_parse_build_opts "$@"
elif $ACT_CREATE_WRAPPERS
then
_parse_create_wrappers_opts "$@"
elif $ACT_NAMESPACE
then
_parse_ns_opts "$@"
elif $ACT_PROOT
then
_parse_proot_opts "$@"
elif $ACT_GROOT
then
_parse_root_opts "$@"
elif $ACT_ROOT
then
_parse_root_opts "$@"
fi
}
function execute_operation(){
$OPT_HELP && usage && return
$OPT_VERSION && version && return
function _parse_root_opts() {
# Options:
BACKEND_ARGS=""
OPT_NO_COPY_FILES=false
BACKEND_COMMAND=""
if $OPT_BUILD_IMAGE; then
build_image_env $OPT_DISABLE_VALIDATION $OPT_SKIP_ROOT_TEST
return
elif $OPT_DELETE; then
delete_env
return
elif $OPT_CHECK; then
check_env "${JUNEST_HOME}" "${CHECK_ARG}" $OPT_SKIP_ROOT_TEST
return
fi
while [[ -n "$1" ]]
do
case "$1" in
-b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;;
-n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;;
--backend-command) shift; BACKEND_COMMAND="$1"; shift ;;
--) shift ; break ;;
-*) die "Invalid option $1" ;;
*) break ;;
esac
done
if ! is_env_installed
then
if $OPT_SETUP_FROM_FILE; then
setup_env_from_file $IMAGE_FILE
else
setup_env $ARCH_ARG
unset ARCH_ARG
fi
elif $OPT_SETUP_FROM_FILE; then
die "Error: The image cannot be installed since $JUNEST_HOME is not empty."
fi
[ -z "${ARCH_ARG}" ] || \
die "The option --arch cannot be specified since JuNest has already been downloaded in $JUNEST_HOME"
if $OPT_FAKEROOT; then
run_env_as_fakeroot "${PROOT_ARGS}" "${ARGS[@]}"
elif $OPT_ROOT; then
run_env_as_root "${ARGS[@]}"
else
run_env_as_user "${PROOT_ARGS}" "${ARGS[@]}"
fi
ARGS=()
for arg in "$@"
do
ARGS+=("$arg")
done
}
parse_arguments "$@"
check_cli
execute_operation
function _parse_ns_opts() {
# Options:
OPT_FAKEROOT=false
BACKEND_ARGS=""
OPT_NO_COPY_FILES=false
BACKEND_COMMAND=""
while [[ -n "$1" ]]
do
case "$1" in
-f|--fakeroot) OPT_FAKEROOT=true ; shift ;;
-b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;;
-n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;;
--backend-command) shift; BACKEND_COMMAND="$1"; shift ;;
--) shift ; break ;;
-*) die "Invalid option $1" ;;
*) break ;;
esac
done
ARGS=()
for arg in "$@"
do
ARGS+=("$arg")
done
}
function _parse_proot_opts() {
# Options:
OPT_FAKEROOT=false
BACKEND_ARGS=""
OPT_NO_COPY_FILES=false
BACKEND_COMMAND=""
while [[ -n "$1" ]]
do
case "$1" in
-f|--fakeroot) OPT_FAKEROOT=true ; shift ;;
-b|--backend-args) shift ; BACKEND_ARGS=$1; shift ;;
-n|--no-copy-files) OPT_NO_COPY_FILES=true ; shift ;;
--backend-command) shift; BACKEND_COMMAND="$1"; shift ;;
--) shift ; break ;;
-*) die "Invalid option $1" ;;
*) break ;;
esac
done
ARGS=("$@")
}
function _parse_build_opts() {
OPT_DISABLE_CHECK=false
while [[ -n "$1" ]]
do
case "$1" in
-n|--disable-check) OPT_DISABLE_CHECK=true ; shift ;;
*) die "Invalid option $1" ;;
esac
done
}
function _parse_create_wrappers_opts() {
OPT_FORCE=false
OPT_BIN_PATH=""
while [[ -n "$1" ]]
do
case "$1" in
-f|--force) OPT_FORCE=true ; shift ;;
-p|--bin-path) shift ; OPT_BIN_PATH="$1" ; shift ;;
*) die "Invalid option $1" ;;
esac
done
}
function _parse_setup_opts() {
OPT_FROM_FILE=false
IMAGE_FILE=""
ARCH_ARG=""
OPT_DELETE=false
while [[ -n "$1" ]]
do
case "$1" in
-i|--from-file) OPT_FROM_FILE=true ; shift ; IMAGE_FILE=$1 ; shift ;;
-a|--arch) shift ; ARCH_ARG=$1; shift ;;
-d|--delete) OPT_DELETE=true ; shift ;;
*) die "Invalid option $1" ;;
esac
done
}
function execute_operation() {
$ACT_HELP && usage && return
$ACT_VERSION && version && return
if $ACT_BUILD; then
# shellcheck disable=SC2086
build_image_env $OPT_DISABLE_CHECK
return
fi
if $ACT_SETUP; then
if $OPT_DELETE; then
delete_env
else
if is_env_installed
then
die "Error: The image cannot be installed since $JUNEST_HOME is not empty."
fi
if $OPT_FROM_FILE; then
setup_env_from_file "$IMAGE_FILE"
else
setup_env "$ARCH_ARG"
fi
create_wrappers
fi
return
fi
if ! is_env_installed
then
die "Error: The image is still not installed in $JUNEST_HOME. Run this first: $CMD setup"
fi
if $ACT_CREATE_WRAPPERS; then
# shellcheck disable=SC2086
create_wrappers $OPT_FORCE "$OPT_BIN_PATH"
exit
fi
local run_env
if $ACT_NAMESPACE; then
if $OPT_FAKEROOT; then
run_env=run_env_as_bwrap_fakeroot
else
run_env=run_env_as_bwrap_user
fi
elif $ACT_PROOT; then
if $OPT_FAKEROOT; then
run_env=run_env_as_proot_fakeroot
else
run_env=run_env_as_proot_user
fi
elif $ACT_GROOT; then
run_env=run_env_as_groot
elif $ACT_ROOT; then
run_env=run_env_as_chroot
fi
# Call create_wrappers in case new bin files have been created
# shellcheck disable=SC2064
trap "PATH=$PATH create_wrappers" EXIT QUIT TERM
# shellcheck disable=SC2086
$run_env "$BACKEND_COMMAND" "${BACKEND_ARGS}" $OPT_NO_COPY_FILES "${ARGS[@]}"
}
function main() {
parse_arguments "$@"
execute_operation
}
main "$@"
# vim: set ts=4 sw=4 noet:

8
bin/sudoj Executable file
View file

@ -0,0 +1,8 @@
#!/usr/bin/env bash
#
# This file is part of JuNest (https://github.com/fsquillace/junest).
#
export PATH="${PATH}:${JUNEST_HOME}/usr/bin_wrappers"
JUNEST_ARGS="ns --fakeroot" "$@"

15
ci/build_image.sh Executable file
View file

@ -0,0 +1,15 @@
#!/usr/bin/env bash
set -ex
pacman -Sy --noconfirm sudo
# Create a travis user
echo "travis ALL=(ALL) NOPASSWD: ALL" > /etc/sudoers.d/travis
chmod 'u=r,g=r,o=' /etc/sudoers.d/travis
groupadd --gid "2000" "travis"
useradd --create-home --uid "2000" --gid "2000" --shell /usr/bin/false "travis"
# Here do not make any validation (-n) because it will be done later on in the Ubuntu host directly
cd /build
runuser -u travis -- /build/bin/junest build -n

48
ci/deploy.sh Executable file
View file

@ -0,0 +1,48 @@
#!/usr/bin/env bash
set -e
IMG_PATH=$1
set -ux
MAX_OLD_IMAGES=5
ENDPOINT="https://8da1bcd84e423c9b013b69fe1e8b4675.r2.cloudflarestorage.com"
# ARCH can be one of: x86, x86_64, arm
HOST_ARCH=$(uname -m)
if [ "$HOST_ARCH" == "i686" ] || [ "$HOST_ARCH" == "i386" ]
then
ARCH="x86"
elif [ "$HOST_ARCH" == "x86_64" ]
then
ARCH="x86_64"
elif [[ $HOST_ARCH =~ .*(arm).* ]]
then
ARCH="arm"
else
echo "Unknown architecture ${HOST_ARCH}" >&2
exit 11
fi
if [[ "$TRAVIS_BRANCH" == "master" ]]
then
export AWS_DEFAULT_REGION=auto
# Upload image
# The put is done via a temporary filename in order to prevent outage on the
# production file for a longer period of time.
img_name=$(basename "${IMG_PATH}")
aws s3 --endpoint-url="$ENDPOINT" cp "${IMG_PATH}" s3://junest-repo/junest/
DATE=$(date +'%Y-%m-%d-%H-%M-%S')
aws s3 --endpoint-url="$ENDPOINT" cp "${IMG_PATH}" "s3://junest-repo/junest/${img_name}.${DATE}"
# Cleanup old images
aws s3 --endpoint-url="$ENDPOINT" ls s3://junest-repo/junest/junest-${ARCH}.tar.gz. | awk '{print $4}' | head -n -${MAX_OLD_IMAGES} | xargs -I {} aws s3 --endpoint-url="$ENDPOINT" rm "s3://junest-repo/junest/{}"
# Test the newly deployed image can be downloaded correctly
junest setup
junest -- echo "Installed JuNest (\$(uname -m))"
yes | junest setup --delete
fi

13
ci/install-bash.sh Executable file
View file

@ -0,0 +1,13 @@
#!/bin/sh
set -ex
VERSION=$1
cd /tmp
wget "http://ftp.gnu.org/gnu/bash/bash-$VERSION.tar.gz"
tar -zxf "bash-$VERSION.tar.gz"
cd /tmp/bash-"$VERSION"*
./configure
make
sudo make install

102
lib/checks/check.sh Executable file
View file

@ -0,0 +1,102 @@
#!/usr/bin/env bash
# shellcheck disable=SC1091
#
# This modules is used for:
# - Running checks against the building JuNest image
# - Integration tests on JuNest script against different execution modes (i.e. -f, -u, -r modes)
#
# Dependencies:
# - None
#
# vim: ft=sh
set -ex
RUN_ROOT_TESTS=false
SKIP_AUR_TESTS=false
USE_SUDO=false
while [[ -n "$1" ]]
do
case "$1" in
--run-root-tests) RUN_ROOT_TESTS=true ; shift ;;
--skip-aur-tests) SKIP_AUR_TESTS=true ; shift ;;
--use-sudo) USE_SUDO=true ; shift ;;
*) die "Invalid option $1" ;;
esac
done
set -u
SUDO=""
[[ -n $USE_SUDO ]] && SUDO="sudo"
JUNEST_HOME=${JUNEST_HOME:-$HOME/.junest}
# JUNEST_BASE can be overridden for testing purposes.
# There is no need for doing it for normal usage.
JUNEST_BASE="${JUNEST_BASE:-$(readlink -f "$(dirname "$(readlink -f "$0")")"/../..)}"
source "${JUNEST_BASE}/lib/utils/utils.sh"
source "${JUNEST_BASE}/lib/core/common.sh"
info "Validating JuNest located in ${JUNEST_HOME}..."
info "Initial JuNest setup..."
# The following ensures that the gpg agent gets killed (if exists)
# otherwise it is not possible to exit from the session
trap "[[ -e /etc/pacman.d/gnupg/S.gpg-agent ]] && gpg-connect-agent -S /etc/pacman.d/gnupg/S.gpg-agent killagent /bye" QUIT EXIT ABRT TERM INT
prepare_archlinux "$SUDO"
PACMAN_OPTIONS="--noconfirm --disable-download-timeout"
# shellcheck disable=SC2086
$SUDO pacman $PACMAN_OPTIONS -S grep coreutils
# shellcheck disable=SC2086
# shellcheck disable=SC2046
$SUDO pacman $PACMAN_OPTIONS -Syu --ignore sudo base-devel
info "Checking basic executables work..."
$SUDO pacman -Qi pacman 1> /dev/null
/usr/bin/groot --help 1> /dev/null
# Test FAKEROOTDONTTRYCHOWN is set to true by default
set +u
if [[ -z $FAKEROOTKEY ]]
then
fakeroot chown root /tmp
else
chown root /tmp
fi
set -u
repo_package1=tree
echo "Checking ${repo_package1} package from official repo..."
# shellcheck disable=SC2086
$SUDO pacman $PACMAN_OPTIONS -S ${repo_package1}
tree -L 1
# shellcheck disable=SC2086
$SUDO pacman $PACMAN_OPTIONS -Rsn ${repo_package1}
repo_package2=iftop
info "Checking ${repo_package2} package from official repo..."
# shellcheck disable=SC2086
$SUDO pacman $PACMAN_OPTIONS -S ${repo_package2}
if $RUN_ROOT_TESTS
then
# Time it out given that sometimes it gets stuck after few seconds.
$SUDO timeout 10 iftop -t -s 5 || true
fi
# shellcheck disable=SC2086
$SUDO pacman $PACMAN_OPTIONS -Rsn ${repo_package2}
if ! $SKIP_AUR_TESTS
then
aur_package=tcptraceroute
info "Checking ${aur_package} package from AUR repo..."
yay --noconfirm -S ${aur_package}
# shellcheck disable=SC2086
$SUDO pacman $PACMAN_OPTIONS -Rsn ${aur_package}
fi
exit 0

31
lib/checks/check_all.sh Executable file
View file

@ -0,0 +1,31 @@
#!/usr/bin/env bash
# Multiple tests against different execution modes
set -ex
# JUNEST_BASE can be overridden for testing purposes.
# There is no need for doing it for normal usage.
JUNEST_BASE="${JUNEST_BASE:-$(readlink -f "$(dirname "$(readlink -f "$0")")"/../..)}"
JUNEST_SCRIPT=${JUNEST_SCRIPT:-${JUNEST_BASE}/bin/junest}
CHECK_SCRIPT=${JUNEST_BASE}/lib/checks/check.sh
$JUNEST_SCRIPT proot --fakeroot -- "$CHECK_SCRIPT" --skip-aur-tests
$JUNEST_SCRIPT proot -- "$CHECK_SCRIPT" --skip-aur-tests --use-sudo
# Test the backend command option
$JUNEST_SCRIPT proot --backend-command "$JUNEST_HOME/usr/bin/proot-x86_64" -- exit
$JUNEST_SCRIPT ns --fakeroot -- "$CHECK_SCRIPT" --skip-aur-tests
$JUNEST_SCRIPT ns -- "$CHECK_SCRIPT" --use-sudo
# Test the backend command option
$JUNEST_SCRIPT ns --backend-command "$JUNEST_HOME/usr/bin/bwrap" -- exit
sudo -E "$JUNEST_SCRIPT" groot -- "$CHECK_SCRIPT" --run-root-tests --skip-aur-tests
# Test the wrappers work
"$JUNEST_SCRIPT" create-bin-wrappers --force
"$JUNEST_HOME"/usr/bin_wrappers/pacman --help
"$JUNEST_SCRIPT" create-bin-wrappers --force --bin-path /usr/bin/core_perl/
"$JUNEST_HOME"/usr/bin/core_perl_wrappers/shasum --help
"${JUNEST_BASE}/bin/sudoj" pacman -Syu

View file

@ -1,469 +0,0 @@
#!/usr/bin/env bash
#
# Copyright (c) 2012-2015
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
#
# References:
# https://wiki.archlinux.org/index.php/PKGBUILD
# https://wiki.archlinux.org/index.php/Creating_Packages
set -e
################################ IMPORTS #################################
source "$(dirname ${BASH_ARGV[0]})/util.sh"
################################# VARIABLES ##############################
NAME='JuNest'
CMD='junest'
VERSION='5.6.7'
CODE_NAME='Nitida'
DESCRIPTION='The Arch Linux based distro that runs upon any Linux distros without root access'
AUTHOR='Filippo Squillace <feel dot sqoox at gmail.com>'
HOMEPAGE="https://github.com/fsquillace/${CMD}"
COPYRIGHT='2012-2015'
if [ "$JUNEST_ENV" == "1" ]
then
die "Error: Nested ${NAME} environments are not allowed"
elif [ ! -z $JUNEST_ENV ] && [ "$JUNEST_ENV" != "0" ]
then
die "The variable JUNEST_ENV is not properly set"
fi
[ -z ${JUNEST_HOME} ] && JUNEST_HOME=~/.${CMD}
[ -z ${JUNEST_BASE} ] && JUNEST_BASE=${JUNEST_HOME}/opt/junest
if [ -z ${JUNEST_TEMPDIR} ] || [ ! -d ${JUNEST_TEMPDIR} ]
then
JUNEST_TEMPDIR=/tmp
fi
# The update of the variable PATH ensures that the executables are
# found on different locations
PATH=/usr/bin:/bin:/usr/sbin:/sbin:$PATH
# The executable uname is essential in order to get the architecture
# of the host system, so a fallback mechanism cannot be used for it.
UNAME=uname
ARCH_LIST=('x86_64' 'x86' 'arm')
HOST_ARCH=$($UNAME -m)
if [ $HOST_ARCH == "i686" ] || [ $HOST_ARCH == "i386" ]
then
ARCH="x86"
LD_LIB="${JUNEST_HOME}/lib/ld-linux.so.2"
elif [ $HOST_ARCH == "x86_64" ]
then
ARCH="x86_64"
LD_LIB="${JUNEST_HOME}/lib64/ld-linux-x86-64.so.2"
elif [[ $HOST_ARCH =~ .*(arm).* ]]
then
ARCH="arm"
LD_LIB="${JUNEST_HOME}/lib/ld-linux-armhf.so.3"
else
die "Unknown architecture ${ARCH}"
fi
PROOT_LINK=http://static.proot.me/
MAIN_REPO=https://dl.dropboxusercontent.com/u/42449030
ENV_REPO=${MAIN_REPO}/${CMD}
DEFAULT_MIRROR='https://mirrors.kernel.org/archlinux/$repo/os/$arch'
ORIGIN_WD=$(pwd)
################################ EXECUTABLES ##################################
# This section contains all the executables needed for JuNest to run properly.
# They are based on a fallback mechanism that tries to use the executable in
# different locations in the host OS.
# List of executables that are run inside JuNest:
SH=("/bin/sh" "--login")
# List of executables that are run in the host OS:
PROOT_COMPAT="${JUNEST_HOME}/opt/proot/proot-${ARCH}"
CHROOT=${JUNEST_BASE}/bin/jchroot
CLASSIC_CHROOT="chroot"
WGET="wget --no-check-certificate"
CURL="curl -L -J -O -k"
TAR=tar
CHOWN="chown"
LD_EXEC="$LD_LIB --library-path ${JUNEST_HOME}/usr/lib:${JUNEST_HOME}/lib"
# The following functions attempt first to run the executable in the host OS.
# As a last hope they try to run the same executable available in the JuNest
# image.
function ln_cmd(){
ln $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/ln $@
}
function rm_cmd(){
rm $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/rm $@
}
function chown_cmd(){
$CHOWN $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/chown $@
}
function mkdir_cmd(){
mkdir $@ || $LD_EXEC ${JUNEST_HOME}/usr/bin/mkdir $@
}
function proot_cmd(){
local proot_args="$1"
shift
if ${PROOT_COMPAT} ${proot_args} "${SH[@]}" "-c" ":"
then
${PROOT_COMPAT} ${proot_args} "${@}"
elif PROOT_NO_SECCOMP=1 ${PROOT_COMPAT} ${proot_args} "${SH[@]}" "-c" ":"
then
PROOT_NO_SECCOMP=1 ${PROOT_COMPAT} ${proot_args} "${@}"
else
die "Error: Check if the ${CMD} arguments are correct and if the kernel is too old use the option ${CMD} -p \"-k 3.10\""
fi
}
function download_cmd(){
$WGET $@ || $CURL $@
}
function chroot_cmd(){
$CHROOT "$@" || $CLASSIC_CHROOT "$@" || $LD_EXEC ${JUNEST_HOME}/usr/bin/chroot "$@"
}
################################# MAIN FUNCTIONS ##############################
function is_env_installed(){
[ -d "$JUNEST_HOME" ] && [ "$(ls -A $JUNEST_HOME)" ] && return 0
return 1
}
function _cleanup_build_directory(){
# $1: maindir (optional) - str: build directory to get rid
local maindir=$1
builtin cd $ORIGIN_WD
trap - QUIT EXIT ABRT KILL TERM INT
rm_cmd -fr "$maindir"
}
function _prepare_build_directory(){
trap - QUIT EXIT ABRT KILL TERM INT
trap "rm_cmd -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT KILL TERM INT
}
function _setup_env(){
is_env_installed && die "Error: ${NAME} has been already installed in $JUNEST_HOME"
mkdir_cmd -p "${JUNEST_HOME}"
imagepath=$1
$TAR -zxpf ${imagepath} -C ${JUNEST_HOME}
mkdir_cmd -p ${JUNEST_HOME}/run/lock
info "The default mirror URL is ${DEFAULT_MIRROR}."
info "Remember to refresh the package databases from the server:"
info " pacman -Syy"
info "${NAME} installed successfully"
}
function setup_env(){
local arch=$ARCH
[ -z $1 ] || arch="$1"
contains_element $arch "${ARCH_LIST[@]}" || \
die "The architecture is not one of: ${ARCH_LIST[@]}"
local maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t ${CMD}.XXXXXXXXXX)
_prepare_build_directory
info "Downloading ${NAME}..."
builtin cd ${maindir}
local imagefile=${CMD}-${arch}.tar.gz
download_cmd ${ENV_REPO}/${imagefile}
info "Installing ${NAME}..."
_setup_env ${maindir}/${imagefile}
_cleanup_build_directory ${maindir}
}
function setup_env_from_file(){
local imagefile=$1
[ ! -e ${imagefile} ] && die "Error: The ${NAME} image file ${imagefile} does not exist"
info "Installing ${NAME} from ${imagefile}..."
_setup_env ${imagefile}
builtin cd $ORIGIN_WD
}
function run_env_as_root(){
source ${JUNEST_HOME}/etc/junest/info
[ "$JUNEST_ARCH" != "$ARCH" ] && \
die "The host system architecture is not correct: $ARCH != $JUNEST_ARCH"
local uid=$UID
# SUDO_USER is more reliable compared to SUDO_UID
[ -z $SUDO_USER ] || uid=$SUDO_USER:$SUDO_GID
local main_cmd="${SH[@]}"
[ "$1" != "" ] && main_cmd="$(insert_quotes_on_spaces "$@")"
# With chown the ownership of the files is assigned to the real user
trap - QUIT EXIT ABRT KILL TERM INT
trap "[ -z $uid ] || chown_cmd -R ${uid} ${JUNEST_HOME}; rm_cmd -f ${JUNEST_HOME}/etc/mtab" EXIT QUIT ABRT KILL TERM INT
[ ! -e ${JUNEST_HOME}/etc/mtab ] && ln_cmd -s /proc/self/mounts ${JUNEST_HOME}/etc/mtab
JUNEST_ENV=1 chroot_cmd "$JUNEST_HOME" "${SH[@]}" "-c" "${main_cmd}"
}
function _run_env_with_proot(){
local proot_args="$1"
shift
if [ "$1" != "" ]
then
JUNEST_ENV=1 proot_cmd "${proot_args}" "${SH[@]}" "-c" "$(insert_quotes_on_spaces "${@}")"
else
JUNEST_ENV=1 proot_cmd "${proot_args}" "${SH[@]}"
fi
}
function _run_env_with_qemu(){
local proot_args="$1"
source ${JUNEST_HOME}/etc/junest/info
if [ "$JUNEST_ARCH" != "$ARCH" ]
then
local qemu_bin="qemu-$JUNEST_ARCH-static-$ARCH"
local qemu_symlink="/tmp/${qemu_bin}-$RANDOM"
trap - QUIT EXIT ABRT KILL TERM INT
trap "[ -e ${qemu_symlink} ] && rm_cmd -f ${qemu_symlink}" EXIT QUIT ABRT KILL TERM INT
warn "Emulating $NAME via QEMU..."
[ -e ${qemu_symlink} ] || \
ln_cmd -s ${JUNEST_HOME}/opt/qemu/${qemu_bin} ${qemu_symlink}
proot_args="-q ${qemu_symlink} $proot_args"
fi
shift
_run_env_with_proot "$proot_args" "${@}"
}
function run_env_as_fakeroot(){
(( EUID == 0 )) && \
die "You cannot access with root privileges. Use --root option instead."
[ ! -e ${JUNEST_HOME}/etc/mtab ] && ln_cmd -s /proc/self/mounts ${JUNEST_HOME}/etc/mtab
_run_env_with_qemu "-S ${JUNEST_HOME} $1" "${@:2}"
}
function run_env_as_user(){
(( EUID == 0 )) && \
die "You cannot access with root privileges. Use --root option instead."
[ -e ${JUNEST_HOME}/etc/mtab ] && rm_cmd -f ${JUNEST_HOME}/etc/mtab
_run_env_with_qemu "-R ${JUNEST_HOME} $1" "${@:2}"
}
function delete_env(){
! ask "Are you sure to delete ${NAME} located in ${JUNEST_HOME}" "N" && return
if mountpoint -q ${JUNEST_HOME}
then
info "There are mounted directories inside ${JUNEST_HOME}"
if ! umount --force ${JUNEST_HOME}
then
error "Cannot umount directories in ${JUNEST_HOME}"
die "Try to delete ${NAME} using root permissions"
fi
fi
# the CA directories are read only and can be deleted only by changing the mod
chmod -R +w ${JUNEST_HOME}/etc/ca-certificates
if rm_cmd -rf ${JUNEST_HOME}/*
then
info "${NAME} deleted in ${JUNEST_HOME}"
else
error "Error: Cannot delete ${NAME} in ${JUNEST_HOME}"
fi
}
function _check_package(){
if ! pacman -Qq $1 > /dev/null
then
die "Package $1 must be installed"
fi
}
function _install_from_aur(){
local maindir=$1
local pkgname=$2
local installname=$3
mkdir -p ${maindir}/packages/${pkgname}
builtin cd ${maindir}/packages/${pkgname}
$CURL "https://aur.archlinux.org/cgit/aur.git/plain/PKGBUILD?h=${pkgname}"
[ -z "${installname}" ] || $CURL "https://aur.archlinux.org/cgit/aur.git/plain/${installname}?h=${pkgname}"
makepkg -sfc
sudo pacman --noconfirm --root ${maindir}/root -U ${pkgname}*.pkg.tar.xz
}
function build_image_env(){
umask 022
# The function must runs on ArchLinux with non-root privileges.
(( EUID == 0 )) && \
die "You cannot build with root privileges."
_check_package arch-install-scripts
_check_package gcc
_check_package package-query
_check_package git
local disable_validation=$1
local skip_root_tests=$2
local maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t ${CMD}.XXXXXXXXXX)
sudo mkdir -p ${maindir}/root
trap - QUIT EXIT ABRT KILL TERM INT
trap "sudo rm -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT KILL TERM INT
info "Installing pacman and its dependencies..."
# The archlinux-keyring and libunistring are due to missing dependencies declaration in ARM archlinux
# All the essential executables (ln, mkdir, chown, etc) are in coreutils
# yaourt requires sed
sudo pacstrap -G -M -d ${maindir}/root pacman coreutils libunistring archlinux-keyring sed
sudo bash -c "echo 'Server = $DEFAULT_MIRROR' >> ${maindir}/root/etc/pacman.d/mirrorlist"
info "Install ${NAME} script..."
sudo pacman --noconfirm --root ${maindir}/root -S git
_install_from_aur ${maindir} "${CMD}-git" "${CMD}.install"
sudo pacman --noconfirm --root ${maindir}/root -Rsn git
info "Generating the locales..."
# sed command is required for locale-gen
sudo ln -sf /usr/share/zoneinfo/posix/UTC ${maindir}/root/etc/localtime
sudo bash -c "echo 'en_US.UTF-8 UTF-8' >> ${maindir}/root/etc/locale.gen"
sudo ${maindir}/root/opt/junest/bin/jchroot ${maindir}/root locale-gen
sudo bash -c "echo 'LANG = \"en_US.UTF-8\"' >> ${maindir}/root/etc/locale.conf"
info "Generating the metadata info..."
sudo mkdir ${maindir}/root/etc/${CMD}
sudo bash -c "echo 'JUNEST_ARCH=$ARCH' > ${maindir}/root/etc/${CMD}/info"
info "Installing compatibility binaries proot"
sudo mkdir -p ${maindir}/root/opt/proot
builtin cd ${maindir}/root/opt/proot
for arch in ${ARCH_LIST[@]}
do
info "Downloading $PROOT_LINK/proot-$arch ..."
sudo $CURL $PROOT_LINK/proot-$arch
sudo chmod +x proot-$arch
done
info "Installing qemu static binaries"
sudo mkdir -p ${maindir}/root/opt/qemu
builtin cd ${maindir}/root/opt/qemu
for arch in ${ARCH_LIST[@]}
do
if [ "$arch" != "$ARCH" ]
then
info "Downloading qemu-$ARCH-static-$arch ..."
sudo $CURL ${MAIN_REPO}/qemu/$ARCH/qemu-$ARCH-static-$arch
sudo chmod +x qemu-$ARCH-static-$arch
fi
done
# AUR packages requires non-root user to be compiled. proot fakes the user to 10
info "Compiling and installing yaourt..."
_install_from_aur ${maindir} "package-query"
_install_from_aur ${maindir} "yaourt"
# Apply patches for yaourt and makepkg
sudo mkdir -p ${maindir}/root/opt/yaourt/bin/
sudo cp ${maindir}/root/usr/bin/yaourt ${maindir}/root/opt/yaourt/bin/
sudo sed -i -e 's/"--asroot"//' ${maindir}/root/opt/yaourt/bin/yaourt
sudo cp ${maindir}/root/usr/bin/makepkg ${maindir}/root/opt/yaourt/bin/
sudo sed -i -e 's/EUID\s==\s0/false/' ${maindir}/root/opt/yaourt/bin/makepkg
sudo bash -c "echo 'export PATH=/opt/yaourt/bin:\$PATH' > ${maindir}/root/etc/profile.d/${CMD}.sh"
sudo chmod +x ${maindir}/root/etc/profile.d/${CMD}.sh
info "Setting up the pacman keyring (this might take a while!)..."
sudo ${maindir}/root/opt/junest/bin/jchroot ${maindir}/root bash -c \
"pacman-key --init; pacman-key --populate archlinux; [ -e /etc/pacman.d/gnupg/S.gpg-agent ] && gpg-connect-agent -S /etc/pacman.d/gnupg/S.gpg-agent killagent /bye"
sudo rm ${maindir}/root/var/cache/pacman/pkg/*
mkdir -p ${maindir}/output
builtin cd ${maindir}/output
local imagefile="${CMD}-${ARCH}.tar.gz"
info "Compressing image to ${imagefile}..."
sudo $TAR -zcpf ${imagefile} -C ${maindir}/root .
if ! $disable_validation
then
mkdir -p ${maindir}/root_test
$TAR -zxpf ${imagefile} -C "${maindir}/root_test"
check_env "${maindir}/root_test" "${maindir}/root_test/opt/${CMD}/bin/${CMD}" $skip_root_tests
fi
sudo cp ${maindir}/output/${imagefile} ${ORIGIN_WD}
builtin cd ${ORIGIN_WD}
trap - QUIT EXIT ABRT KILL TERM INT
sudo rm -fr "$maindir"
}
function check_env(){
local testdir=$1
local cmd=$2
local skip_root_tests=$3
info "Validating ${NAME} located in ${testdir} using the ${cmd} script..."
echo "Server = ${DEFAULT_MIRROR}" >> ${testdir}/etc/pacman.d/mirrorlist
JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -Syy
# Check most basic executables work
$skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r pacman -Qi pacman 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -- pacman -Qi pacman 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -f -- pacman -Qi pacman 1> /dev/null
$skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r yaourt -V 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -- yaourt -V 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -f -- yaourt -V 1> /dev/null
$skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r /opt/proot/proot-$ARCH --help 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -- /opt/proot/proot-$ARCH --help 1> /dev/null
JUNEST_HOME=${testdir} ${cmd} -f -- /opt/proot/proot-$ARCH --help 1> /dev/null
local repo_package=tree
info "Installing ${repo_package} package from official repo using proot..."
JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -S ${repo_package}
JUNEST_HOME=${testdir} ${cmd} tree
JUNEST_HOME=${testdir} ${cmd} -f tree
local repo_package=iftop
info "Installing ${repo_package} package from official repo using root..."
JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -S ${repo_package}
$skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r iftop -t -s 5
JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -S base-devel
local yaourt_package=tcptraceroute
info "Installing ${yaourt_package} package from AUR repo using proot..."
JUNEST_HOME=${testdir} ${cmd} -f -- yaourt -A --noconfirm -S ${yaourt_package}
$skip_root_tests || JUNEST_HOME=${testdir} sudo -E ${cmd} -r tcptraceroute localhost
info "Removing the previous packages..."
JUNEST_HOME=${testdir} ${cmd} -f pacman --noconfirm -Rsn tcptraceroute tree iftop
}

141
lib/core/build.sh Normal file
View file

@ -0,0 +1,141 @@
#!/usr/bin/env bash
#
# This module contains all build functionalities for JuNest.
#
# Dependencies:
# - lib/utils/utils.sh
# - lib/core/common.sh
#
# vim: ft=sh
function _install_pkg(){
# This function allows to install packages from AUR.
# At the moment is not used.
local maindir=$1
local pkgbuilddir=$2
# Generate a working directory because sources will be downloaded to there
working_dir=$(TMPDIR=/tmp mktemp -d -t junest-wd.XXXXXXXXXX)
cp -R "$pkgbuilddir"/* "$working_dir"
builtin cd "${working_dir}" || return 1
makepkg -sfcd
makepkg --printsrcinfo > "${pkgbuilddir}"/.SRCINFO
sudo pacman --noconfirm --root "${maindir}"/root -U ./*.pkg.tar.*
}
function _prepare() {
# ArchLinux System initialization
prepare_archlinux
# curl is used to download pacman.conf file
sudo pacman -S --noconfirm git arch-install-scripts haveged curl
}
function build_image_env(){
set -x
umask 022
# The function must runs on ArchLinux with non-root privileges.
# This is because installing AUR packages can be done by normal users only.
(( EUID == 0 )) && \
die "You cannot build with root privileges."
_prepare
local disable_validation=$1
local maindir
maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t "${CMD}".XXXXXXXXXX)
sudo mkdir -p "${maindir}"/root
trap - QUIT EXIT ABRT TERM INT
# shellcheck disable=SC2064
trap "sudo rm -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT TERM INT
info "Installing pacman and its dependencies..."
# All the essential executables (ln, mkdir, chown, etc) are in coreutils
# bwrap command belongs to bubblewrap
sudo pacstrap -G -M "${maindir}"/root pacman coreutils bubblewrap
if [[ ${ARCH} != "arm" ]]
then
# x86_64 does not have any mirror set by default...
sudo bash -c "echo 'Server = $DEFAULT_MIRROR' >> ${maindir}/root/etc/pacman.d/mirrorlist"
fi
sudo mkdir -p "${maindir}"/root/run/lock
# For some reasons, pacstrap does not create the pacman.conf file,
# I could not reproduce the issue locally though:
# https://app.travis-ci.com/github/fsquillace/junest/builds/268216346
[[ -e "${maindir}"/root/etc/pacman.conf ]] || sudo curl "https://gitlab.archlinux.org/archlinux/packaging/packages/pacman/-/raw/main/pacman.conf" -o "${maindir}/root/etc/pacman.conf"
# Pacman/pacstrap bug: https://gitlab.archlinux.org/archlinux/packaging/packages/arch-install-scripts/-/issues/3
sudo sed -i '/^DownloadUser = alpm$/d' "${maindir}"/root/etc/pacman.conf
sudo tee -a "${maindir}"/root/etc/pacman.conf <<EOT
[junest]
SigLevel = Optional TrustedOnly
Server = https://raw.githubusercontent.com/fsquillace/junest-repo/master/any
EOT
info "pacman.conf being used:"
cat "${maindir}"/root/etc/pacman.conf
sudo pacman --noconfirm --config "${maindir}"/root/etc/pacman.conf --root "${maindir}"/root -Sy sudo-fake groot-git proot-static qemu-user-static-bin-alt yay-git
echo "Generating the metadata info"
sudo install -d -m 755 "${maindir}/root/etc/${CMD}"
sudo bash -c "echo 'JUNEST_ARCH=$ARCH' > ${maindir}/root/etc/${CMD}/info"
# Related to: https://github.com/fsquillace/junest/issues/305
sudo bash -c "echo 'export FAKEROOTDONTTRYCHOWN=true' > ${maindir}/root/etc/profile.d/junest.sh"
info "Generating the locales..."
# sed command is required for locale-gen but it is required by fakeroot
# and cannot be removed
# localedef (called by locale-gen) requires gzip but it is supposed to be
# already installed as systemd already depends on it
sudo pacman --noconfirm --root "${maindir}"/root -S sed gzip
sudo ln -sf /usr/share/zoneinfo/posix/UTC "${maindir}"/root/etc/localtime
sudo bash -c "echo 'en_US.UTF-8 UTF-8' >> ${maindir}/root/etc/locale.gen"
sudo "${maindir}"/root/bin/groot "${maindir}"/root locale-gen
sudo bash -c "echo LANG=\"en_US.UTF-8\" >> ${maindir}/root/etc/locale.conf"
info "Setting up the pacman keyring (this might take a while!)..."
if [[ $(uname -m) == *"arm"* ]]
then
sudo pacman -S --noconfirm --root "${maindir}"/root archlinuxarm-keyring
else
sudo pacman -S --noconfirm --root "${maindir}"/root archlinux-keyring
fi
sudo mount --bind "${maindir}"/root "${maindir}"/root
sudo arch-chroot "${maindir}"/root bash -c '
set -e
pacman-key --init;
for keyring_file in /usr/share/pacman/keyrings/*.gpg;
do
keyring=$(basename $keyring_file | cut -f 1 -d ".");
pacman-key --populate $keyring;
done;'
sudo umount "${maindir}"/root
sudo rm "${maindir}"/root/var/cache/pacman/pkg/*
# This is needed on system with busybox tar command.
# If the file does not have write permission, the tar command to extract files fails.
sudo chmod -R u+rw "${maindir}"/root/
mkdir -p "${maindir}"/output
builtin cd "${maindir}"/output || return 1
local imagefile="${CMD}-${ARCH}.tar.gz"
info "Compressing image to ${imagefile}..."
sudo "$TAR" -zcpf "${imagefile}" -C "${maindir}"/root .
if ! $disable_validation
then
mkdir -p "${maindir}"/root_test
$TAR -zxpf "${imagefile}" -C "${maindir}/root_test"
JUNEST_HOME="${maindir}/root_test" "${JUNEST_BASE}"/lib/checks/check_all.sh
fi
sudo cp "${maindir}"/output/"${imagefile}" "${ORIGIN_WD}"
builtin cd "${ORIGIN_WD}" || return 1
trap - QUIT EXIT ABRT KILL TERM INT
sudo rm -fr "$maindir"
set +x
}

104
lib/core/chroot.sh Normal file
View file

@ -0,0 +1,104 @@
#!/usr/bin/env bash
#
# This module contains all chroot functionalities for JuNest.
#
# Dependencies:
# - lib/utils/utils.sh
# - lib/core/common.sh
#
# vim: ft=sh
function _run_env_as_xroot(){
local cmd=$1
local backend_args="$2"
local no_copy_files="$3"
shift 3
check_same_arch
local uid=$UID
# SUDO_USER is more reliable compared to SUDO_UID
[[ -z $SUDO_USER ]] || uid=$SUDO_USER:$SUDO_GID
local args=()
[[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")")
# With chown the ownership of the files is assigned to the real user
trap - QUIT EXIT ABRT KILL TERM INT
# shellcheck disable=SC2064
trap "[ -z $uid ] || chown_cmd -R ${uid} ${JUNEST_HOME};" EXIT QUIT ABRT TERM INT
if ! $no_copy_files
then
copy_common_files
fi
# shellcheck disable=SC2086
JUNEST_ENV=1 $cmd $backend_args "$JUNEST_HOME" "${DEFAULT_SH[@]}" "${args[@]}"
}
#######################################
# Run JuNest as real root via GRoot command.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# UID (RO) : The user ID.
# SUDO_USER (RO) : The sudo user ID.
# SUDO_GID (RO) : The sudo group ID.
# DEFAULT_SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# backend_args ($1) : The arguments to pass to backend program
# no_copy_files ($2?) : If false it will copy some files in /etc
# from host to JuNest environment.
# cmd ($3-?) : The command to run inside JuNest environment.
# Default command is defined by DEFAULT_SH variable.
# Returns:
# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different.
# Output:
# - : The command output.
#######################################
function run_env_as_groot(){
check_nested_env
local backend_command="${1:-$GROOT}"
local backend_args="$2"
local no_copy_files="$3"
shift 3
provide_common_bindings
local bindings=${RESULT}
unset RESULT
_run_env_as_xroot "$backend_command $bindings" "$backend_args" "$no_copy_files" "$@"
}
#######################################
# Run JuNest as real root via chroot command.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# UID (RO) : The user ID.
# SUDO_USER (RO) : The sudo user ID.
# SUDO_GID (RO) : The sudo group ID.
# DEFAULT_SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# backend_args ($1) : The arguments to pass to backend program
# no_copy_files ($2?) : If false it will copy some files in /etc
# from host to JuNest environment.
# cmd ($3-?) : The command to run inside JuNest environment.
# Default command is defined by DEFAULT_SH variable.
# Returns:
# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different.
# Output:
# - : The command output.
#######################################
function run_env_as_chroot(){
check_nested_env
local backend_command="${1:-chroot_cmd}"
local backend_args="$2"
local no_copy_files="$3"
shift 3
_run_env_as_xroot "$backend_command" "$backend_args" "$no_copy_files" "$@"
}

335
lib/core/common.sh Normal file
View file

@ -0,0 +1,335 @@
#!/usr/bin/env bash
# shellcheck disable=SC2034
# shellcheck disable=SC1091
#
# This module contains all common functionalities for JuNest.
#
# Dependencies:
# - lib/utils/utils.sh
#
# vim: ft=sh
NAME='JuNest'
CMD='junest'
DESCRIPTION='The Arch Linux based distro that runs upon any Linux distros without root access'
NOT_AVAILABLE_ARCH=102
NOT_EXISTING_FILE=103
ARCHITECTURE_MISMATCH=104
ROOT_ACCESS_ERROR=105
NESTED_ENVIRONMENT=106
VARIABLE_NOT_SET=107
NO_CONFIG_FOUND=108
UNPRIVILEGED_USERNS_DISABLED=109
JUNEST_HOME=${JUNEST_HOME:-~/.${CMD}}
JUNEST_TEMPDIR=${JUNEST_TEMPDIR:-/tmp}
# The update of the variable PATH ensures that the executables are
# found on different locations
PATH=/usr/bin:/bin:/usr/local/bin:/usr/sbin:/sbin:${HOME}/.local/bin:"$PATH"
# The executable uname is essential in order to get the architecture
# of the host system, so a fallback mechanism cannot be used for it.
UNAME="uname"
ARCH_LIST=('x86_64' 'x86' 'arm')
HOST_ARCH=$($UNAME -m)
# To check all available architectures look here:
# https://wiki.archlinux.org/index.php/PKGBUILD#arch
if [[ $HOST_ARCH == "i686" ]] || [[ $HOST_ARCH == "i386" ]]
then
ARCH="x86"
LD_LIB="${JUNEST_HOME}/lib/ld-linux.so.2"
elif [[ $HOST_ARCH == "x86_64" ]]
then
ARCH="x86_64"
LD_LIB="${JUNEST_HOME}/lib64/ld-linux-x86-64.so.2"
elif [[ $HOST_ARCH =~ .*(arm).* ]] || [[ $HOST_ARCH == "aarch64" ]]
then
ARCH="arm"
LD_LIB="${JUNEST_HOME}/lib/ld-linux-armhf.so.3"
else
die "Unknown architecture ${HOST_ARCH}"
fi
MAIN_REPO=https://link.storjshare.io/s/jvb5tgarnjtt565fffa44spvyuga/junest-repo
MAIN_REPO=https://pub-a2af2344e8554f6c807bc3db355ae622.r2.dev
ENV_REPO=${MAIN_REPO}/${CMD}
# shellcheck disable=SC2016
DEFAULT_MIRROR='https://mirror.rackspace.com/archlinux/$repo/os/$arch'
ORIGIN_WD=$(pwd)
################## EXECUTABLES ################
# This section contains all the executables needed for JuNest to run properly.
# They are based on a fallback mechanism that tries to use the executable in
# different locations in the host OS.
# List of executables that are run inside JuNest:
DEFAULT_SH=("/bin/sh" "--login")
# List of executables that are run in the host OS:
BWRAP="${JUNEST_HOME}/usr/bin/bwrap"
PROOT="${JUNEST_HOME}/usr/bin/proot-${ARCH}"
GROOT="${JUNEST_HOME}/usr/bin/groot"
CLASSIC_CHROOT=chroot
WGET="wget --content-disposition --no-check-certificate"
CURL="curl -L -J -O -k"
TAR="tar"
CHOWN="chown"
LN="ln"
RM="rm"
MKDIR="mkdir"
GETENT="getent"
CP="cp"
ID="id"
# Used for checking user namespace in config.gz file
ZGREP="zgrep"
UNSHARE="unshare"
LD_EXEC="$LD_LIB --library-path ${JUNEST_HOME}/usr/lib:${JUNEST_HOME}/lib"
# The following functions attempt first to run the executable in the host OS.
# As a last hope they try to run the same executable available in the JuNest
# image.
function ln_cmd(){
$LN "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$LN "$@"
}
function getent_cmd(){
$GETENT "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$GETENT "$@"
}
function cp_cmd(){
$CP "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$CP "$@"
}
function rm_cmd(){
$RM "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$RM "$@"
}
function chown_cmd(){
$CHOWN "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$CHOWN "$@"
}
function mkdir_cmd(){
$MKDIR "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$MKDIR "$@"
}
function zgrep_cmd(){
# No need for LD_EXEC as zgrep is a POSIX shell script
$ZGREP "$@" || "${JUNEST_HOME}"/usr/bin/$ZGREP "$@"
}
function download_cmd(){
$WGET "$@" || $CURL "$@"
}
function chroot_cmd(){
$CLASSIC_CHROOT "$@" || $LD_EXEC "${JUNEST_HOME}"/usr/bin/$CLASSIC_CHROOT "$@"
}
function unshare_cmd(){
# Most of the distros do not have the `unshare` command updated
# with --user option available.
# Hence, give priority to the `unshare` executable in JuNest image.
# Also, unshare provides an environment in which /bin/sh maps to dash shell,
# therefore it ignores all the remaining DEFAULT_SH arguments (i.e. --login) as
# they are not supported by dash.
if $LD_EXEC "${JUNEST_HOME}"/usr/bin/$UNSHARE --user "${DEFAULT_SH[0]}" "-c" ":"
then
$LD_EXEC "${JUNEST_HOME}"/usr/bin/$UNSHARE "${@}"
elif $UNSHARE --user "${DEFAULT_SH[0]}" "-c" ":"
then
$UNSHARE "$@"
else
die "Error: Something went wrong while executing unshare command. Exiting"
fi
}
function bwrap_cmd(){
if $LD_EXEC "$BWRAP" --dev-bind / / "${DEFAULT_SH[0]}" "-c" ":"
then
$LD_EXEC "$BWRAP" "${@}"
else
die "Error: Something went wrong while executing bwrap command. Exiting"
fi
}
function proot_cmd(){
local proot_args="$1"
shift
# shellcheck disable=SC2086
if ${PROOT} ${proot_args} "${DEFAULT_SH[@]}" "-c" ":"
then
# shellcheck disable=SC2086
${PROOT} ${proot_args} "${@}"
elif PROOT_NO_SECCOMP=1 ${PROOT} ${proot_args} "${DEFAULT_SH[@]}" "-c" ":"
then
warn "Warn: Proot is not properly working. Disabling SECCOMP and expect the application to run slowly in particular when it uses syscalls intensively."
warn "Try to use Linux namespace instead as it is more reliable: junest ns"
PROOT_NO_SECCOMP=1 ${PROOT} ${proot_args} "${@}"
else
die "Error: Something went wrong with proot command. Exiting"
fi
}
############## COMMON FUNCTIONS ###############
#######################################
# Check if the executable is being running inside a JuNest environment.
#
# Globals:
# JUNEST_ENV (RO) : The boolean junest env check
# NESTED_ENVIRONMENT (RO) : The nest env exception
# VARIABLE_NOT_SET (RO) : The var not set exception
# NAME (RO) : The JuNest name
# Arguments:
# None
# Returns:
# VARIABLE_NOT_SET : If no JUNEST_ENV is not properly set
# NESTED_ENVIRONMENT : If the script is executed inside JuNest env
# Output:
# None
#######################################
function check_nested_env() {
if [[ $JUNEST_ENV == "1" ]]
then
die_on_status $NESTED_ENVIRONMENT "Error: Nested ${NAME} environments are not allowed"
elif [[ -n $JUNEST_ENV ]] && [[ $JUNEST_ENV != "0" ]]
then
die_on_status $VARIABLE_NOT_SET "The variable JUNEST_ENV is not properly set"
fi
}
#######################################
# Check if the architecture between Host OS and Guest OS is the same.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home path.
# ARCHITECTURE_MISMATCH (RO) : The arch mismatch exception
# ARCH (RO) : The host OS arch
# JUNEST_ARCH (RO) : The JuNest arch
# Arguments:
# None
# Returns:
# ARCHITECTURE_MISMATCH : If arch between host and guest is not the same
# Output:
# None
#######################################
function check_same_arch() {
source "${JUNEST_HOME}"/etc/junest/info
[ "$JUNEST_ARCH" != "$ARCH" ] && \
die_on_status $ARCHITECTURE_MISMATCH "The host system architecture is not correct: $ARCH != $JUNEST_ARCH"
return 0
}
#######################################
# Provide the proot common binding options for both normal user and fakeroot.
# The list of bindings can be found in `proot --help`. This function excludes
# /etc/mtab file so that it will not give conflicts with the related
# symlink in the image.
#
# Globals:
# HOME (RO) : The home directory.
# RESULT (WO) : Contains the binding options.
# Arguments:
# None
# Returns:
# None
# Output:
# None
#######################################
function provide_common_bindings(){
RESULT=""
local re='(.*):.*'
for bind in "/dev" "/sys" "/proc" "/tmp" "$HOME" "/run/user/$($ID -u)"
do
if [[ $bind =~ $re ]]
then
[ -e "${BASH_REMATCH[1]}" ] && RESULT="-b $bind $RESULT"
else
[ -e "$bind" ] && RESULT="-b $bind $RESULT"
fi
done
return 0
}
#######################################
# Build passwd and group files using getent command.
# If getent fails the function fallbacks by copying the content from /etc/passwd
# and /etc/group.
#
# The generated passwd and group will be stored in $JUNEST_HOME/etc/junest.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# Arguments:
# None
# Returns:
# None
# Output:
# None
#######################################
function copy_passwd_and_group(){
# Enumeration of users/groups is disabled/limited depending on how nsswitch.conf
# is configured.
# Try to at least get the current user via `getent passwd $USER` since it uses
# a more reliable and faster system call (getpwnam(3)).
if ! getent_cmd passwd > "${JUNEST_HOME}"/etc/passwd || \
! getent_cmd passwd "${USER}" >> "${JUNEST_HOME}"/etc/passwd
then
warn "getent command failed or does not exist. Binding directly from /etc/passwd."
copy_file /etc/passwd
fi
if ! getent_cmd group > "${JUNEST_HOME}"/etc/group
then
warn "getent command failed or does not exist. Binding directly from /etc/group."
copy_file /etc/group
fi
return 0
}
function copy_file() {
local file="${1}"
# -f option ensure to remove destination file if it cannot be opened
# https://github.com/fsquillace/junest/issues/284
[[ -r "$file" ]] && cp_cmd -f "$file" "${JUNEST_HOME}/$file"
return 0
}
function copy_common_files() {
copy_file /etc/host.conf
copy_file /etc/hosts
copy_file /etc/nsswitch.conf
copy_file /etc/resolv.conf
return 0
}
function prepare_archlinux() {
local sudo=${1:-sudo}
local pacman_options="--noconfirm --disable-download-timeout"
# shellcheck disable=SC2086
$sudo pacman $pacman_options -Syy
$sudo pacman-key --init
if [[ $(uname -m) == *"arm"* ]]
then
# shellcheck disable=SC2086
$sudo pacman $pacman_options -S archlinuxarm-keyring
$sudo pacman-key --populate archlinuxarm
else
# shellcheck disable=SC2086
$sudo pacman $pacman_options -S archlinux-keyring
$sudo pacman-key --populate archlinux
fi
# shellcheck disable=SC2086
$sudo pacman $pacman_options -Su
}

165
lib/core/namespace.sh Normal file
View file

@ -0,0 +1,165 @@
#!/usr/bin/env bash
#
# This module contains functionalities for accessing to JuNest via bubblewrap.
#
# https://github.com/containers/bubblewrap
#
# Dependencies:
# - lib/utils/utils.sh
# - lib/core/common.sh
#
# vim: ft=sh
# shellcheck disable=SC2027
COMMON_BWRAP_OPTION="--bind "$JUNEST_HOME" / --bind "$HOME" "$HOME" --bind /tmp /tmp --bind /sys /sys --bind /proc /proc --dev-bind-try /dev /dev --bind-try "/run/user/$($ID -u)" "/run/user/$($ID -u)" --unshare-user-try"
CONFIG_PROC_FILE="/proc/config.gz"
CONFIG_BOOT_FILE="/boot/config-$($UNAME -r)"
PROC_USERNS_CLONE_FILE="/proc/sys/kernel/unprivileged_userns_clone"
PROC_USERNS_FILE="/proc/$$/ns/user"
function _is_user_namespace_enabled() {
if [[ -L $PROC_USERNS_FILE ]]
then
return 0
fi
if [[ -e $PROC_USERNS_CLONE_FILE ]]
then
# `-q` option in zgrep may cause a gzip: stdout: Broken pipe
# Use redirect to /dev/null instead
if zgrep_cmd "1" "$PROC_USERNS_CLONE_FILE" > /dev/null
then
return 0
fi
fi
local config_file=""
if [[ -e $CONFIG_PROC_FILE ]]
then
config_file=$CONFIG_PROC_FILE
elif [[ -e $CONFIG_BOOT_FILE ]]
then
config_file=$CONFIG_BOOT_FILE
else
return "$NOT_EXISTING_FILE"
fi
# `-q` option in zgrep may cause a gzip: stdout: Broken pipe
# Use redirect to /dev/null instead
if ! zgrep_cmd "CONFIG_USER_NS=y" "$config_file" > /dev/null
then
return "$NO_CONFIG_FOUND"
fi
return "$UNPRIVILEGED_USERNS_DISABLED"
}
function _check_user_namespace() {
set +e
_is_user_namespace_enabled
case $? in
"$NOT_EXISTING_FILE") warn "Could not understand if user namespace is enabled. No config.gz file found. Proceeding anyway..." ;;
"$NO_CONFIG_FOUND") warn "Unprivileged user namespace is disabled at kernel compile time or kernel too old (<3.8). Proceeding anyway..." ;;
"$UNPRIVILEGED_USERNS_DISABLED") warn "Unprivileged user namespace disabled. Root permissions are required to enable it: sudo sysctl kernel.unprivileged_userns_clone=1" ;;
esac
set -e
}
#######################################
# Run JuNest as fakeroot via bwrap
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# DEFAULT_SH (RO) : Contains the default command to run in JuNest.
# BWRAP (RO): : The location of the bwrap binary.
# Arguments:
# backend_args ($1) : The arguments to pass to bwrap
# no_copy_files ($2?) : If false it will copy some files in /etc
# from host to JuNest environment.
# cmd ($3-?) : The command to run inside JuNest environment.
# Default command is defined by DEFAULT_SH variable.
# Returns:
# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different.
# $ROOT_ACCESS_ERROR : If the user is the real root.
# Output:
# - : The command output.
#######################################
function run_env_as_bwrap_fakeroot(){
check_nested_env
local backend_command="${1:-$BWRAP}"
local backend_args="$2"
local no_copy_files="$3"
shift 3
_check_user_namespace
check_same_arch
if ! $no_copy_files
then
copy_common_files
fi
local args=()
[[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")")
# Fix PATH to /usr/bin to make sudo working and avoid polluting with host related bin paths
# shellcheck disable=SC2086
PATH="/usr/bin" BWRAP="${backend_command}" JUNEST_ENV=1 bwrap_cmd $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 $backend_args sudo "${DEFAULT_SH[@]}" "${args[@]}"
}
#######################################
# Run JuNest as normal user via bwrap.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# DEFAULT_SH (RO) : Contains the default command to run in JuNest.
# BWRAP (RO): : The location of the bwrap binary.
# Arguments:
# backend_args ($1) : The arguments to pass to bwrap
# no_copy_files ($2?) : If false it will copy some files in /etc
# from host to JuNest environment.
# cmd ($3-?) : The command to run inside JuNest environment.
# Default command is defined by DEFAULT_SH variable.
# Returns:
# $ARCHITECTURE_MISMATCH : If host and JuNest architecture are different.
# Output:
# - : The command output.
#######################################
function run_env_as_bwrap_user() {
check_nested_env
local backend_command="${1:-$BWRAP}"
local backend_args="$2"
local no_copy_files="$3"
shift 3
_check_user_namespace
check_same_arch
if ! $no_copy_files
then
copy_common_files
copy_file /etc/hosts.equiv
copy_file /etc/netgroup
copy_file /etc/networks
# No need for localtime as it is setup during the image build
#copy_file /etc/localtime
copy_passwd_and_group
fi
local args=()
[[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")")
# Resets PATH to avoid polluting with host related bin paths
# shellcheck disable=SC2086
PATH='' BWRAP="${backend_command}" JUNEST_ENV=1 bwrap_cmd $COMMON_BWRAP_OPTION $backend_args "${DEFAULT_SH[@]}" "${args[@]}"
}

138
lib/core/proot.sh Normal file
View file

@ -0,0 +1,138 @@
#!/usr/bin/env bash
# shellcheck disable=SC1091
#
# This module contains all proot functionalities for JuNest.
#
# Dependencies:
# - lib/utils/utils.sh
# - lib/core/common.sh
#
# vim: ft=sh
function _run_env_with_proot(){
local backend_command="${1:-$PROOT}"
local backend_args="$2"
shift 2
local args=()
[[ "$1" != "" ]] && args=("-c" "$(insert_quotes_on_spaces "${@}")")
# Resets PATH to avoid polluting with host related bin paths
PATH='' PROOT="${backend_command}" JUNEST_ENV=1 proot_cmd "${backend_args}" "${DEFAULT_SH[@]}" "${args[@]}"
}
function _run_env_with_qemu(){
local backend_command="$1"
local backend_args="$2"
shift 2
source "${JUNEST_HOME}"/etc/junest/info
if [ "$JUNEST_ARCH" != "$ARCH" ]
then
local qemu_bin="qemu-$JUNEST_ARCH-static-$ARCH"
local qemu_symlink="/tmp/${qemu_bin}-$RANDOM"
trap - QUIT EXIT ABRT KILL TERM INT
# shellcheck disable=SC2064
trap "[ -e ${qemu_symlink} ] && rm_cmd -f ${qemu_symlink}" EXIT QUIT ABRT TERM INT
warn "Emulating $NAME via QEMU..."
[[ -e ${qemu_symlink} ]] || \
ln_cmd -s "${JUNEST_HOME}/bin/${qemu_bin}" "${qemu_symlink}"
backend_args="-q ${qemu_symlink} $backend_args"
fi
_run_env_with_proot "${backend_command}" "$backend_args" "${@}"
}
#######################################
# Run JuNest as fakeroot.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# EUID (RO) : The user ID.
# DEFAULT_SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# backend_args ($1) : The arguments to pass to proot
# no_copy_files ($2?) : If false it will copy some files in /etc
# from host to JuNest environment.
# cmd ($3-?) : The command to run inside JuNest environment.
# Default command is defined by DEFAULT_SH variable.
# Returns:
# $ROOT_ACCESS_ERROR : If the user is the real root.
# Output:
# - : The command output.
#######################################
function run_env_as_proot_fakeroot(){
(( EUID == 0 )) && \
die_on_status "$ROOT_ACCESS_ERROR" "You cannot access with root privileges. Use --groot option instead."
check_nested_env
local backend_command="$1"
local backend_args="$2"
local no_copy_files="$3"
shift 3
if ! $no_copy_files
then
copy_common_files
fi
provide_common_bindings
local bindings=${RESULT}
unset RESULT
# An alternative is via -S option:
#_run_env_with_qemu "-S ${JUNEST_HOME} $1" "${@:2}"
_run_env_with_qemu "$backend_command" "-0 ${bindings} -r ${JUNEST_HOME} $backend_args" "$@"
}
#######################################
# Run JuNest as normal user.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# EUID (RO) : The user ID.
# DEFAULT_SH (RO) : Contains the default command to run in JuNest.
# Arguments:
# backend_args ($1) : The arguments to pass to proot
# no_copy_files ($2?) : If false it will copy some files in /etc
# from host to JuNest environment.
# cmd ($3-?) : The command to run inside JuNest environment.
# Default command is defined by DEFAULT_SH variable.
# Returns:
# $ROOT_ACCESS_ERROR : If the user is the real root.
# Output:
# - : The command output.
#######################################
function run_env_as_proot_user(){
(( EUID == 0 )) && \
die_on_status "$ROOT_ACCESS_ERROR" "You cannot access with root privileges. Use --groot option instead."
check_nested_env
local backend_command="$1"
local backend_args="$2"
local no_copy_files="$3"
shift 3
if ! $no_copy_files
then
# Files to bind are visible in `proot --help`.
# This function excludes /etc/mtab file so that
# it will not give conflicts with the related
# symlink in the Arch Linux image.
copy_common_files
copy_file /etc/hosts.equiv
copy_file /etc/netgroup
copy_file /etc/networks
# No need for localtime as it is setup during the image build
#copy_file /etc/localtime
copy_passwd_and_group
fi
provide_common_bindings
local bindings=${RESULT}
unset RESULT
_run_env_with_qemu "$backend_command" "${bindings} -r ${JUNEST_HOME} $backend_args" "$@"
}

164
lib/core/setup.sh Normal file
View file

@ -0,0 +1,164 @@
#!/usr/bin/env bash
#
# This module contains all setup functionalities for JuNest.
#
# Dependencies:
# - lib/utils/utils.sh
# - lib/core/common.sh
#
# vim: ft=sh
#######################################
# Check if the JuNest system is installed in JUNEST_HOME.
#
# Globals:
# JUNEST_HOME (RO) : Contains the JuNest home directory.
# Arguments:
# None
# Returns:
# 0 : If JuNest is installed
# 1 : If JuNest is not installed
# Output:
# None
#######################################
function is_env_installed(){
[[ -d "$JUNEST_HOME" ]] && [[ "$(ls -A "$JUNEST_HOME")" ]] && return 0
return 1
}
function _cleanup_build_directory(){
local maindir=$1
check_not_null "$maindir"
builtin cd "$ORIGIN_WD" || return 1
trap - QUIT EXIT ABRT KILL TERM INT
rm_cmd -fr "$maindir"
}
function _prepare_build_directory(){
local maindir=$1
check_not_null "$maindir"
trap - QUIT EXIT ABRT KILL TERM INT
# shellcheck disable=SC2064
trap "rm_cmd -rf ${maindir}; die \"Error occurred when installing ${NAME}\"" EXIT QUIT ABRT TERM INT
}
function _setup_env(){
local imagepath=$1
check_not_null "$imagepath"
is_env_installed && die "Error: ${NAME} has been already installed in $JUNEST_HOME"
mkdir_cmd -p "${JUNEST_HOME}"
$TAR -zxpf "${imagepath}" -C "${JUNEST_HOME}"
info "${NAME} installed successfully!"
echo
info "Default mirror URL set to: ${DEFAULT_MIRROR}"
info "You can change the pacman mirror URL in /etc/pacman.d/mirrorlist according to your location:"
info " \$EDITOR ${JUNEST_HOME}/etc/pacman.d/mirrorlist"
echo
info "Remember to refresh the package databases from the server:"
info " pacman -Syy"
echo
info "To install packages from AUR follow the wiki here:"
info "https://github.com/fsquillace/junest#install-packages-from-aur"
}
#######################################
# Setup JuNest.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory in which JuNest needs
# to be installed.
# ARCH (RO) : The host architecture.
# JUNEST_TEMPDIR (RO) : The JuNest temporary directory for building
# the JuNest system from the image.
# ENV_REPO (RO) : URL of the site containing JuNest images.
# NAME (RO) : The JuNest name.
# Arguments:
# arch ($1?) : The JuNest architecture image to download.
# Defaults to the host architecture
# Returns:
# $NOT_AVAILABLE_ARCH : If the architecture is not one of the available ones.
# Output:
# None
#######################################
function setup_env(){
local arch=${1:-$ARCH}
contains_element "$arch" "${ARCH_LIST[@]}" || \
die_on_status "$NOT_AVAILABLE_ARCH" "The architecture is not one of: ${ARCH_LIST[*]}"
local maindir
maindir=$(TMPDIR=$JUNEST_TEMPDIR mktemp -d -t "${CMD}".XXXXXXXXXX)
_prepare_build_directory "$maindir"
info "Downloading ${NAME}..."
builtin cd "${maindir}" || return 1
local imagefile=${CMD}-${arch}.tar.gz
download_cmd "${ENV_REPO}/${imagefile}"
info "Installing ${NAME}..."
_setup_env "${maindir}/${imagefile}"
_cleanup_build_directory "${maindir}"
}
#######################################
# Setup JuNest from file.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory in which JuNest needs
# to be installed.
# NAME (RO) : The JuNest name.
# Arguments:
# imagefile ($1) : The JuNest image file.
# Returns:
# $NOT_EXISTING_FILE : If the image file does not exist.
# Output:
# None
#######################################
function setup_env_from_file(){
local imagefile=$1
check_not_null "$imagefile"
[[ ! -e ${imagefile} ]] && die_on_status "$NOT_EXISTING_FILE" "Error: The ${NAME} image file ${imagefile} does not exist"
info "Installing ${NAME} from ${imagefile}..."
_setup_env "${imagefile}"
}
#######################################
# Remove an existing JuNest system.
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory to remove.
# Arguments:
# None
# Returns:
# None
# Output:
# None
#######################################
function delete_env(){
! ask "Are you sure to delete ${NAME} located in ${JUNEST_HOME}" "N" && return
if mountpoint -q "${JUNEST_HOME}"
then
info "There are mounted directories inside ${JUNEST_HOME}"
if ! umount --force "${JUNEST_HOME}"
then
error "Cannot umount directories in ${JUNEST_HOME}"
die "Try to delete ${NAME} using root permissions"
fi
fi
# the CA directories are read only and can be deleted only by changing the mod
chmod -R +w "${JUNEST_HOME}"/etc/ca-certificates
if rm_cmd -rf "${JUNEST_HOME}"
then
info "${NAME} deleted in ${JUNEST_HOME}"
else
error "Error: Cannot delete ${NAME} in ${JUNEST_HOME}"
fi
}

60
lib/core/wrappers.sh Normal file
View file

@ -0,0 +1,60 @@
#!/usr/bin/env bash
#
# Dependencies:
# None
#
# vim: ft=sh
#######################################
# Create bin wrappers
#
# Globals:
# JUNEST_HOME (RO) : The JuNest home directory.
# Arguments:
# force ($1?) : Create bin wrappers even if the bin file exists.
# Defaults to false.
# Returns:
# None
# Output:
# None
#######################################
function create_wrappers() {
local force=${1:-false}
local bin_path=${2:-/usr/bin}
bin_path=${bin_path%/}
mkdir -p "${JUNEST_HOME}${bin_path}_wrappers"
# Arguments inside a variable (i.e. `JUNEST_ARGS`) separated by quotes
# are not recognized normally unless using `eval`. More info here:
# https://github.com/fsquillace/junest/issues/262
# https://github.com/fsquillace/junest/pull/287
cat <<EOF > "${JUNEST_HOME}/usr/bin/junest_wrapper"
#!/usr/bin/env bash
eval "junest_args_array=(\${JUNEST_ARGS:-ns})"
junest "\${junest_args_array[@]}" -- \$(basename \${0}) "\$@"
EOF
chmod +x "${JUNEST_HOME}/usr/bin/junest_wrapper"
cd "${JUNEST_HOME}${bin_path}" || return 1
for file in *
do
[[ -d $file ]] && continue
# Symlinks outside junest appear as broken even though they are correct
# within a junest session. The following do not skip broken symlinks:
[[ -x $file || -L $file ]] || continue
if [[ -e ${JUNEST_HOME}${bin_path}_wrappers/$file ]] && ! $force
then
continue
fi
rm -f "${JUNEST_HOME}${bin_path}_wrappers/$file"
ln -s "${JUNEST_HOME}/usr/bin/junest_wrapper" "${JUNEST_HOME}${bin_path}_wrappers/$file"
done
# Remove wrappers no longer needed
cd "${JUNEST_HOME}${bin_path}_wrappers" || return 1
for file in *
do
[[ -e ${JUNEST_HOME}${bin_path}/$file || -L ${JUNEST_HOME}${bin_path}/$file ]] || rm -f "$file"
done
}

View file

@ -1,93 +0,0 @@
#!/usr/bin/env bash
#
# This file is part of JuNest (https://github.com/fsquillace/junest)
#
# Copyright (c) 2012-2015
#
# This program is free software; you can redistribute it and/or modify it
# under the terms of the GNU Library General Public License as published
# by the Free Software Foundation; either version 2, or (at your option)
# any later version.
#
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
# GNU General Public License for more details.
#
# You should have received a copy of the GNU General Public License
# along with this program. If not, see <http://www.gnu.org/licenses/>.
function echoerr(){
echo "$@" 1>&2
}
function die(){
# $@: msg (mandatory) - str: Message to print
error $@
exit 1
}
function error(){
# $@: msg (mandatory) - str: Message to print
echoerr -e "\033[1;31m$@\033[0m"
}
function warn(){
# $@: msg (mandatory) - str: Message to print
echoerr -e "\033[1;33m$@\033[0m"
}
function info(){
# $@: msg (mandatory) - str: Message to print
echo -e "\033[1;37m$@\033[0m"
}
function ask(){
# $1: question string
# $2: default value - can be either Y, y, N, n (by default Y)
local default="Y"
[ -z $2 ] || default=$(echo "$2" | tr '[:lower:]' '[:upper:]')
local other="n"
[ "$default" == "N" ] && other="y"
local prompt="$1 (${default}/${other})> "
local res="none"
while [ "$res" != "Y" ] && [ "$res" != "N" ] && [ "$res" != "" ];
do
read -p "$prompt" res
res=$(echo "$res" | tr '[:lower:]' '[:upper:]')
done
[ "$res" == "" ] && res="$default"
if [ "$res" == "Y" ]
then
return 0
else
return 1
fi
}
function insert_quotes_on_spaces(){
# It inserts quotes between arguments.
# Useful to preserve quotes on command
# to be used inside sh -c/bash -c
C=''
whitespace="[[:space:]]"
for i in "$@"
do
if [[ $i =~ $whitespace ]]
then
C="$C \"$i\""
else
C="$C $i"
fi
done
echo $C
}
contains_element () {
local e
for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}

205
lib/utils/utils.sh Normal file
View file

@ -0,0 +1,205 @@
#!/usr/bin/env bash
NULL_EXCEPTION=11
WRONG_ANSWER=33
#######################################
# Check if the argument is null.
#
# Globals:
# None
# Arguments:
# argument ($1) : Argument to check.
# Returns:
# 0 : If argument is not null.
# NULL_EXCEPTION : If argument is null.
# Output:
# None
#######################################
function check_not_null() {
[ -z "$1" ] && { error "Error: null argument $1"; return $NULL_EXCEPTION; }
return 0
}
#######################################
# Redirect message to stderr.
#
# Globals:
# None
# Arguments:
# msg ($@): Message to print.
# Returns:
# None
# Output:
# Message printed to stderr.
#######################################
function echoerr() {
echo "$@" 1>&2;
}
#######################################
# Print an error message to stderr and exit program.
#
# Globals:
# None
# Arguments:
# msg ($@) : Message to print.
# Returns:
# 1 : The unique exit status printed.
# Output:
# Message printed to stderr.
#######################################
function die() {
error "$@"
exit 1
}
#######################################
# Print an error message to stderr and exit program with a given status.
#
# Globals:
# None
# Arguments:
# status ($1) : The exit status to use.
# msg ($2-) : Message to print.
# Returns:
# $? : The $status exit status.
# Output:
# Message printed to stderr.
#######################################
function die_on_status() {
status=$1
shift
error "$@"
exit "$status"
}
#######################################
# Print an error message to stderr.
#
# Globals:
# None
# Arguments:
# msg ($@): Message to print.
# Returns:
# None
# Output:
# Message printed to stderr.
#######################################
function error() {
echoerr -e "\033[1;31m$*\033[0m"
}
#######################################
# Print a warn message to stderr.
#
# Globals:
# None
# Arguments:
# msg ($@): Message to print.
# Returns:
# None
# Output:
# Message printed to stderr.
#######################################
function warn() {
# $@: msg (mandatory) - str: Message to print
echoerr -e "\033[1;33m$*\033[0m"
}
#######################################
# Print an info message to stdout.
#
# Globals:
# None
# Arguments:
# msg ($@): Message to print.
# Returns:
# None
# Output:
# Message printed to stdout.
#######################################
function info(){
echo -e "\033[1;36m$*\033[0m"
}
#######################################
# Ask a question and wait to receive an answer from stdin.
# It returns $default_answer if no answer has be received from stdin.
#
# Globals:
# None
# Arguments:
# question ($1) : The question to ask.
# default_answer ($2) : Possible values: 'Y', 'y', 'N', 'n' (default: 'Y')
# Returns:
# 0 : If user replied with either 'Y' or 'y'.
# 1 : If user replied with either 'N' or 'n'.
# WRONG_ANSWER : If `default_answer` is not one of the possible values.
# Output:
# Print the question to ask.
#######################################
function ask(){
local question=$1
local default_answer=$2
check_not_null "$question"
if [ -n "$default_answer" ]
then
local answers="Y y N n"
[[ "$answers" =~ $default_answer ]] || { error "The default answer: $default_answer is wrong."; return $WRONG_ANSWER; }
fi
local default="Y"
[ -z "$default_answer" ] || default=$(echo "$default_answer" | tr '[:lower:]' '[:upper:]')
local other="n"
[ "$default" == "N" ] && other="y"
local prompt
prompt=$(info "$question (${default}/${other})> ")
local res="none"
while [ "$res" != "Y" ] && [ "$res" != "N" ] && [ "$res" != "" ];
do
read -r -p "$prompt" res
res=$(echo "$res" | tr '[:lower:]' '[:upper:]')
done
[ "$res" == "" ] && res="$default"
[ "$res" == "Y" ]
}
function insert_quotes_on_spaces(){
# It inserts quotes between arguments.
# Useful to preserve quotes on command
# to be used inside sh -c/bash -c
local C=""
whitespace="[[:space:]]"
for i in "$@"
do
if [[ $i =~ $whitespace ]]
then
temp_C="\"$i\""
else
temp_C="$i"
fi
# Handle edge case when C is empty to avoid adding an extra space
if [[ -z $C ]]
then
C="$temp_C"
else
C="$C $temp_C"
fi
done
echo "$C"
}
contains_element () {
local e
for e in "${@:2}"; do [[ "$e" == "$1" ]] && return 0; done
return 1
}

22
tests/checkstyle/checkstyle.sh Executable file
View file

@ -0,0 +1,22 @@
#!/usr/bin/env bash
# shellcheck disable=SC1091
source "$(dirname "$0")/../utils/utils.sh"
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
function test_check_no_tabs(){
assertCommandFailOnStatus 1 grep -R "$(printf '\t')" "$(dirname "$0")"/../../bin/*
assertEquals "" "$(cat "$STDOUTF")"
assertEquals "" "$(cat "$STDERRF")"
assertCommandFailOnStatus 1 grep -R "$(printf '\t')" "$(dirname "$0")"/../../lib/*
assertEquals "" "$(cat "$STDOUTF")"
assertEquals "" "$(cat "$STDERRF")"
}
source "$(dirname "$0")"/../utils/shunit2

View file

@ -1,7 +0,0 @@
tests_succeded=true
for tst in $(ls $(dirname $0)/test_* | grep -v $(basename $0))
do
$tst || tests_succeded=false
done
$tests_succeded

View file

@ -1,202 +0,0 @@
#!/bin/bash
source $(dirname $0)/../bin/junest -h &> /dev/null
# Disable the exiterr
set +e
function setUp(){
function is_env_installed(){
return 0
}
}
## Mock functions ##
function usage(){
echo "usage"
}
function version(){
echo "version"
}
function build_image_env(){
local disable_validation=$1
local skip_root_tests=$2
echo "build_image_env($disable_validation,$skip_root_tests)"
}
function check_env(){
local env_home=$1
local cmd_script=$2
local skip_root_tests=$3
echo "check_env($env_home,$cmd_script,$skip_root_tests)"
}
function delete_env(){
echo "delete_env"
}
function setup_env_from_file(){
echo "setup_env_from_file($1)"
}
function setup_env(){
echo "setup_env($1)"
}
function run_env_as_fakeroot(){
local proot_args="$1"
shift
echo "run_env_as_fakeroot($proot_args,$@)"
}
function run_env_as_root(){
echo "run_env_as_root $@"
}
function run_env_as_user(){
local proot_args="$1"
shift
echo "run_env_as_user($proot_args,$@)"
}
function wrap_env(){
parse_arguments "$@"
check_cli
execute_operation
}
function test_help(){
local output=$(wrap_env -h)
assertEquals $output "usage"
local output=$(wrap_env --help)
assertEquals $output "usage"
}
function test_version(){
local output=$(wrap_env -v)
assertEquals $output "version"
local output=$(wrap_env --version)
assertEquals $output "version"
}
function test_build_image_env(){
local output=$(wrap_env -b)
assertEquals $output "build_image_env(false,false)"
local output=$(wrap_env --build-image)
assertEquals $output "build_image_env(false,false)"
local output=$(wrap_env -b -s)
assertEquals $output "build_image_env(false,true)"
local output=$(wrap_env -b -n)
assertEquals $output "build_image_env(true,false)"
local output=$(wrap_env -b -n -s)
assertEquals $output "build_image_env(true,true)"
local output=$(wrap_env --build-image --disable-validation --skip-root-tests)
assertEquals $output "build_image_env(true,true)"
}
function test_check_env(){
local output=$(wrap_env -c myscript)
assertEquals $output "check_env(${JUNEST_HOME},myscript,false)"
local output=$(wrap_env --check myscript)
assertEquals $output "check_env(${JUNEST_HOME},myscript,false)"
local output=$(wrap_env -c myscript -s)
assertEquals $output "check_env(${JUNEST_HOME},myscript,true)"
local output=$(wrap_env --check myscript --skip-root-tests)
assertEquals $output "check_env(${JUNEST_HOME},myscript,true)"
}
function test_delete_env(){
local output=$(wrap_env -d)
assertEquals $output "delete_env"
local output=$(wrap_env --delete)
assertEquals $output "delete_env"
}
#function test_setup_env_from_file(){
#local output=$(wrap_env -i myimage)
#assertEquals $output "setup_env_from_file(myimage)"
#local output=$(wrap_env --setup-from-file myimage)
#assertEquals $output "setup_env_from_file(myimage)"
#}
function test_setup_env_from_file(){
is_env_installed(){
return 1
}
local output=$(wrap_env -i myimage)
assertEquals "$output" "$(echo -e "setup_env_from_file(myimage)\nrun_env_as_user(,)")"
local output=$(wrap_env --setup-from-file myimage)
assertEquals "$output" "$(echo -e "setup_env_from_file(myimage)\nrun_env_as_user(,)")"
is_env_installed(){
return 0
}
$(wrap_env -i myimage 2> /dev/null)
assertEquals 1 $?
}
function test_setup_env(){
is_env_installed(){
return 1
}
local output=$(wrap_env -a arm)
assertEquals "$output" "$(echo -e "setup_env(arm)\nrun_env_as_user(,)")"
local output=$(wrap_env --arch arm)
assertEquals "$output" "$(echo -e "setup_env(arm)\nrun_env_as_user(,)")"
local output=$(wrap_env)
assertEquals "$output" "$(echo -e "setup_env()\nrun_env_as_user(,)")"
is_env_installed(){
return 0
}
$(wrap_env -a arm 2> /dev/null)
assertEquals 1 $?
}
function test_run_env_as_fakeroot(){
local output=$(wrap_env -f)
assertEquals $output "run_env_as_fakeroot(,)"
local output=$(wrap_env --fakeroot)
assertEquals $output "run_env_as_fakeroot(,)"
local output=$(wrap_env -f -p "-b arg")
assertEquals "${output[@]}" "run_env_as_fakeroot(-b arg,)"
local output=$(wrap_env -f -p "-b arg" -- command -kv)
assertEquals "${output[@]}" "run_env_as_fakeroot(-b arg,command -kv)"
local output=$(wrap_env -f command --as)
assertEquals "${output[@]}" "run_env_as_fakeroot(,command --as)"
$(wrap_env -a "myarch" -f command --as 2> /dev/null)
assertEquals 1 $?
}
function test_run_env_as_user(){
local output=$(wrap_env)
assertEquals $output "run_env_as_user(,)"
local output=$(wrap_env -p "-b arg")
assertEquals "$output" "run_env_as_user(-b arg,)"
local output=$(wrap_env -p "-b arg" -- command -ll)
assertEquals "$output" "run_env_as_user(-b arg,command -ll)"
local output=$(wrap_env command -ls)
assertEquals "$output" "run_env_as_user(,command -ls)"
$(wrap_env -a "myarch" -- command -ls 2> /dev/null)
assertEquals 1 $?
}
function test_run_env_as_root(){
local output=$(wrap_env -r)
assertEquals $output "run_env_as_root"
local output=$(wrap_env -r command)
assertEquals "${output[@]}" "run_env_as_root command"
}
function test_check_cli(){
$(wrap_env -b -h 2> /dev/null)
assertEquals $? 1
$(wrap_env -b -c 2> /dev/null)
assertEquals $? 1
$(wrap_env -d -s 2> /dev/null)
assertEquals $? 1
$(wrap_env -n -v 2> /dev/null)
assertEquals $? 1
$(wrap_env -d -r 2> /dev/null)
assertEquals $? 1
$(wrap_env -h -f 2> /dev/null)
assertEquals $? 1
$(wrap_env -v -i fsd 2> /dev/null)
assertEquals $? 1
$(wrap_env -f -r 2> /dev/null)
assertEquals $? 1
$(wrap_env -p args -v 2> /dev/null)
assertEquals $? 1
$(wrap_env -a arch -v 2> /dev/null)
assertEquals $? 1
$(wrap_env -d args 2> /dev/null)
assertEquals $? 1
}
source $(dirname $0)/shunit2

View file

@ -1,362 +0,0 @@
#!/bin/bash
function oneTimeSetUp(){
[ -z "$SKIP_ROOT_TESTS" ] && SKIP_ROOT_TESTS=0
CURRPWD=$PWD
ENV_MAIN_HOME=/tmp/envtesthome
[ -e $ENV_MAIN_HOME ] || JUNEST_HOME=$ENV_MAIN_HOME bash --rcfile "$(dirname $0)/../lib/core.sh" -ic "setup_env"
JUNEST_HOME=""
}
function install_mini_env(){
cp -rfa $ENV_MAIN_HOME/* $JUNEST_HOME
}
function setUp(){
cd $CURRPWD
JUNEST_HOME=$(TMPDIR=/tmp mktemp -d -t envhome.XXXXXXXXXX)
JUNEST_BASE="$CURRPWD/$(dirname $0)/.."
source "${JUNEST_BASE}/lib/core.sh"
ORIGIN_WD=$(TMPDIR=/tmp mktemp -d -t envowd.XXXXXXXXXX)
cd $ORIGIN_WD
JUNEST_TEMPDIR=$(TMPDIR=/tmp mktemp -d -t envtemp.XXXXXXXXXX)
set +e
trap - QUIT EXIT ABRT KILL TERM INT
trap "rm -rf ${JUNEST_HOME}; rm -rf ${ORIGIN_WD}; rm -rf ${JUNEST_TEMPDIR}" EXIT QUIT ABRT KILL TERM INT
}
function tearDown(){
# the CA directories are read only and can be deleted only by changing the mod
[ -d ${JUNEST_HOME}/etc/ca-certificates ] && chmod -R +w ${JUNEST_HOME}/etc/ca-certificates
rm -rf $JUNEST_HOME
rm -rf $ORIGIN_WD
rm -rf $JUNEST_TEMPDIR
trap - QUIT EXIT ABRT KILL TERM INT
}
function test_is_env_installed(){
is_env_installed
assertEquals $? 1
touch $JUNEST_HOME/just_file
is_env_installed
assertEquals $? 0
}
function test_download(){
WGET=/bin/true
CURL=/bin/false
download_cmd
assertEquals $? 0
WGET=/bin/false
CURL=/bin/true
download_cmd
assertEquals $? 0
$(WGET=/bin/false CURL=/bin/false download_cmd something 2> /dev/null)
assertEquals $? 1
}
function test_ln(){
install_mini_env
touch ln_file
ln_cmd -s ln_file new_file
assertEquals $? 0
assertTrue "[ -e new_file ]"
rm new_file
touch ln_file
OLDPATH="$PATH"
PATH=""
ln_cmd -s ln_file new_file 2> /dev/null
local ret=$?
PATH="$OLDPATH"
assertEquals $ret 0
assertTrue "[ -e new_file ]"
}
function test_rm(){
install_mini_env
touch rm_file
rm_cmd rm_file
assertEquals $? 0
assertTrue "[ ! -e rm_file ]"
touch rm_file
OLDPATH="$PATH"
PATH=""
rm_cmd rm_file 2> /dev/null
local ret=$?
PATH="$OLDPATH"
assertEquals $ret 0
assertTrue "[ ! -e rm_file ]"
}
function test_chown(){
install_mini_env
local id=$(id -u)
touch chown_file
chown_cmd $id chown_file
assertEquals $? 0
touch chown_file
OLDPATH="$PATH"
PATH=""
chown_cmd $id chown_file 2> /dev/null
local ret=$?
PATH="$OLDPATH"
assertEquals $ret 0
}
function test_mkdir(){
install_mini_env
mkdir_cmd -p new_dir/new_dir
assertEquals $? 0
assertTrue "[ -d new_dir/new_dir ]"
rm -rf new_dir
OLDPATH="$PATH"
PATH=""
mkdir_cmd -p new_dir/new_dir 2> /dev/null
local ret=$?
PATH="$OLDPATH"
assertEquals $ret 0
assertTrue "[ -d new_dir/new_dir ]"
}
function test_setup_env(){
wget_mock(){
# Proof that the setup is happening
# inside $JUNEST_TEMPDIR
local cwd=${PWD#${JUNEST_TEMPDIR}}
local parent_dir=${PWD%${cwd}}
assertEquals "$JUNEST_TEMPDIR" "${parent_dir}"
touch file
tar -czvf ${CMD}-${ARCH}.tar.gz file
}
WGET=wget_mock
setup_env 1> /dev/null
assertTrue "[ -e $JUNEST_HOME/file ]"
assertTrue "[ -e $JUNEST_HOME/run/lock ]"
$(setup_env "noarch" 2> /dev/null)
assertEquals 1 $?
}
function test_setup_env_from_file(){
touch file
tar -czvf ${CMD}-${ARCH}.tar.gz file 1> /dev/null
setup_env_from_file ${CMD}-${ARCH}.tar.gz &> /dev/null
assertTrue "[ -e $JUNEST_HOME/file ]"
assertTrue "[ -e $JUNEST_HOME/run/lock ]"
$(setup_env_from_file noexist.tar.gz 2> /dev/null)
assertEquals $? 1
}
function test_setup_env_from_file_with_absolute_path(){
touch file
tar -czvf ${CMD}-${ARCH}.tar.gz file 1> /dev/null
setup_env_from_file ${ORIGIN_WD}/${CMD}-${ARCH}.tar.gz &> /dev/null
assertTrue "[ -e $JUNEST_HOME/file ]"
assertTrue "[ -e $JUNEST_HOME/run/lock ]"
}
function test_run_env_as_root(){
[ $SKIP_ROOT_TESTS -eq 1 ] && return
install_mini_env
CHROOT="sudo $CHROOT"
CLASSIC_CHROOT="sudo $CLASSIC_CHROOT"
CHOWN="sudo $CHOWN"
local output=$(run_env_as_root pwd)
assertEquals "/" "$output"
run_env_as_root [ -e /run/lock ]
assertEquals 0 $?
run_env_as_root [ -e $HOME ]
assertEquals 0 $?
# test that normal user has ownership of the files created by root
run_env_as_root touch /a_root_file
# This ensure that the trap will be executed
kill -TERM $$
local output=$(run_env_as_root stat -c '%u' /a_root_file)
assertEquals "$UID" "$output"
SH=("sh" "--login" "-c" "type -t type")
local output=$(run_env_as_root)
assertEquals "builtin" "$output"
SH=("sh" "--login" "-c" "[ -e /run/lock ]")
run_env_as_root
assertEquals 0 $?
SH=("sh" "--login" "-c" "[ -e $HOME ]")
run_env_as_root
assertEquals 0 $?
}
function test_run_env_as_root_different_arch(){
[ $SKIP_ROOT_TESTS -eq 1 ] && return
install_mini_env
echo "JUNEST_ARCH=XXX" > ${JUNEST_HOME}/etc/junest/info
$(run_env_as_root pwd 2> /dev/null)
assertEquals 1 $?
}
function test_run_env_as_classic_root(){
[ $SKIP_ROOT_TESTS -eq 1 ] && return
install_mini_env
CHROOT="sudo unknowncommand"
CLASSIC_CHROOT="sudo $CLASSIC_CHROOT"
CHOWN="sudo $CHOWN"
local output=$(run_env_as_root pwd 2> /dev/null)
assertEquals "/" "$output"
run_env_as_root [ -e /run/lock ] 2> /dev/null
assertEquals 0 $?
}
function test_run_env_as_junest_root(){
[ $SKIP_ROOT_TESTS -eq 1 ] && return
install_mini_env
CHROOT="sudo unknowncommand"
CLASSIC_CHROOT="sudo unknowncommand"
LD_EXEC="sudo $LD_EXEC"
CHOWN="sudo $CHOWN"
local output=$(run_env_as_root pwd 2> /dev/null)
assertEquals "/" "$output"
run_env_as_root [ -e /run/lock ] 2> /dev/null
assertEquals 0 $?
run_env_as_root [ -e $HOME ] 2> /dev/null
assertEquals 1 $?
}
function test_run_env_as_user(){
install_mini_env
local output=$(run_env_as_user "-k 3.10" "/usr/bin/mkdir" "-v" "/newdir2" | awk -F: '{print $1}')
assertEquals "$output" "/usr/bin/mkdir"
assertTrue "[ -e $JUNEST_HOME/newdir2 ]"
SH=("/usr/bin/echo")
local output=$(run_env_as_user "-k 3.10")
assertEquals "-c :" "$output"
}
function test_run_env_as_proot_mtab(){
install_mini_env
$(run_env_as_fakeroot "-k 3.10" "echo")
assertTrue "[ -e $JUNEST_HOME/etc/mtab ]"
$(run_env_as_user "-k 3.10" "echo")
assertTrue "[ ! -e $JUNEST_HOME/etc/mtab ]"
}
function test_run_env_as_root_mtab(){
[ $SKIP_ROOT_TESTS -eq 1 ] && return
install_mini_env
CHROOT="sudo $CHROOT"
CLASSIC_CHROOT="sudo $CLASSIC_CHROOT"
CHOWN="sudo $CHOWN"
$(run_env_as_root "echo")
assertTrue "[ ! -e $JUNEST_HOME/etc/mtab ]"
}
function test_run_env_with_quotes(){
install_mini_env
local output=$(run_env_as_user "-k 3.10" "bash" "-c" "/usr/bin/mkdir -v /newdir2" | awk -F: '{print $1}')
assertEquals "/usr/bin/mkdir" "$output"
assertTrue "[ -e $JUNEST_HOME/newdir2 ]"
}
function test_run_env_as_user_proot_args(){
install_mini_env
run_env_as_user "--help" "" &> /dev/null
assertEquals 0 $?
mkdir $JUNEST_TEMPDIR/newdir
touch $JUNEST_TEMPDIR/newdir/newfile
run_env_as_user "-b $JUNEST_TEMPDIR/newdir:/newdir -k 3.10" "ls" "-l" "/newdir/newfile" &> /dev/null
assertEquals 0 $?
$(_run_env_with_proot --helps 2> /dev/null)
assertEquals 1 $?
}
function test_run_env_with_proot_compat(){
PROOT_COMPAT="/bin/true"
_run_env_with_proot "" "" &> /dev/null
assertEquals 0 $?
$(PROOT_COMPAT="/bin/false" _run_env_with_proot --helps 2> /dev/null)
assertEquals 1 $?
}
function test_run_env_with_proot_as_root(){
[ $SKIP_ROOT_TESTS -eq 1 ] && return
install_mini_env
$(sudo run_env_as_user 2> /dev/null)
assertEquals 1 $?
$(sudo run_env_as_fakeroot 2> /dev/null)
assertEquals 1 $?
}
function test_run_proot_seccomp(){
envv(){
env
}
PROOT_COMPAT=envv
local output=$(proot_cmd | grep "^PROOT_NO_SECCOMP")
assertEquals "" "$output"
envv(){
env | grep "^PROOT_NO_SECCOMP"
}
PROOT_COMPAT=envv
local output=$(proot_cmd | grep "^PROOT_NO_SECCOMP")
# The variable PROOT_NO_SECCOMP will be produced
# twice due to the fallback mechanism
assertEquals "PROOT_NO_SECCOMP=1
PROOT_NO_SECCOMP=1" "$output"
}
function test_run_env_as_fakeroot(){
install_mini_env
local output=$(run_env_as_fakeroot "-k 3.10" "id" | awk '{print $1}')
assertEquals "uid=0(root)" "$output"
}
function test_delete_env(){
install_mini_env
echo "N" | delete_env 1> /dev/null
is_env_installed
assertEquals 0 $?
echo "Y" | delete_env 1> /dev/null
is_env_installed
assertEquals 1 $?
}
function test_nested_env(){
install_mini_env
JUNEST_ENV=1 bash -ic "source $CURRPWD/$(dirname $0)/../lib/core.sh" &> /dev/null
assertEquals 1 $?
}
source $(dirname $0)/shunit2

View file

@ -1,72 +0,0 @@
#!/bin/bash
source "$(dirname $0)/../lib/util.sh"
# Disable the exiterr
set +e
function test_echoerr(){
local actual=$(echoerr "Test" 2>&1)
assertEquals "$actual" "Test"
}
function test_error(){
local actual=$(error "Test" 2>&1)
local expected=$(echo -e "\033[1;31mTest\033[0m")
assertEquals "$actual" "$expected"
}
function test_warn(){
local actual=$(warn "Test" 2>&1)
local expected=$(echo -e "\033[1;33mTest\033[0m")
assertEquals "$actual" "$expected"
}
function test_info(){
local actual=$(info "Test")
local expected=$(echo -e "\033[1;37mTest\033[0m")
assertEquals "$actual" "$expected"
}
function test_die(){
local actual=$(die "Test" 2>&1)
local expected=$(echo -e "\033[1;31mTest\033[0m")
assertEquals "$actual" "$expected"
$(die Dying 2> /dev/null)
assertEquals $? 1
}
function test_ask(){
echo "Y" | ask "Test" &> /dev/null
assertEquals $? 0
echo "y" | ask "Test" &> /dev/null
assertEquals $? 0
echo "N" | ask "Test" &> /dev/null
assertEquals $? 1
echo "n" | ask "Test" &> /dev/null
assertEquals $? 1
echo -e "\n" | ask "Test" &> /dev/null
assertEquals $? 0
echo -e "\n" | ask "Test" "N" &> /dev/null
assertEquals $? 1
echo -e "asdf\n\n" | ask "Test" "N" &> /dev/null
assertEquals $? 1
}
function test_insert_quotes_on_spaces(){
local actual=$(insert_quotes_on_spaces this is "a test")
assertEquals "this is \"a test\"" "$actual"
local actual=$(insert_quotes_on_spaces this is 'a test')
assertEquals "this is \"a test\"" "$actual"
}
function test_contains_element(){
array=("something to search for" "a string" "test2000")
contains_element "a string" "${array[@]}"
assertEquals "$?" "0"
contains_element "blabla" "${array[@]}"
assertEquals "$?" "1"
}
source $(dirname $0)/shunit2

125
tests/unit-tests/test-chroot.sh Executable file
View file

@ -0,0 +1,125 @@
#!/bin/bash
# shellcheck disable=SC1091
JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..)
source "$JUNEST_ROOT/tests/utils/utils.sh"
source "$JUNEST_ROOT/lib/utils/utils.sh"
source "$JUNEST_ROOT/lib/core/common.sh"
source "$JUNEST_ROOT/lib/core/chroot.sh"
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
function setUp(){
cwdSetUp
junestSetUp
init_mocks
}
function tearDown(){
junestTearDown
cwdTearDown
}
function init_mocks() {
chroot_cmd() {
# shellcheck disable=SC2317
[ "$JUNEST_ENV" != "1" ] && return 1
# shellcheck disable=SC2317
echo "chroot_cmd $*"
}
# shellcheck disable=SC2034
GROOT=chroot_cmd
mychroot() {
# shellcheck disable=SC2317
echo mychroot "$*"
}
}
function test_run_env_as_groot_cmd(){
assertCommandSuccess run_env_as_groot "" "" "false" pwd
assertEquals "chroot_cmd -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")"
}
function test_run_env_as_groot_no_cmd(){
assertCommandSuccess run_env_as_groot "" "" "false" ""
assertEquals "chroot_cmd -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")"
}
function test_run_env_as_groot_with_backend_command(){
assertCommandSuccess run_env_as_groot "mychroot" "" "false" ""
assertEquals "mychroot -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")"
}
function test_run_env_as_groot_no_copy(){
assertCommandSuccess run_env_as_groot "" "" "true" pwd
assertEquals "chroot_cmd -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")"
[[ ! -e ${JUNEST_HOME}/etc/hosts ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/host.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/nsswitch.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/resolv.conf ]]
assertEquals 0 $?
}
function test_run_env_as_groot_nested_env(){
JUNEST_ENV=1
assertCommandFailOnStatus 106 run_env_as_groot "" "" "false" ""
unset JUNEST_ENV
}
function test_run_env_as_groot_cmd_with_backend_args(){
assertCommandSuccess run_env_as_groot "" "-n -b /home/blah" "false" pwd
assertEquals "chroot_cmd -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -n -b /home/blah $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")"
}
function test_run_env_as_chroot_cmd(){
assertCommandSuccess run_env_as_chroot "" "" "false" pwd
assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")"
}
function test_run_env_as_chroot_no_cmd(){
assertCommandSuccess run_env_as_chroot "" "" "false" ""
assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")"
}
function test_run_env_as_chroot_with_backend_command(){
assertCommandSuccess run_env_as_chroot "mychroot" "" "false" ""
assertEquals "mychroot $JUNEST_HOME /bin/sh --login" "$(cat "$STDOUTF")"
}
function test_run_env_as_chroot_no_copy(){
assertCommandSuccess run_env_as_chroot "" "" "true" pwd
assertEquals "chroot_cmd $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")"
[[ ! -e ${JUNEST_HOME}/etc/hosts ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/host.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/nsswitch.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/resolv.conf ]]
assertEquals 0 $?
}
function test_run_env_as_choot_nested_env(){
JUNEST_ENV=1
assertCommandFailOnStatus 106 run_env_as_chroot "" "" "false" ""
unset JUNEST_ENV
}
function test_run_env_as_chroot_cmd_with_backend_args(){
assertCommandSuccess run_env_as_chroot "" "-n -b /home/blah" "false" pwd
assertEquals "chroot_cmd -n -b /home/blah $JUNEST_HOME /bin/sh --login -c pwd" "$(cat "$STDOUTF")"
}
source "$JUNEST_ROOT"/tests/utils/shunit2

242
tests/unit-tests/test-common.sh Executable file
View file

@ -0,0 +1,242 @@
#!/bin/bash
# shellcheck disable=SC1091
JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..)
source "$JUNEST_ROOT/tests/utils/utils.sh"
source "$JUNEST_ROOT/lib/utils/utils.sh"
source "$JUNEST_ROOT/lib/core/common.sh"
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
junestSetUp
}
function oneTimeTearDown(){
junestTearDown
}
function setUp(){
ld_exec_mock() {
# shellcheck disable=SC2317
echo "ld_exec $*"
}
# shellcheck disable=SC2317
ld_exec_mock_false() {
echo "ld_exec $*"
return 1
}
# shellcheck disable=SC2034
LD_EXEC=ld_exec_mock
unshare_mock() {
# shellcheck disable=SC2317
echo "unshare $*"
}
# shellcheck disable=SC2034
UNSHARE=unshare_mock
# shellcheck disable=SC2317
bwrap_mock() {
echo "bwrap $*"
}
# shellcheck disable=SC2034
BWRAP=bwrap_mock
}
function test_ln(){
LN="echo" assertCommandSuccess ln_cmd -s ln_file new_file
assertEquals "-s ln_file new_file" "$(cat "$STDOUTF")"
LN=false assertCommandSuccess ln_cmd -s ln_file new_file
assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false -s ln_file new_file" "$(cat "$STDOUTF")"
LN=false LD_EXEC=false assertCommandFail ln_cmd
}
function test_getent(){
GETENT="echo" assertCommandSuccess getent_cmd passwd
assertEquals "passwd" "$(cat "$STDOUTF")"
GETENT="false" assertCommandSuccess getent_cmd passwd
assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false passwd" "$(cat "$STDOUTF")"
GETENT=false LD_EXEC=false assertCommandFail getent_cmd
}
function test_cp(){
CP="echo" assertCommandSuccess cp_cmd passwd
assertEquals "passwd" "$(cat "$STDOUTF")"
CP=false assertCommandSuccess cp_cmd passwd
assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false passwd" "$(cat "$STDOUTF")"
CP=false LD_EXEC=false assertCommandFail cp_cmd
}
function test_download(){
WGET=/bin/true
CURL=/bin/false
assertCommandSuccess download_cmd
# shellcheck disable=SC2034
WGET=/bin/false
# shellcheck disable=SC2034
CURL=/bin/true
assertCommandSuccess download_cmd
WGET=/bin/false CURL=/bin/false assertCommandFail download_cmd
}
function test_rm(){
RM="echo" assertCommandSuccess rm_cmd rm_file
assertEquals "rm_file" "$(cat "$STDOUTF")"
RM="false" assertCommandSuccess rm_cmd rm_file
assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false rm_file" "$(cat "$STDOUTF")"
RM=false LD_EXEC=false assertCommandFail rm_cmd rm_file
}
function test_chown(){
local id
id=$(id -u)
CHOWN="echo" assertCommandSuccess chown_cmd "$id" chown_file
assertEquals "$id chown_file" "$(cat "$STDOUTF")"
CHOWN="false" assertCommandSuccess chown_cmd "$id" chown_file
assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false $id chown_file" "$(cat "$STDOUTF")"
CHOWN=false LD_EXEC=false assertCommandFail chown_cmd "$id" chown_file
}
function test_mkdir(){
MKDIR="echo" assertCommandSuccess mkdir_cmd -p new_dir/new_dir
assertEquals "-p new_dir/new_dir" "$(cat "$STDOUTF")"
MKDIR=false assertCommandSuccess mkdir_cmd -p new_dir/new_dir
assertEquals "ld_exec ${JUNEST_HOME}/usr/bin/false -p new_dir/new_dir" "$(cat "$STDOUTF")"
MKDIR=false LD_EXEC=false assertCommandFail mkdir_cmd -p new_dir/new_dir
}
function test_zgrep(){
ZGREP="echo" assertCommandSuccess zgrep_cmd new_file
assertEquals "new_file" "$(cat "$STDOUTF")"
mkdir -p "${JUNEST_HOME}"/usr/bin
touch "${JUNEST_HOME}"/usr/bin/false
chmod +x "${JUNEST_HOME}"/usr/bin/false
echo -e "#!/bin/bash\necho zgrep" > "${JUNEST_HOME}"/usr/bin/false
ZGREP=false assertCommandSuccess zgrep_cmd new_file
assertEquals "zgrep" "$(cat "$STDOUTF")"
echo -e "#!/bin/bash\nexit 1" > "${JUNEST_HOME}"/usr/bin/false
ZGREP=false assertCommandFail zgrep_cmd new_file
}
function test_unshare(){
assertCommandSuccess unshare_cmd new_program
assertEquals "$(echo -e "ld_exec ${JUNEST_HOME}/usr/bin/$UNSHARE --user /bin/sh -c :\nld_exec ${JUNEST_HOME}/usr/bin/$UNSHARE new_program")" "$(cat "$STDOUTF")"
LD_EXEC=ld_exec_mock_false assertCommandSuccess unshare_cmd new_program
assertEquals "$(echo -e "ld_exec ${JUNEST_HOME}/usr/bin/unshare_mock --user /bin/sh -c :\nunshare --user /bin/sh -c :\nunshare new_program")" "$(cat "$STDOUTF")"
UNSHARE=false LD_EXEC=false assertCommandFail unshare_cmd new_program
}
function test_bwrap(){
assertCommandSuccess bwrap_cmd new_program
assertEquals "$(echo -e "ld_exec $BWRAP --dev-bind / / /bin/sh -c :\nld_exec $BWRAP new_program")" "$(cat "$STDOUTF")"
BWRAP=false LD_EXEC=false assertCommandFail bwrap_cmd new_program
}
function test_chroot(){
CLASSIC_CHROOT="echo" assertCommandSuccess chroot_cmd root
assertEquals "root" "$(cat "$STDOUTF")"
CLASSIC_CHROOT=false assertCommandSuccess chroot_cmd root
assertEquals "ld_exec $JUNEST_HOME/usr/bin/false root" "$(cat "$STDOUTF")"
CLASSIC_CHROOT=false LD_EXEC=false assertCommandFail chroot_cmd root
}
function test_proot_cmd_compat(){
PROOT="/bin/true" assertCommandSuccess proot_cmd "" ""
PROOT="/bin/false" assertCommandFail proot_cmd --helps
}
function test_proot_cmd_seccomp(){
envv(){
# shellcheck disable=SC2317
env
}
PROOT=envv
assertCommandSuccess proot_cmd cmd
assertEquals "" "$(grep "^PROOT_NO_SECCOMP" "$STDOUTF")"
envv(){
# shellcheck disable=SC2317
env | grep "^PROOT_NO_SECCOMP"
}
# shellcheck disable=SC2034
PROOT=envv
assertCommandSuccess proot_cmd cmd
# The variable PROOT_NO_SECCOMP will be produced
# twice due to the fallback mechanism
assertEquals "PROOT_NO_SECCOMP=1
PROOT_NO_SECCOMP=1" "$(grep "^PROOT_NO_SECCOMP" "$STDOUTF")"
}
function test_copy_passwd_and_group(){
getent_cmd_mock() {
# shellcheck disable=SC2317
echo "$*"
}
GETENT=getent_cmd_mock assertCommandSuccess copy_passwd_and_group
assertEquals "$(echo -e "passwd\npasswd $USER")" "$(cat "$JUNEST_HOME"/etc/passwd)"
assertEquals "group" "$(cat "$JUNEST_HOME"/etc/group)"
}
function test_copy_passwd_and_group_fallback(){
cp_cmd_mock() {
# shellcheck disable=SC2317
echo "$*"
}
CP=cp_cmd_mock GETENT=false LD_EXEC=false assertCommandSuccess copy_passwd_and_group
assertEquals "$(echo -e "-f /etc/passwd $JUNEST_HOME//etc/passwd\n-f /etc/group $JUNEST_HOME//etc/group")" "$(cat "$STDOUTF")"
}
function test_copy_passwd_and_group_failure(){
CP=false GETENT=false LD_EXEC=false assertCommandFailOnStatus 1 copy_passwd_and_group
}
function test_nested_env(){
JUNEST_ENV=1 assertCommandFailOnStatus 106 check_nested_env
}
function test_nested_env_not_set_variable(){
JUNEST_ENV=aaa assertCommandFailOnStatus 107 check_nested_env
}
function test_check_same_arch_not_same(){
echo "JUNEST_ARCH=XXX" > "${JUNEST_HOME}"/etc/junest/info
assertCommandFailOnStatus 104 check_same_arch
}
function test_check_same_arch(){
echo "JUNEST_ARCH=$ARCH" > "${JUNEST_HOME}"/etc/junest/info
assertCommandSuccess check_same_arch
}
source "$JUNEST_ROOT"/tests/utils/shunit2

383
tests/unit-tests/test-junest.sh Executable file
View file

@ -0,0 +1,383 @@
#!/bin/bash
# shellcheck disable=SC1091
source "$(dirname "$0")/../utils/utils.sh"
JUNEST_BASE="$(dirname "$0")/../.."
source "$JUNEST_BASE"/bin/junest -h &> /dev/null
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
function setUp(){
## Mock functions ##
# shellcheck disable=SC2317
function usage(){
echo "usage"
}
# shellcheck disable=SC2317
function version(){
echo "version"
}
# shellcheck disable=SC2317
function build_image_env(){
local disable_check=$1
echo "build_image_env($disable_check)"
}
# shellcheck disable=SC2317
function delete_env(){
echo "delete_env"
}
# shellcheck disable=SC2317
function setup_env_from_file(){
echo "setup_env_from_file($1)"
}
# shellcheck disable=SC2317
function setup_env(){
echo "setup_env($1)"
}
# shellcheck disable=SC2317
function run_env_as_proot_fakeroot(){
local backend_command="$1"
local backend_args="$2"
local no_copy_files="$3"
shift 3
echo "run_env_as_proot_fakeroot($backend_command,$backend_args,$no_copy_files,$*)"
}
# shellcheck disable=SC2317
function run_env_as_groot(){
local backend_command="$1"
local backend_args="$2"
local no_copy_files="$3"
shift 3
echo "run_env_as_groot($backend_command,$backend_args,$no_copy_files,$*)"
}
# shellcheck disable=SC2317
function run_env_as_chroot(){
local backend_command="$1"
local backend_args="$2"
local no_copy_files="$3"
shift 3
echo "run_env_as_chroot($backend_command,$backend_args,$no_copy_files,$*)"
}
# shellcheck disable=SC2317
function run_env_as_proot_user(){
local backend_command="$1"
local backend_args="$2"
local no_copy_files="$3"
shift 3
echo "run_env_as_proot_user($backend_command,$backend_args,$no_copy_files,$*)"
}
# shellcheck disable=SC2317
function run_env_as_bwrap_fakeroot(){
local backend_command="$1"
local backend_args="$2"
local no_copy_files="$3"
shift 3
echo "run_env_as_bwrap_fakeroot($backend_command,$backend_args,$no_copy_files,$*)"
}
# shellcheck disable=SC2317
function run_env_as_bwrap_user(){
local backend_command="$1"
local backend_args="$2"
local no_copy_files="$3"
shift 3
echo "run_env_as_bwrap_user($backend_command,$backend_args,$no_copy_files,$*)"
}
# shellcheck disable=SC2317
function is_env_installed(){
return 0
}
# shellcheck disable=SC2317
function create_wrappers(){
:
}
}
function test_help(){
assertCommandSuccess main -h
assertEquals "usage" "$(cat "$STDOUTF")"
assertCommandSuccess main --help
assertEquals "usage" "$(cat "$STDOUTF")"
}
function test_version(){
assertCommandSuccess main -V
assertEquals "version" "$(cat "$STDOUTF")"
assertCommandSuccess main --version
assertEquals "version" "$(cat "$STDOUTF")"
}
function test_build_image_env(){
assertCommandSuccess main b
assertEquals "build_image_env(false)" "$(cat "$STDOUTF")"
assertCommandSuccess main build
assertEquals "build_image_env(false)" "$(cat "$STDOUTF")"
assertCommandSuccess main b -n
assertEquals "build_image_env(true)" "$(cat "$STDOUTF")"
assertCommandSuccess main build --disable-check
assertEquals "build_image_env(true)" "$(cat "$STDOUTF")"
}
function test_create_wrappers(){
# shellcheck disable=SC2317
function create_wrappers(){
local force=$1
echo "create_wrappers($force)"
}
assertCommandSuccess main create-bin-wrappers
assertEquals "create_wrappers(false)" "$(cat "$STDOUTF")"
assertCommandSuccess main create-bin-wrappers --force
assertEquals "create_wrappers(true)" "$(cat "$STDOUTF")"
}
function test_delete_env(){
assertCommandSuccess main s -d
assertEquals "delete_env" "$(cat "$STDOUTF")"
assertCommandSuccess main setup --delete
assertEquals "delete_env" "$(cat "$STDOUTF")"
}
function test_setup_env_from_file(){
# shellcheck disable=SC2317
is_env_installed(){
return 1
}
assertCommandSuccess main s -i myimage
assertEquals "setup_env_from_file(myimage)" "$(cat "$STDOUTF")"
assertCommandSuccess main setup --from-file myimage
assertEquals "setup_env_from_file(myimage)" "$(cat "$STDOUTF")"
# shellcheck disable=SC2317
is_env_installed(){
return 0
}
assertCommandFail main setup -i myimage
}
function test_setup_env(){
# shellcheck disable=SC2317
is_env_installed(){
return 1
}
assertCommandSuccess main s
assertEquals "setup_env()" "$(cat "$STDOUTF")"
assertCommandSuccess main setup
assertEquals "setup_env()" "$(cat "$STDOUTF")"
assertCommandSuccess main s -a arm
assertEquals "setup_env(arm)" "$(cat "$STDOUTF")"
assertCommandSuccess main setup --arch arm
assertEquals "setup_env(arm)" "$(cat "$STDOUTF")"
# shellcheck disable=SC2317
is_env_installed(){
return 0
}
assertCommandFail main setup -a arm
}
function test_run_env_as_proot_fakeroot(){
assertCommandSuccess main p -f
assertEquals "run_env_as_proot_fakeroot(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main proot --fakeroot
assertEquals "run_env_as_proot_fakeroot(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main p -f -n
assertEquals "run_env_as_proot_fakeroot(,,true,)" "$(cat "$STDOUTF")"
assertCommandSuccess main p -f --backend-command blah
assertEquals "run_env_as_proot_fakeroot(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main proot -f --backend-command blah
assertEquals "run_env_as_proot_fakeroot(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main proot -f -b "-b arg"
assertEquals "run_env_as_proot_fakeroot(,-b arg,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main proot -f -b "-b arg" -- command -kv
assertEquals "run_env_as_proot_fakeroot(,-b arg,false,command -kv)" "$(cat "$STDOUTF")"
assertCommandSuccess main proot -f command --as
assertEquals "run_env_as_proot_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")"
assertCommandSuccess main proot -f -- command --as
assertEquals "run_env_as_proot_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")"
# shellcheck disable=SC2317
is_env_installed(){
return 1
}
assertCommandFail main proot -f
}
function test_run_env_as_user(){
assertCommandSuccess main proot
assertEquals "run_env_as_proot_user(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main p -n
assertEquals "run_env_as_proot_user(,,true,)" "$(cat "$STDOUTF")"
assertCommandSuccess main p --backend-command blah
assertEquals "run_env_as_proot_user(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main proot --backend-command blah
assertEquals "run_env_as_proot_user(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main proot -b "-b arg"
assertEquals "run_env_as_proot_user(,-b arg,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main proot -b "-b arg" -- command -ll
assertEquals "run_env_as_proot_user(,-b arg,false,command -ll)" "$(cat "$STDOUTF")"
assertCommandSuccess main proot command -ls
assertEquals "run_env_as_proot_user(,,false,command -ls)" "$(cat "$STDOUTF")"
assertCommandSuccess main proot -- command -ls
assertEquals "run_env_as_proot_user(,,false,command -ls)" "$(cat "$STDOUTF")"
# shellcheck disable=SC2317
is_env_installed(){
return 1
}
assertCommandFail main proot
}
function test_run_env_as_groot(){
assertCommandSuccess main g
assertEquals "run_env_as_groot(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main g -n
assertEquals "run_env_as_groot(,,true,)" "$(cat "$STDOUTF")"
assertCommandSuccess main g -b "-b arg"
assertEquals "run_env_as_groot(,-b arg,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main g --backend-command blah
assertEquals "run_env_as_groot(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main groot --backend-command blah
assertEquals "run_env_as_groot(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main groot command
assertEquals "run_env_as_groot(,,false,command)" "$(cat "$STDOUTF")"
assertCommandSuccess main groot -- command
assertEquals "run_env_as_groot(,,false,command)" "$(cat "$STDOUTF")"
# shellcheck disable=SC2317
is_env_installed(){
return 1
}
assertCommandFail main groot
}
function test_run_env_as_chroot(){
assertCommandSuccess main r
assertEquals "run_env_as_chroot(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main r -b "-b arg"
assertEquals "run_env_as_chroot(,-b arg,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main r --backend-command blah
assertEquals "run_env_as_chroot(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main root --backend-command blah
assertEquals "run_env_as_chroot(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main root command
assertEquals "run_env_as_chroot(,,false,command)" "$(cat "$STDOUTF")"
assertCommandSuccess main root -- command
assertEquals "run_env_as_chroot(,,false,command)" "$(cat "$STDOUTF")"
# shellcheck disable=SC2317
is_env_installed(){
return 1
}
assertCommandFail main root -f
}
function test_run_env_as_bwrap_fakeroot(){
assertCommandSuccess main n -f
assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns -f
assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns -n -f
assertEquals "run_env_as_bwrap_fakeroot(,,true,)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns -f -b "-b arg"
assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns -f -b "-b arg" -- command -kv
assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,command -kv)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns -f command --as
assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns -f -- command --as
assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns -f --backend-command blah
assertEquals "run_env_as_bwrap_fakeroot(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main -f --backend-command blah
assertEquals "run_env_as_bwrap_fakeroot(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main -f
assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main -f
assertEquals "run_env_as_bwrap_fakeroot(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main -f -b "-b arg"
assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main -f -b "-b arg" -- command -kv
assertEquals "run_env_as_bwrap_fakeroot(,-b arg,false,command -kv)" "$(cat "$STDOUTF")"
assertCommandSuccess main -f command --as
assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")"
assertCommandSuccess main -f -- command --as
assertEquals "run_env_as_bwrap_fakeroot(,,false,command --as)" "$(cat "$STDOUTF")"
# shellcheck disable=SC2317
is_env_installed(){
return 1
}
assertCommandFail main ns -f
}
function test_run_env_as_bwrap_user(){
assertCommandSuccess main n
assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns
assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns -n
assertEquals "run_env_as_bwrap_user(,,true,)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns -b "-b arg"
assertEquals "run_env_as_bwrap_user(,-b arg,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns -b "-b arg" -- command -kv
assertEquals "run_env_as_bwrap_user(,-b arg,false,command -kv)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns command --as
assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns -- command --as
assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")"
assertCommandSuccess main ns --backend-command blah
assertEquals "run_env_as_bwrap_user(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main --backend-command blah
assertEquals "run_env_as_bwrap_user(blah,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main
assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main
assertEquals "run_env_as_bwrap_user(,,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main -b "-b arg"
assertEquals "run_env_as_bwrap_user(,-b arg,false,)" "$(cat "$STDOUTF")"
assertCommandSuccess main -b "-b arg" -- command -kv
assertEquals "run_env_as_bwrap_user(,-b arg,false,command -kv)" "$(cat "$STDOUTF")"
assertCommandSuccess main command --as
assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")"
assertCommandSuccess main -- command --as
assertEquals "run_env_as_bwrap_user(,,false,command --as)" "$(cat "$STDOUTF")"
# shellcheck disable=SC2317
is_env_installed(){
return 1
}
assertCommandFail main ns
}
function test_invalid_option(){
assertCommandFail main --no-option
assertCommandFail main n --no-option
assertCommandFail main g --no-option
assertCommandFail main r --no-option
assertCommandFail main p --no-option
assertCommandFail main b --no-option
assertCommandFail main s --no-option
}
source "$(dirname "$0")"/../utils/shunit2

View file

@ -0,0 +1,241 @@
#!/bin/bash
# shellcheck disable=SC1091
JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..)
source "$JUNEST_ROOT/tests/utils/utils.sh"
source "$JUNEST_ROOT/lib/utils/utils.sh"
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
## Mock functions ##
function init_mocks() {
# shellcheck disable=SC2317
function bwrap_cmd(){
echo "$BWRAP $*"
}
}
function setUp(){
cwdSetUp
junestSetUp
# Attempt to source the files under test to revert variable
# overrides (i.e. SH variable)
source "$JUNEST_ROOT/lib/core/common.sh"
source "$JUNEST_ROOT/lib/core/namespace.sh"
set +e
init_mocks
}
function tearDown(){
junestTearDown
cwdTearDown
}
function _test_copy_common_files() {
[[ -e /etc/hosts ]] && assertEquals "$(cat /etc/hosts)" "$(cat "${JUNEST_HOME}"/etc/hosts)"
[[ -e /etc/host.conf ]] && assertEquals "$(cat /etc/host.conf)" "$(cat "${JUNEST_HOME}"/etc/host.conf)"
[[ -e /etc/nsswitch.conf ]] && assertEquals "$(cat /etc/nsswitch.conf)" "$(cat "${JUNEST_HOME}"/etc/nsswitch.conf)"
[[ -e /etc/resolv.conf ]] && assertEquals "$(cat /etc/resolv.conf)" "$(cat "${JUNEST_HOME}"/etc/resolv.conf)"
}
function _test_copy_remaining_files() {
[[ -e /etc/hosts.equiv ]] && assertEquals "$(cat /etc/hosts.equiv)" "$(cat "${JUNEST_HOME}"/etc/hosts.equiv)"
[[ -e /etc/netgroup ]] && assertEquals "$(cat /etc/netgroup)" "$(cat "${JUNEST_HOME}"/etc/netgroup)"
[[ -e /etc/networks ]] && assertEquals "$(cat /etc/networks)" "$(cat "${JUNEST_HOME}"/etc/networks)"
[[ -e ${JUNEST_HOME}/etc/passwd ]]
assertEquals 0 $?
[[ -e ${JUNEST_HOME}/etc/group ]]
assertEquals 0 $?
}
function test_is_user_namespace_enabled_no_config_file(){
PROC_USERNS_FILE="blah"
PROC_USERNS_CLONE_FILE="blah"
CONFIG_PROC_FILE="blah"
CONFIG_BOOT_FILE="blah"
assertCommandFailOnStatus "$NOT_EXISTING_FILE" _is_user_namespace_enabled
}
function test_is_user_namespace_enabled_no_config(){
PROC_USERNS_FILE="blah"
PROC_USERNS_CLONE_FILE="blah"
touch config
gzip config
# shellcheck disable=SC2034
CONFIG_PROC_FILE="config.gz"
# shellcheck disable=SC2034
CONFIG_BOOT_FILE="blah"
assertCommandFailOnStatus "$NO_CONFIG_FOUND" _is_user_namespace_enabled
}
function test_is_user_namespace_enabled_with_userns_clone_file_disabled(){
PROC_USERNS_FILE="blah"
PROC_USERNS_CLONE_FILE="unprivileged_userns_clone"
echo "0" > $PROC_USERNS_CLONE_FILE
assertCommandFailOnStatus "$UNPRIVILEGED_USERNS_DISABLED" _is_user_namespace_enabled
}
function test_is_user_namespace_enabled_with_userns_clone_file_enabled(){
PROC_USERNS_CLONE_FILE="unprivileged_userns_clone"
echo "1" > $PROC_USERNS_CLONE_FILE
assertCommandSuccess _is_user_namespace_enabled
}
function test_is_user_namespace_enabled_with_proc_userns_file_existing(){
PROC_USERNS_FILE="user"
ln -s . $PROC_USERNS_FILE
PROC_USERNS_CLONE_FILE="blah"
assertCommandSuccess _is_user_namespace_enabled
}
function test_run_env_as_bwrap_fakeroot() {
assertCommandSuccess run_env_as_bwrap_fakeroot "" "" "false"
assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login" "$(cat "$STDOUTF")"
_test_copy_common_files
}
function test_run_env_as_bwrap_fakeroot_with_backend_command() {
assertCommandSuccess run_env_as_bwrap_fakeroot "mybwrap" "" "false"
assertEquals "mybwrap $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login" "$(cat "$STDOUTF")"
_test_copy_common_files
}
function test_run_env_as_bwrap_user() {
assertCommandSuccess run_env_as_bwrap_user "" "" "false"
assertEquals "$BWRAP $COMMON_BWRAP_OPTION /bin/sh --login" "$(cat "$STDOUTF")"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_bwrap_user_with_backend_command() {
assertCommandSuccess run_env_as_bwrap_user "mybwrap" "" "false"
assertEquals "mybwrap $COMMON_BWRAP_OPTION /bin/sh --login" "$(cat "$STDOUTF")"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_bwrap_fakeroot_no_copy() {
assertCommandSuccess run_env_as_bwrap_fakeroot "" "" "true" ""
assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login" "$(cat "$STDOUTF")"
[[ ! -e ${JUNEST_HOME}/etc/hosts ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/host.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/nsswitch.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/resolv.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/hosts.equiv ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/netgroup ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/networks ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/passwd ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/group ]]
assertEquals 0 $?
}
function test_run_env_as_bwrap_user_no_copy() {
assertCommandSuccess run_env_as_bwrap_user "" "" "true" ""
assertEquals "$BWRAP $COMMON_BWRAP_OPTION /bin/sh --login" "$(cat "$STDOUTF")"
[[ ! -e ${JUNEST_HOME}/etc/hosts ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/host.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/nsswitch.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/resolv.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/hosts.equiv ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/netgroup ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/networks ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/passwd ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/group ]]
assertEquals 0 $?
}
function test_run_env_as_bwrap_fakeroot_with_backend_args() {
assertCommandSuccess run_env_as_bwrap_fakeroot "" "--bind /usr /usr" "false"
assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 --bind /usr /usr sudo /bin/sh --login" "$(cat "$STDOUTF")"
_test_copy_common_files
}
function test_run_env_as_bwrap_user_with_backend_args() {
assertCommandSuccess run_env_as_bwrap_user "" "--bind /usr /usr" "false"
assertEquals "$BWRAP $COMMON_BWRAP_OPTION --bind /usr /usr /bin/sh --login" "$(cat "$STDOUTF")"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_bwrap_fakeroot_with_command() {
assertCommandSuccess run_env_as_bwrap_fakeroot "" "" "false" "ls -la"
assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 sudo /bin/sh --login -c \"ls -la\"" "$(cat "$STDOUTF")"
_test_copy_common_files
}
function test_run_env_as_bwrap_user_with_command() {
assertCommandSuccess run_env_as_bwrap_user "" "" "false" "ls -la"
assertEquals "$BWRAP $COMMON_BWRAP_OPTION /bin/sh --login -c \"ls -la\"" "$(cat "$STDOUTF")"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_bwrap_fakeroot_with_backend_args_and_command() {
assertCommandSuccess run_env_as_bwrap_fakeroot "" "--bind /usr /usr" "false" "ls -la"
assertEquals "$BWRAP $COMMON_BWRAP_OPTION --cap-add ALL --uid 0 --gid 0 --bind /usr /usr sudo /bin/sh --login -c \"ls -la\"" "$(cat "$STDOUTF")"
_test_copy_common_files
}
function test_run_env_as_bwrap_user_with_backend_args_and_command() {
assertCommandSuccess run_env_as_bwrap_user "" "--bind /usr /usr" "false" "ls -la"
assertEquals "$BWRAP $COMMON_BWRAP_OPTION --bind /usr /usr /bin/sh --login -c \"ls -la\"" "$(cat "$STDOUTF")"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_bwrap_fakeroot_nested_env(){
JUNEST_ENV=1
assertCommandFailOnStatus 106 run_env_as_bwrap_fakeroot "" "" "false" ""
unset JUNEST_ENV
}
function test_run_env_as_bwrap_user_nested_env(){
# shellcheck disable=SC2034
JUNEST_ENV=1
assertCommandFailOnStatus 106 run_env_as_bwrap_user "" "" "false" ""
unset JUNEST_ENV
}
source "$JUNEST_ROOT"/tests/utils/shunit2

220
tests/unit-tests/test-proot.sh Executable file
View file

@ -0,0 +1,220 @@
#!/bin/bash
# shellcheck disable=SC1091
JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..)
source "$JUNEST_ROOT/tests/utils/utils.sh"
source "$JUNEST_ROOT/lib/utils/utils.sh"
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
function setUp(){
cwdSetUp
junestSetUp
# Attempt to source the files under test to revert variable
# overrides (i.e. SH variable)
source "$JUNEST_ROOT/lib/core/common.sh"
source "$JUNEST_ROOT/lib/core/proot.sh"
set +e
}
function tearDown(){
junestTearDown
cwdTearDown
}
function _test_copy_common_files() {
[[ -e /etc/hosts ]] && assertEquals "$(cat /etc/hosts)" "$(cat "${JUNEST_HOME}"/etc/hosts)"
[[ -e /etc/host.conf ]] && assertEquals "$(cat /etc/host.conf)" "$(cat "${JUNEST_HOME}"/etc/host.conf)"
[[ -e /etc/nsswitch.conf ]] && assertEquals "$(cat /etc/nsswitch.conf)" "$(cat "${JUNEST_HOME}"/etc/nsswitch.conf)"
[[ -e /etc/resolv.conf ]] && assertEquals "$(cat /etc/resolv.conf)" "$(cat "${JUNEST_HOME}"/etc/resolv.conf)"
}
function _test_copy_remaining_files() {
[[ -e /etc/hosts.equiv ]] && assertEquals "$(cat /etc/hosts.equiv)" "$(cat "${JUNEST_HOME}"/etc/hosts.equiv)"
[[ -e /etc/netgroup ]] && assertEquals "$(cat /etc/netgroup)" "$(cat "${JUNEST_HOME}"/etc/netgroup)"
[[ -e /etc/networks ]] && assertEquals "$(cat /etc/networks)" "$(cat "${JUNEST_HOME}"/etc/networks)"
[[ -e ${JUNEST_HOME}/etc/passwd ]]
assertEquals 0 $?
[[ -e ${JUNEST_HOME}/etc/group ]]
assertEquals 0 $?
}
function test_run_env_as_proot_user(){
# shellcheck disable=SC2317
_run_env_with_qemu() {
# shellcheck disable=SC2086
# shellcheck disable=SC2048
echo $*
}
assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2"
assertEquals "-b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")"
SH=("/usr/bin/echo")
assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "false"
assertEquals "-b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_proot_user_with_backend_command(){
# shellcheck disable=SC2317
_run_env_with_qemu() {
# shellcheck disable=SC2086
# shellcheck disable=SC2048
echo $*
}
assertCommandSuccess run_env_as_proot_user "myproot" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2"
assertEquals "myproot -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")"
SH=("/usr/bin/echo")
assertCommandSuccess run_env_as_proot_user "myproot" "-k 3.10" "false"
assertEquals "myproot -b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")"
_test_copy_common_files
_test_copy_remaining_files
}
function test_run_env_as_proot_user_no_copy(){
# shellcheck disable=SC2317
_run_env_with_qemu() {
# shellcheck disable=SC2086
# shellcheck disable=SC2048
echo $*
}
assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "true" "/usr/bin/mkdir" "-v" "/newdir2"
assertEquals "-b /run/user/$(id -u) -b $HOME -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")"
[[ ! -e ${JUNEST_HOME}/etc/hosts ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/host.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/nsswitch.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/resolv.conf ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/hosts.equiv ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/netgroup ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/networks ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/passwd ]]
assertEquals 0 $?
[[ ! -e ${JUNEST_HOME}/etc/group ]]
assertEquals 0 $?
}
function test_run_env_as_proot_user_nested_env(){
JUNEST_ENV=1
assertCommandFailOnStatus 106 run_env_as_proot_user "" "" "false"
unset JUNEST_ENV
}
function test_run_env_as_proot_fakeroot(){
# shellcheck disable=SC2317
_run_env_with_qemu() {
# shellcheck disable=SC2086
# shellcheck disable=SC2048
echo $*
}
assertCommandSuccess run_env_as_proot_fakeroot "" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2"
assertEquals "-0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")"
SH=("/usr/bin/echo")
assertCommandSuccess run_env_as_proot_fakeroot "" "-k 3.10" "false"
assertEquals "-0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")"
_test_copy_common_files
}
function test_run_env_as_proot_fakeroot_with_backend_command(){
# shellcheck disable=SC2317
_run_env_with_qemu() {
# shellcheck disable=SC2086
# shellcheck disable=SC2048
echo $*
}
assertCommandSuccess run_env_as_proot_fakeroot "myproot" "-k 3.10" "false" "/usr/bin/mkdir" "-v" "/newdir2"
assertEquals "myproot -0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")"
# shellcheck disable=SC2034
SH=("/usr/bin/echo")
assertCommandSuccess run_env_as_proot_fakeroot "myproot" "-k 3.10" "false"
assertEquals "myproot -0 -b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10" "$(cat "$STDOUTF")"
_test_copy_common_files
}
function test_run_env_as_proot_fakeroot_nested_env(){
JUNEST_ENV=1
assertCommandFailOnStatus 106 run_env_as_proot_fakeroot "" "" "false" ""
unset JUNEST_ENV
}
function test_run_env_with_quotes(){
# shellcheck disable=SC2317
_run_env_with_qemu() {
# shellcheck disable=SC2086
# shellcheck disable=SC2048
echo $*
}
assertCommandSuccess run_env_as_proot_user "" "-k 3.10" "false" "bash" "-c" "/usr/bin/mkdir -v /newdir2"
assertEquals "-b /run/user/$(id -u) -b ${HOME} -b /tmp -b /proc -b /sys -b /dev -r ${JUNEST_HOME} -k 3.10 bash -c /usr/bin/mkdir -v /newdir2" "$(cat "$STDOUTF")"
}
function test_run_env_with_proot_args(){
# shellcheck disable=SC2317
proot_cmd() {
[ "$JUNEST_ENV" != "1" ] && return 1
# shellcheck disable=SC2086
# shellcheck disable=SC2048
echo $*
}
assertCommandSuccess _run_env_with_proot "" "--help"
assertEquals "--help /bin/sh --login" "$(cat "$STDOUTF")"
assertCommandSuccess _run_env_with_proot "" "--help" mycommand
assertEquals "--help /bin/sh --login -c mycommand" "$(cat "$STDOUTF")"
assertCommandFail _run_env_with_proot
}
function test_qemu() {
echo "JUNEST_ARCH=arm" > "${JUNEST_HOME}"/etc/junest/info
# shellcheck disable=SC2317
rm_cmd() {
# shellcheck disable=SC2086
# shellcheck disable=SC2048
echo $*
}
# shellcheck disable=SC2317
ln_cmd() {
# shellcheck disable=SC2086
# shellcheck disable=SC2048
echo $*
}
# shellcheck disable=SC2317
_run_env_with_proot() {
# shellcheck disable=SC2086
# shellcheck disable=SC2048
echo $*
}
RANDOM=100 ARCH=x86_64 assertCommandSuccess _run_env_with_qemu "" ""
assertEquals "$(echo -e "-s $JUNEST_HOME/bin/qemu-arm-static-x86_64 /tmp/qemu-arm-static-x86_64-100\n-q /tmp/qemu-arm-static-x86_64-100")" "$(cat "$STDOUTF")"
}
source "$JUNEST_ROOT"/tests/utils/shunit2

85
tests/unit-tests/test-setup.sh Executable file
View file

@ -0,0 +1,85 @@
#!/bin/bash
# shellcheck disable=SC1091
JUNEST_ROOT=$(readlink -f "$(dirname "$0")"/../..)
source "$JUNEST_ROOT/tests/utils/utils.sh"
source "$JUNEST_ROOT/lib/utils/utils.sh"
source "$JUNEST_ROOT/lib/core/common.sh"
source "$JUNEST_ROOT/lib/core/setup.sh"
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
function setUp(){
cwdSetUp
junestSetUp
}
function tearDown(){
junestTearDown
cwdTearDown
}
function test_is_env_installed(){
rm -rf "${JUNEST_HOME:?}"/*
assertCommandFail is_env_installed
touch "$JUNEST_HOME"/just_file
assertCommandSuccess is_env_installed
}
function test_setup_env(){
rm -rf "${JUNEST_HOME:?}"/*
# shellcheck disable=SC2317
wget_mock(){
# Proof that the setup is happening
# inside $JUNEST_TEMPDIR
local cwd=${PWD#"${JUNEST_TEMPDIR}"}
local parent_dir=${PWD%"${cwd}"}
assertEquals "$JUNEST_TEMPDIR" "${parent_dir}"
touch file
tar -czvf "${CMD}-${ARCH}".tar.gz file
}
# shellcheck disable=SC2034
WGET=wget_mock
# shellcheck disable=SC2119
setup_env 1> /dev/null
assertTrue "[ -e $JUNEST_HOME/file ]"
assertCommandFailOnStatus 102 setup_env "noarch"
}
function test_setup_env_from_file(){
rm -rf "${JUNEST_HOME:?}"/*
touch file
tar -czvf "${CMD}-${ARCH}".tar.gz file 1> /dev/null
assertCommandSuccess setup_env_from_file "${CMD}-${ARCH}.tar.gz"
assertTrue "[ -e $JUNEST_HOME/file ]"
}
function test_setup_env_from_file_not_existing_file(){
assertCommandFailOnStatus 103 setup_env_from_file noexist.tar.gz
}
function test_setup_env_from_file_with_absolute_path(){
rm -rf "${JUNEST_HOME:?}"/*
touch file
tar -czf "${CMD}-${ARCH}".tar.gz file
assertCommandSuccess setup_env_from_file "${CMD}-${ARCH}.tar.gz"
assertTrue "[ -e $JUNEST_HOME/file ]"
}
function test_delete_env(){
echo "N" | delete_env 1> /dev/null
assertCommandSuccess is_env_installed
echo "Y" | delete_env 1> /dev/null
assertCommandFail is_env_installed
}
source "$JUNEST_ROOT"/tests/utils/shunit2

105
tests/unit-tests/test-utils.sh Executable file
View file

@ -0,0 +1,105 @@
#!/bin/bash
# shellcheck disable=SC1091
source "$(dirname "$0")/../utils/utils.sh"
unset HOME
export HOME
HOME=$(TMPDIR=/tmp mktemp -d -t pearl-user-home.XXXXXXX)
source "$(dirname "$0")/../../lib/utils/utils.sh"
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
function test_check_not_null(){
assertCommandFailOnStatus 11 check_not_null "" ""
assertCommandSuccess check_not_null "bla" ""
}
function test_echoerr(){
assertCommandSuccess echoerr "Test"
assertEquals "Test" "$(cat "$STDERRF")"
}
function test_error(){
assertCommandSuccess error "Test"
local expected
expected=$(echo -e "\033[1;31mTest\033[0m")
assertEquals "$expected" "$(cat "$STDERRF")"
}
function test_warn(){
assertCommandSuccess warn "Test"
local expected
expected=$(echo -e "\033[1;33mTest\033[0m")
assertEquals "$expected" "$(cat "$STDERRF")"
}
function test_info(){
assertCommandSuccess info "Test"
local expected
expected=$(echo -e "\033[1;36mTest\033[0m")
assertEquals "$expected" "$(cat "$STDOUTF")"
}
function test_die(){
assertCommandFail die "Test"
local expected
expected=$(echo -e "\033[1;31mTest\033[0m")
assertEquals "$expected" "$(cat "$STDERRF")"
}
function test_die_on_status(){
assertCommandFailOnStatus 222 die_on_status 222 "Test"
local expected
expected=$(echo -e "\033[1;31mTest\033[0m")
assertEquals "$expected" "$(cat "$STDERRF")"
}
function test_ask_null_question(){
assertCommandFailOnStatus 11 ask "" "Y"
}
function test_ask(){
echo "Y" | ask "Test" &> /dev/null
assertEquals 0 $?
echo "y" | ask "Test" &> /dev/null
assertEquals 0 $?
echo "N" | ask "Test" &> /dev/null
assertEquals 1 $?
echo "n" | ask "Test" &> /dev/null
assertEquals 1 $?
echo -e "\n" | ask "Test" &> /dev/null
assertEquals 0 $?
echo -e "\n" | ask "Test" "N" &> /dev/null
assertEquals 1 $?
echo -e "asdf\n\n" | ask "Test" "N" &> /dev/null
assertEquals 1 $?
}
function test_ask_wrong_default_answer() {
echo "Y" | ask "Test" G &> /dev/null
assertEquals 33 $?
}
function test_insert_quotes_on_spaces(){
assertCommandSuccess insert_quotes_on_spaces this is "a test"
assertEquals "this is \"a test\"" "$(cat "$STDOUTF")"
assertCommandSuccess insert_quotes_on_spaces this is 'a test'
assertEquals "this is \"a test\"" "$(cat "$STDOUTF")"
}
function test_contains_element(){
array=("something to search for" "a string" "test2000")
assertCommandSuccess contains_element "a string" "${array[@]}"
assertCommandFailOnStatus 1 contains_element "blabla" "${array[@]}"
}
source "$(dirname "$0")"/../utils/shunit2

139
tests/unit-tests/test-wrappers.sh Executable file
View file

@ -0,0 +1,139 @@
#!/bin/bash
# shellcheck disable=SC1091
source "$(dirname "$0")/../utils/utils.sh"
source "$(dirname "$0")/../../lib/core/wrappers.sh"
# Disable the exiterr
set +e
function oneTimeSetUp(){
setUpUnitTests
}
function setUp(){
junestSetUp
}
function tearDown(){
junestTearDown
}
function test_create_wrappers_empty_bin(){
assertCommandSuccess create_wrappers
assertEquals "" "$(cat "$STDOUTF")"
assertTrue "bin_wrappers does not exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]"
}
function test_create_wrappers_not_executable_file(){
touch "$JUNEST_HOME"/usr/bin/myfile
assertCommandSuccess create_wrappers
assertEquals "" "$(cat "$STDOUTF")"
assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]"
assertTrue "myfile wrapper should not exist" "[ ! -x $JUNEST_HOME/usr/bin_wrappers/myfile ]"
}
function test_create_wrappers_directory(){
mkdir -p "$JUNEST_HOME"/usr/bin/mydir
assertCommandSuccess create_wrappers
assertEquals "" "$(cat "$STDOUTF")"
assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]"
assertTrue "mydir wrapper should not exist" "[ ! -e $JUNEST_HOME/usr/bin_wrappers/mydir ]"
}
function test_create_wrappers_broken_link(){
ln -s /opt/myapp/bin/cmd "$JUNEST_HOME"/usr/bin/cmd
assertCommandSuccess create_wrappers
assertEquals "" "$(cat "$STDOUTF")"
assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]"
assertTrue "cmd wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/cmd ]"
}
function test_create_wrappers_executable_file(){
touch "$JUNEST_HOME"/usr/bin/myfile
chmod +x "$JUNEST_HOME"/usr/bin/myfile
assertCommandSuccess create_wrappers
assertEquals "" "$(cat "$STDOUTF")"
assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]"
assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/myfile ]"
}
function test_create_wrappers_verify_content(){
# Test for:
# https://github.com/fsquillace/junest/issues/262
# https://github.com/fsquillace/junest/issues/292
touch "$JUNEST_HOME"/usr/bin/myfile
chmod +x "$JUNEST_HOME"/usr/bin/myfile
export JUNEST_ARGS="ns --fakeroot -b '--bind /run /run2'"
assertCommandSuccess create_wrappers
assertEquals "" "$(cat "$STDOUTF")"
# Mock junest command to capture the actual output generated from myfile script
# shellcheck disable=SC2317
junest(){
for arg in "$@"
do
echo "$arg"
done
}
assertEquals "ns
--fakeroot
-b
--bind /run /run2
--
test-wrappers.sh
pacman
-Rsn
neovim
new package" "$(source "$JUNEST_HOME"/usr/bin_wrappers/myfile pacman -Rsn neovim 'new package')"
}
function test_create_wrappers_already_exist(){
touch "$JUNEST_HOME"/usr/bin/myfile
chmod +x "$JUNEST_HOME"/usr/bin/myfile
mkdir -p "$JUNEST_HOME"/usr/bin_wrappers
echo "original" > "$JUNEST_HOME"/usr/bin_wrappers/myfile
chmod +x "$JUNEST_HOME"/usr/bin_wrappers/myfile
assertCommandSuccess create_wrappers false
assertEquals "" "$(cat "$STDOUTF")"
assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]"
assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/myfile ]"
assertEquals "original" "$(cat "$JUNEST_HOME"/usr/bin_wrappers/myfile)"
}
function test_create_wrappers_forced_already_exist(){
echo "new" > "$JUNEST_HOME"/usr/bin/myfile
chmod +x "$JUNEST_HOME"/usr/bin/myfile
mkdir -p "$JUNEST_HOME"/usr/bin_wrappers
echo "original" > "$JUNEST_HOME"/usr/bin_wrappers/myfile
chmod +x "$JUNEST_HOME"/usr/bin_wrappers/myfile
assertCommandSuccess create_wrappers true
assertEquals "" "$(cat "$STDOUTF")"
assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]"
assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/bin_wrappers/myfile ]"
assertNotEquals "original" "$(cat "$JUNEST_HOME"/usr/bin_wrappers/myfile)"
}
function test_create_wrappers_executable_no_longer_exist(){
mkdir -p "$JUNEST_HOME"/usr/bin_wrappers
touch "$JUNEST_HOME"/usr/bin_wrappers/myfile
chmod +x "$JUNEST_HOME"/usr/bin_wrappers/myfile
assertCommandSuccess create_wrappers
assertEquals "" "$(cat "$STDOUTF")"
assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/bin_wrappers ]"
assertTrue "myfile wrapper should not exist" "[ ! -x $JUNEST_HOME/usr/bin_wrappers/myfile ]"
}
function test_create_wrappers_custom_bin_path(){
mkdir -p "$JUNEST_HOME"/usr/mybindir
touch "$JUNEST_HOME"/usr/mybindir/myfile
chmod +x "$JUNEST_HOME"/usr/mybindir/myfile
assertCommandSuccess create_wrappers false /usr/mybindir/
assertEquals "" "$(cat "$STDOUTF")"
assertTrue "bin_wrappers should exist" "[ -e $JUNEST_HOME/usr/mybindir_wrappers ]"
assertTrue "myfile wrapper should exist" "[ -x $JUNEST_HOME/usr/mybindir_wrappers/myfile ]"
}
source "$(dirname "$0")"/../utils/shunit2

9
tests/unit-tests/unit-tests.sh Executable file
View file

@ -0,0 +1,9 @@
#!/bin/bash
tests_succeded=true
# shellcheck disable=SC2010
for tst in $(ls "$(dirname "$0")"/test* | grep -v "$(basename "$0")")
do
$tst || tests_succeded=false
done
$tests_succeded

61
tests/shunit2 → tests/utils/shunit2 Executable file → Normal file
View file

@ -1,5 +1,5 @@
#! /bin/sh
# $Id: shunit2 335 2011-05-01 20:10:33Z kate.ward@forestent.com $
# $Id$
# vim:et:ft=sh:sts=2:sw=2
#
# Copyright 2008 Kate Ward. All Rights Reserved.
@ -15,20 +15,31 @@
# return if shunit already loaded
[ -n "${SHUNIT_VERSION:-}" ] && exit 0
SHUNIT_VERSION='2.1.7pre'
SHUNIT_VERSION='2.1.6'
# return values that scripts can use
SHUNIT_TRUE=0
SHUNIT_FALSE=1
SHUNIT_ERROR=2
# enable strict mode by default
SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}}
# logging functions
_shunit_warn() { echo "shunit2:WARN $@" >&2; }
_shunit_error() { echo "shunit2:ERROR $@" >&2; }
_shunit_fatal() { echo "shunit2:FATAL $@" >&2; exit ${SHUNIT_ERROR}; }
# determine some reasonable command defaults
__SHUNIT_UNAME_S=`uname -s`
case "${__SHUNIT_UNAME_S}" in
BSD) __SHUNIT_EXPR_CMD='gexpr' ;;
*) __SHUNIT_EXPR_CMD='expr' ;;
esac
# commands a user can override if needed
SHUNIT_EXPR_CMD=${SHUNIT_EXPR_CMD:-${__SHUNIT_EXPR_CMD}}
# enable strict mode by default
SHUNIT_STRICT=${SHUNIT_STRICT:-${SHUNIT_TRUE}}
# specific shell checks
if [ -n "${ZSH_VERSION:-}" ]; then
setopt |grep "^shwordsplit$" >/dev/null
@ -51,19 +62,24 @@ __SHUNIT_MODE_STANDALONE='standalone'
__SHUNIT_PARENT=${SHUNIT_PARENT:-$0}
# set the constants readonly
shunit_constants_=`set |grep '^__SHUNIT_' |cut -d= -f1`
echo "${shunit_constants_}" |grep '^Binary file' >/dev/null && \
shunit_constants_=`set |grep -a '^__SHUNIT_' |cut -d= -f1`
for shunit_constant_ in ${shunit_constants_}; do
shunit_ro_opts_=''
case ${ZSH_VERSION:-} in
'') ;; # this isn't zsh
[123].*) ;; # early versions (1.x, 2.x, 3.x)
*) shunit_ro_opts_='-g' ;; # all later versions. declare readonly globally
esac
readonly ${shunit_ro_opts_} ${shunit_constant_}
__shunit_constants=`set |grep '^__SHUNIT_' |cut -d= -f1`
echo "${__shunit_constants}" |grep '^Binary file' >/dev/null && \
__shunit_constants=`set |grep -a '^__SHUNIT_' |cut -d= -f1`
for __shunit_const in ${__shunit_constants}; do
if [ -z "${ZSH_VERSION:-}" ]; then
readonly ${__shunit_const}
else
case ${ZSH_VERSION} in
[123].*) readonly ${__shunit_const} ;;
*) readonly -g ${__shunit_const} # declare readonly constants globally
esac
fi
done
unset shunit_constant_ shunit_constants_ shunit_ro_opts_
unset __shunit_const __shunit_constants
#
# internal variables
#
# variables
__shunit_lineno='' # line number of executed test
@ -88,6 +104,9 @@ __shunit_assertsSkipped=0
# macros
_SHUNIT_LINENO_='eval __shunit_lineno=""; if [ "${1:-}" = "--lineno" ]; then [ -n "$2" ] && __shunit_lineno="[$2] "; shift 2; fi'
#-----------------------------------------------------------------------------
# private functions
#-----------------------------------------------------------------------------
# assert functions
#
@ -313,8 +332,8 @@ _ASSERT_NOT_SAME_='eval assertNotSame --lineno "${LINENO:-}"'
assertTrue()
{
${_SHUNIT_LINENO_}
if [ $# -gt 2 ]; then
_shunit_error "assertTrue() takes one two arguments; $# given"
if [ $# -lt 1 -o $# -gt 2 ]; then
_shunit_error "assertTrue() takes one or two arguments; $# given"
return ${SHUNIT_ERROR}
fi
_shunit_shouldSkip && return ${SHUNIT_TRUE}
@ -968,7 +987,7 @@ _shunit_extractTestFunctions()
# extract the lines with test function names, strip of anything besides the
# function name, and output everything on a single line.
_shunit_regex_='^[ ]*(function )*test[A-Za-z0-9_]* *\(\)'
egrep "${_shunit_regex_}" "${_shunit_script_}" \
grep -E "${_shunit_regex_}" "${_shunit_script_}" \
|sed 's/^[^A-Za-z0-9_]*//;s/^function //;s/\([A-Za-z0-9_]*\).*/\1/g' \
|xargs

62
tests/utils/utils.sh Normal file
View file

@ -0,0 +1,62 @@
#!/usr/bin/env bash
OLD_CWD=${PWD}
function cwdSetUp(){
ORIGIN_CWD=$(TMPDIR=/tmp mktemp -d -t junest-cwd.XXXXXXXXXX)
cd "$ORIGIN_CWD" || return 1
}
function cwdTearDown(){
rm -rf "$ORIGIN_CWD"
cd "$OLD_CWD" || return 1
}
function junestSetUp(){
JUNEST_HOME=$(TMPDIR=/tmp mktemp -d -t junest-home.XXXXXXXXXX)
mkdir -p "${JUNEST_HOME}/usr/bin"
mkdir -p "${JUNEST_HOME}/etc/junest"
echo "JUNEST_ARCH=x86_64" > "${JUNEST_HOME}/etc/junest/info"
mkdir -p "${JUNEST_HOME}/etc/ca-certificates"
}
function junestTearDown(){
# the CA directories are read only and can be deleted only by changing the mod
[ -d "${JUNEST_HOME}/etc/ca-certificates" ] && chmod -R +w "${JUNEST_HOME}/etc/ca-certificates"
rm -rf "$JUNEST_HOME"
unset JUNEST_HOME
}
function setUpUnitTests(){
OUTPUT_DIR="${SHUNIT_TMPDIR}/output"
mkdir "${OUTPUT_DIR}"
STDOUTF="${OUTPUT_DIR}/stdout"
STDERRF="${OUTPUT_DIR}/stderr"
}
function assertCommandSuccess(){
# shellcheck disable=SC2091
$(set -e
"$@" > "$STDOUTF" 2> "$STDERRF"
)
assertTrue "The command $1 did not return 0 exit status" $?
}
function assertCommandFail(){
# shellcheck disable=SC2091
$(set -e
"$@" > "$STDOUTF" 2> "$STDERRF"
)
assertFalse "The command $1 returned 0 exit status" $?
}
# $1: expected exit status
# $2-: The command under test
function assertCommandFailOnStatus(){
local status=$1
shift
# shellcheck disable=SC2091
$(set -e
"$@" > "$STDOUTF" 2> "$STDERRF"
)
assertEquals "$status" $?
}