Compare commits

...

254 Commits

Author SHA1 Message Date
Kroese
1b8054e847
fix: Podman detection (#990)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-05-27 00:50:40 +02:00
Kroese
631a558a9f
feat: Enable IPv6 support for user-mode container networking (#983)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-27 11:28:54 +02:00
Kroese
d5805b4e08
feat: Check CPU vendor (#982) 2025-04-27 11:21:37 +02:00
Kroese
1dc15ce2e0
docs: Add input types (#981) 2025-04-23 12:54:22 +02:00
Kroese
4145e811df
feat: Improve CPU detection (#980)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-20 21:43:02 +02:00
Kroese
fe8615d7d3
docs: Add quotes around $PWD (#977)
Some checks failed
Build / Check (push) Has been cancelled
Update / dockerHubDescription (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-16 12:10:59 +02:00
Kroese
27ea5506bc
feat: Default to 2 CPU cores and 2GB of RAM (#976) 2025-04-16 11:56:02 +02:00
Kroese
394131a0d7
feat: Podman detection (#972)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-09 09:01:01 +02:00
Kroese
4d0d16023f
feat: Podman detection (#971) 2025-04-09 08:58:02 +02:00
Kroese
60dd794b29
fix: Avoid pipe to head on find (#970) 2025-04-09 08:03:41 +02:00
Kroese
f13d104968
build: Add root-user-action flag (#968)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-04-08 07:53:17 +02:00
Kroese
914099fd12
build: Add --root-user-action flag (#967)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-04-07 18:47:36 +02:00
Kroese
639725957d
fix: Permission errors on QNAP (#966) 2025-04-07 18:36:47 +02:00
Kroese
64bbe82c4a
fix: KVM warning (#965) 2025-04-07 17:44:26 +02:00
Kroese
73a13e5697
fix: QNAP permission errors (#964) 2025-04-07 17:20:14 +02:00
Kroese
e8ec5fb8ed
feat: Support 4k sector sizes (#963)
Some checks failed
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
Update / dockerHubDescription (push) Has been cancelled
2025-04-07 13:25:20 +02:00
Kroese
0b500ca377
docs: Disk pass-through (#960)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-04-03 10:22:26 +02:00
Kroese
df58745ed8
fix: Ignore missing custom .pat after install (#959)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-04-03 01:13:14 +02:00
Kroese
55e8ab4930
docs: Github Codespaces (#956)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-27 01:48:40 +01:00
Kroese
4882fd3bf6
fix: Local file support (#955)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-03-26 19:51:58 +01:00
Kroese
3e69f4bcc9
feat: New extraction method (#954)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-26 09:24:13 +01:00
Kroese
39428909d6
docs: Installation (#953)
Some checks are pending
Update / dockerHubDescription (push) Waiting to run
2025-03-26 02:47:28 +01:00
Kroese
71d2ed40db
docs: Github Codespaces (#952)
Some checks are pending
Update / dockerHubDescription (push) Waiting to run
2025-03-25 14:49:46 +01:00
Kroese
9ad21a28a6
docs: Disk pass-through (#951)
Some checks are pending
Update / dockerHubDescription (push) Waiting to run
2025-03-25 12:18:38 +01:00
Kroese
0c7acd6e71
fix: Remove non-printable characters (#949)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-03-24 13:52:38 +01:00
Kroese
ad26129375
docs: KVM information (#947)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-20 23:21:41 +01:00
Kroese
4e7822fedd
docs: Compatibility chart (#946) 2025-03-20 23:07:14 +01:00
Kroese
4dfcbe4ab1
docs: Add Podman (#945) 2025-03-20 20:09:53 +01:00
Kroese
8fb6f1f9ad
feat: Podman detection (#944)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-03-20 11:54:26 +01:00
Kroese
11d4fafa6d
fix: User-mode networking (#943)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-20 03:07:01 +01:00
Kroese
be15557798
fix: User-mode networking (#942) 2025-03-20 03:05:50 +01:00
Kroese
1e83757494
fix: Set user-mode IP address (#941)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-19 12:55:53 +01:00
Kroese
fad463a439
feat: Add devcontainer (#940)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-19 09:17:48 +01:00
Kroese
608563d029
feat: Add devcontainer (#939)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-18 19:25:27 +01:00
Kroese
5fcd22b09f
docs: Formatting (#938)
Some checks failed
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
Update / dockerHubDescription (push) Has been cancelled
2025-03-18 14:24:05 +01:00
Kroese
65548da2ef
docs: Github Codespaces (#937) 2025-03-18 14:10:08 +01:00
Kroese
111e513dac
docs: Github Codespaces (#936) 2025-03-18 14:09:10 +01:00
Kroese
7fce3a98fe
feat: Refactor helper functions (#935) 2025-03-18 12:57:52 +01:00
Kroese
4018eeadb4
fix: Remove port 80 (#934)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
Update / dockerHubDescription (push) Waiting to run
2025-03-18 05:03:54 +01:00
Kroese
a8e4647fa6
feat: IPv6 support (#933) 2025-03-18 03:59:58 +01:00
Kroese
36d3fca4fc
fix: Fallback to POSIX fallocate (#932)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-17 11:19:04 +01:00
Kroese
89b28f36d3
docs: Disk pass-through (#930)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-16 12:30:06 +01:00
Kroese
b4f7d70a7a
docs: Readme (#929)
Some checks are pending
Update / dockerHubDescription (push) Waiting to run
2025-03-16 06:52:14 +01:00
Kroese
902bafbd0c
docs: Readme (#928) 2025-03-16 06:40:38 +01:00
Kroese
7ad5c7fa70
fix: Display available memory (#926)
Some checks failed
Update / dockerHubDescription (push) Waiting to run
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-03-15 14:09:17 +01:00
Kroese
4d29f056b7
fix: Format filesizes (#925)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-14 18:53:53 +01:00
Kroese
36af9a3483
feat: Validate configured RAM (#924)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-14 14:42:08 +01:00
Kroese
5e6f931433
feat: Detect clock source (#923) 2025-03-14 14:16:10 +01:00
Kroese
beadfe720c
feat: Improve CPU detection (#921)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-14 05:23:49 +01:00
Kroese
92b4bfc383
feat: Validate configured RAM (#920)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-13 11:35:08 +01:00
Kroese
092ed23085
feat: Check CPU core configuration (#919) 2025-03-13 10:47:25 +01:00
Kroese
c70fa3d00a
feat: Validate specified size (#918)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-12 21:17:47 +01:00
Kroese
fea0ba09f6
docs: Use relative paths (#917)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-12 12:31:50 +01:00
Kroese
7d2af63eac
docs: Kubernetes deployment (#916)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-07 00:42:29 +01:00
Kroese
2e73bf560e
docs: Add link to download (#915) 2025-03-07 00:22:29 +01:00
Kroese
bfc8d7a9c6
fix: Network shutdown (#912)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-03-05 04:59:57 +01:00
Kroese
a1e9936572
fix: Set MTU value (#911)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-04 15:37:37 +01:00
Kroese
3675a50129
fix: Gateway MAC generation (#910)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-03-03 13:57:53 +01:00
Kroese
91229152bd
feat: Set MTU size for TAP interface (#909) 2025-03-03 13:40:15 +01:00
Kroese
a1993e590a
feat: Automaticly match MTU size (#908) 2025-03-03 12:20:16 +01:00
Kroese
52fe712b6f
docs: Readme (#907)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-03-01 14:23:51 +01:00
Kroese
7f400b6b59
feat: Improve CPU detection (#905)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-02-27 11:52:48 +01:00
Kroese
9cb88a99e6
feat: Allow bridge networks (#904) 2025-02-27 11:46:38 +01:00
Kroese
b967a471b5
fix: Add e2fsprogs package (#902)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-02-26 10:03:46 +01:00
Kroese
f40127df01
feat: Make app name configurable (#900)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-02-25 15:13:57 +01:00
Kroese
8c5e0ee274
fix: Generate local MAC address (#899)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-02-24 21:01:55 +01:00
Kroese
2adf0b292b
fix: Preserve gateway MAC address (#898)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-02-24 03:48:42 +01:00
Kroese
1c9b793c75
fix: Move nginx config (#893)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-02-15 02:36:07 +01:00
Kroese
00c4ef7795
feat: Remove existing TAP interface (#892)
Some checks are pending
Build / Check (push) Waiting to run
Build / Build (push) Blocked by required conditions
2025-02-14 20:19:12 +01:00
Kroese
619657adf2
docs: Add restart policy (#888)
Some checks failed
Update / dockerHubDescription (push) Has been cancelled
2025-02-10 00:31:32 +01:00
Kroese
41db7c1035
feat: Improve CPU detection (#884)
Some checks failed
Build / Check (push) Has been cancelled
Build / Build (push) Has been cancelled
2025-02-06 02:19:39 +01:00
Kroese
d71834a777
build: Enable ARM64 platform (#873) 2025-01-10 18:44:37 +01:00
Kroese
d078af0397
fix: TUN device error (#863) 2024-12-03 11:50:56 +01:00
Kroese
2827d1d375
docs: Add TUN device (#861) 2024-12-01 17:57:00 +01:00
Kroese
898499a4e3
feat: Make network adapter configurable (#859) 2024-11-26 20:01:37 +01:00
Kroese
eb010cc215
feat: Improve network error handling (#856) 2024-11-20 16:24:07 +01:00
Kroese
84440d5159
fix: Use relative URL's (#855) 2024-11-20 13:21:20 +01:00
Kroese
ff3744ead9
feat: Improve CPU detection (#854) 2024-11-20 13:13:43 +01:00
Kroese
d00fe4b3eb
docs: Readme (#848) 2024-11-13 03:45:21 +01:00
Kroese
72a86a5d7f
docs: Add Kubernetes URL (#847) 2024-11-13 03:35:50 +01:00
Kroese
811ab622df
docs: Add compatibility chart (#846) 2024-11-13 03:26:15 +01:00
Kroese
e76b72cddf
feat: Disable HTTP keepalives (#845) 2024-11-13 02:48:27 +01:00
Kroese
e9edacc9c3
feat: Support image commit (#844) 2024-11-13 00:39:48 +01:00
Kroese
a6694a6b29
feat: Rename host to kernel (#843) 2024-11-11 16:24:47 +01:00
Kroese
59323cd375
docs: KVM troubleshooting (#842) 2024-11-11 14:16:21 +01:00
Kroese
6dc714e449
feat: Display unknown filesystem (#841) 2024-11-10 15:00:38 +01:00
Kroese
5d75a9b039
feat: Improve CPU detection (#840) 2024-11-10 11:21:51 +01:00
Kroese
92b4cf5997
build: Use latest Debian image (#832) 2024-10-19 22:09:52 +02:00
Kroese
906e61b1b2
feat: Improve CPU detection (#831) 2024-10-15 10:27:51 +02:00
Kroese
dab230f9d5
build: Remove ARM64 platform (#830) 2024-10-15 00:37:28 +02:00
Kroese
5e8bcda9fc
feat: Improve CPU detection (#829) 2024-10-14 18:06:02 +02:00
Kroese
d4bf83ae86
feat: Add NVME disk type (#828) 2024-10-13 03:03:32 +02:00
xrh0905
8244a48511
Correct the possible DISK_IO (io_uring) (#827) 2024-10-06 16:26:35 +02:00
Kroese
43ffa18a5f
fix: Remove scsi parameter from virtio-blk-pci (#824) 2024-10-01 21:49:31 +02:00
Kroese
b131a32d0c
buid: Pin Debian version (#818) 2024-09-27 14:40:56 +02:00
D-Jy
c81787b837
feat: Allow custom rendernode (#817) 2024-09-17 23:30:58 +02:00
Kroese
f9df3c6db6
feat: Update to VirtualDSM 7.2.2 (#815) 2024-09-11 19:20:43 +02:00
Liang Ying-Ruei
e383ec30e3
fix: Splits $USER_PORTS correctly by commas (#813) 2024-09-09 15:43:57 +02:00
Kroese
1197c4791e
fix: Port forwarding warning (#809) 2024-09-05 18:58:23 +02:00
Kroese
106c684389
docs: Update package URL (#808) 2024-09-03 15:09:33 +02:00
Kroese
a199ced77b
docs: Update package URL (#803) 2024-08-27 16:07:09 +02:00
Kroese
77ac73666e
fix: Progress calculation (#799) 2024-08-18 17:55:07 +02:00
Kroese
64975557c2
fix: Validate lscpu output (#798) 2024-08-18 04:40:20 +02:00
Kroese
b62321806a
docs: Fix badge (#796) 2024-08-13 20:51:55 +02:00
renovate[bot]
7c392082b1
chore(deps): update docker/build-push-action action to v6 (#775)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-06-17 12:45:23 +02:00
Kroese
392e9a6417
docs: Networking (#774) 2024-06-16 06:09:26 +02:00
Kroese
fb5f684e7e
fix: Don't set script with file descriptor (#772) 2024-06-14 04:35:40 +02:00
Kroese
0dbc794223
feat: Customize image filenames (#771) 2024-06-14 03:01:27 +02:00
Kroese
6295ed3b7b
docs: Removed Kubevirt dependancy (#770) 2024-06-13 19:31:00 +02:00
Kroese
0f2d889858
fix: Assume GB when no unit is present (#769) 2024-06-13 18:59:13 +02:00
Kroese
60e6b01982
docs: Add markdown alerts (#768) 2024-06-13 18:31:25 +02:00
Kroese
1dc69f9e22
docs: Add GHCR badge (#767) 2024-06-11 23:26:38 +02:00
Kroese
fcca41ad93
docs: Readme (#766) 2024-06-11 21:48:18 +02:00
Kroese
9897747425
docs: Add icons (#765) 2024-06-11 21:06:38 +02:00
Kroese
4dbe9dcefd
docs: KVM information (#764) 2024-06-11 19:56:14 +02:00
Kroese
be8bee90f2
feat: Implement usermode networking (#762) 2024-06-11 02:13:53 +02:00
Kroese
f8879029ec
fix: Do not use IO threading for SATA disks (#760) 2024-06-09 18:34:55 +02:00
Kroese
7b491b3cee
feat: Verify clocksource is set to TSC (#759) 2024-06-09 17:44:15 +02:00
Kroese
2ece865417
fix: Download error messages (#756) 2024-06-05 18:18:29 +02:00
Kroese
0d8c693c65
build: Update qemu-host to v2.05 (#755) 2024-06-05 14:32:21 +02:00
Kroese
9f7d1396b6
docs: Readme (#753) 2024-06-03 02:00:04 +02:00
Kroese
5ad323486e
feat: Print QEMU version on boot (#752) 2024-06-02 23:00:40 +02:00
Kroese
19b4248929
feat: Print QEMU version on boot (#751) 2024-06-02 21:04:09 +02:00
Kroese
c135c4cac3
feat: Add automatic device type (#749) 2024-05-27 17:25:52 +02:00
Kroese
fb1751ff26
docs: Updated issue templates (#747) 2024-05-25 14:07:30 +02:00
Kroese
34f32d4ac6
docs: Docker CLI example (#746) 2024-05-22 07:55:38 +02:00
Kroese
36f1e47c0a
fix: Check for Podman IP ranges (#745) 2024-05-21 13:48:05 +02:00
Kroese
c5dd5c2e46
feat: Print arguments in debug mode (#743) 2024-05-21 09:17:51 +02:00
Kroese
d1d920372a
feat: Support more device types (#742) 2024-05-21 09:10:30 +02:00
Kroese
12d8fd3ed0
feat: Support Kubernetes interfaces (#741) 2024-05-21 07:44:44 +02:00
Kroese
729aed536e
feat: Add flag to disable RAM check (#740) 2024-05-19 23:59:14 +02:00
Kroese
b588f8c90d
feat: Display KVM warning on ARM64 (#739) 2024-05-19 21:19:58 +02:00
Kroese
1f1007a0f1
fix: Display reason for network failure (#738) 2024-05-17 16:09:47 +02:00
Kroese
2ec37e2802
docs: Docker CLI (#737) 2024-05-16 03:22:58 +02:00
Slavik
3126b3847b
feat: Add Kubernetes manifest (#735) 2024-05-16 01:42:58 +02:00
Kroese
41e0157e9d
docs: Update issue templates (#736) 2024-05-16 01:12:16 +02:00
Kroese
1d64410849
docs: Update issue template (#734) 2024-05-15 22:04:25 +02:00
Kroese
55034b0f40
feat: Additional disk device (#733) 2024-05-15 17:56:12 +02:00
Kroese
acffcf3774
docs: Remove restart policy (#732) 2024-05-15 17:23:09 +02:00
Kroese
fba0eb527b
docs: Remove version from compose (#731) 2024-05-15 16:54:17 +02:00
Kroese
d9fc2714a6
build: Declare extra variable (#730) 2024-05-13 04:02:25 +02:00
Kroese
d1f1772d74
build: Optimize Dockerfile (#729) 2024-05-13 03:48:29 +02:00
Kroese
bf1d47e4f3
docs: Readme (#728) 2024-05-12 20:55:50 +02:00
Kroese
3da564dfd1
feat: Support install from local PAT file (#727) 2024-05-12 20:19:30 +02:00
Kroese
abd30b9d91
feat: Detect Windows and MacOS (#726) 2024-05-11 22:11:11 +02:00
Kroese
c86408cbd6
fix: Additional pass-through mounts (#722) 2024-05-08 23:23:17 +02:00
Kroese
1f51974c48
fix: Set download timeout (#719) 2024-05-04 17:24:21 +02:00
Kroese
29f4cde296
feat: Show download percentage (#718) 2024-05-04 16:34:30 +02:00
Kroese
c4a0035062
feat: Display RAM amount warning (#717) 2024-05-03 11:18:20 +02:00
Kroese
6724ddbd7d
feat: Check the amount of RAM available (#716) 2024-05-02 20:21:21 +02:00
Kroese
8d8ed63122
docs: Disk pass-through (#715) 2024-05-01 11:57:56 +02:00
Kroese
6f4ea81907
feat: New disk pass-through method (#714) 2024-05-01 00:13:26 +02:00
Kroese
6d162744ec
feat: Improved networking (#712) 2024-04-30 00:30:44 +02:00
Kroese
145b4aab5b
docs: Update issue templates (#708) 2024-04-26 16:53:15 +02:00
Kroese
2e4eb56d0b
fix: Curl error on Chinese mirror (#707) 2024-04-26 16:32:08 +02:00
Kroese
39c019193e
fix: Curl error on Chinese mirror (#706) 2024-04-26 16:30:30 +02:00
Kroese
faec563b4a
feat: Display kernel version (#704) 2024-04-26 13:59:51 +02:00
Kroese
9840f8e07a
feat: Support ecryptfs filesystem (#703) 2024-04-26 06:04:48 +02:00
Kroese
3a5895fa0f
build: Remove armv7 support (#702) 2024-04-26 04:16:46 +02:00
Kroese
1bac5c8a7f
feat: Print system info (#701) 2024-04-25 14:31:49 +02:00
Kroese
dd76c60e2a
fix: CPU detection (#697) 2024-04-22 16:48:41 +02:00
Kroese
ac78cc7dc5
feat: Display system info on boot (#696) 2024-04-22 16:29:23 +02:00
Kroese
66ac2d2002
docs: Update issue template (#692) 2024-04-17 19:03:17 +02:00
Kroese
0bd099d704
docs: Update issue templates (#691) 2024-04-17 18:32:43 +02:00
Kroese
d2dac3cfb4
docs: Add issue templates (#690) 2024-04-17 18:14:51 +02:00
Kroese
e81dc0f31d
fix: Verify files are not empty (#689) 2024-04-16 15:35:35 +02:00
Kroese
5015597183
fix: Ignore empty MAC address (#688) 2024-04-13 23:33:17 +02:00
Kroese
64e2af9fa2
fix: Continue if file is missing (#682) 2024-04-08 09:54:42 +02:00
Kroese
debb4b69fc
fix: Continue when range request fails (#680) 2024-04-08 04:53:30 +02:00
Kroese
c6d3dda171
docs: Readme (#677) 2024-04-07 01:59:50 +02:00
Kroese
7c0693c2ff
build: Update qemu-host to v2.04 (#670) 2024-03-31 05:24:54 +02:00
Kroese
76355d4857
fix: TUN error message (#669) 2024-03-31 04:57:56 +02:00
Kroese
404aaadefc
build: Dockerfile (#663) 2024-03-25 16:49:25 +01:00
Kroese
be027e10be
fix: Disable IPv6 in Nginx (#662) 2024-03-25 13:53:30 +01:00
Kroese
1c8cad92f8
fix: Disable kernel networking in bridge mode (#656) 2024-03-12 01:50:43 +01:00
Kroese
fabb8ea3b7
docs: Readme (#650) 2024-02-28 20:43:27 +01:00
Kroese
2ee4abca54
docs: Docker run command (#647) 2024-02-28 08:51:55 +01:00
Kroese
5896928030
feat: Persistant MAC address (#646) 2024-02-28 08:40:00 +01:00
Kroese
8652544982
docs: Readme (#638) 2024-02-19 21:32:22 +01:00
Kroese
a70338ec3c
feat: Print filesystem on error (#635) 2024-02-17 05:56:40 +01:00
Kroese
a84878abfc
fix: Detect Mac Journal filesystem (#634) 2024-02-17 03:55:30 +01:00
Kroese
8421a391b7
fix: Prevent re-entry during shutdown (#633) 2024-02-16 16:03:37 +01:00
Kroese
f9340ec3d6
docs: Readme (#630) 2024-02-10 00:33:21 +01:00
Kroese
0cca9c5f83
feat: Disable CoW check on XFS (#629) 2024-02-09 23:53:18 +01:00
Kroese
13d60b7f47
build: Notify after build (#628) 2024-02-09 14:45:38 +01:00
Kroese
f74771a9cc
fix: Convert MAC address to uppercase (#627) 2024-02-08 17:33:11 +01:00
Kroese
f24ba41930
fix: Convert dashes in custom MAC addresses (#626) 2024-02-08 17:03:30 +01:00
Kroese
f412580a4a
feat: Add DNS entry for container (#624) 2024-02-07 22:26:29 +01:00
Kroese
5cde1b4438
feat: Set process name (#623) 2024-02-07 13:04:29 +01:00
Kroese
7cfb57b1bc
docs: Readme (#621) 2024-02-05 16:18:13 +01:00
Kroese
a478b58f97
feat: Set KVM tick policy (#620) 2024-02-05 12:09:12 +01:00
Kroese
8297f4f880
fix: Support CPU's without SSE4 (#619) 2024-02-05 07:41:47 +01:00
Kroese
4c67343d33
feat: Enable L3 cache and multi-threaded TCG (#618) 2024-02-04 19:25:22 +01:00
Kroese
53cc6998f0
fix: CPU features 2024-02-02 22:44:43 +01:00
Kroese
d857d71e0d
fix: Merge drive function (#614) 2024-02-01 11:05:33 +01:00
Kroese
003c2766ce
docs: Readme (#613) 2024-01-31 04:28:24 +01:00
Kroese
78594098cc
feat: Disable CoW check on ZFS (#612) 2024-01-31 03:54:40 +01:00
Kroese
3c31bc91e4
feat: Generate unique MAC address (#611) 2024-01-30 04:46:44 +01:00
Kroese
72141bab7a
build: Lint Dockerfile (#610) 2024-01-29 11:51:28 +01:00
Kroese
bc52463aa4
fix: Process signal faster (#609) 2024-01-29 05:54:22 +01:00
Kroese
9fa68908a9
feat: Show download progress (#608) 2024-01-29 05:40:06 +01:00
Kroese
740dbec1b1
build: Exclude web folder (#607) 2024-01-29 02:29:05 +01:00
Kroese
440d203730
fix: Stylesheet (#606) 2024-01-29 02:27:04 +01:00
Kroese
1a83c67e2c
feat: Font smoothing (#605) 2024-01-29 02:01:51 +01:00
Kroese
34a707a2a5
docs: Readme (#603) 2024-01-27 19:51:26 +01:00
Kroese
cabb2cdfc9
docs: Readme (#602) 2024-01-27 19:10:43 +01:00
Kroese
dc52ccf172
docs: Readme (#601) 2024-01-27 19:06:10 +01:00
Kroese
bdd7fec3c3
fix: Space after URL (#600) 2024-01-27 02:01:27 +01:00
Kroese
bd8b03d089
docs: Readme (#599) 2024-01-26 06:29:15 +01:00
Kroese
a10588b0ce
fix: Check dnsmasq (#598) 2024-01-26 02:19:31 +01:00
renovate[bot]
3503b86e12
chore(deps): update peter-evans/dockerhub-description action to v4 (#597)
Co-authored-by: renovate[bot] <29139614+renovate[bot]@users.noreply.github.com>
2024-01-25 17:05:28 +01:00
Kroese
9e124980cd
fix: Disk message (#596) 2024-01-25 17:02:50 +01:00
Kroese
675aa5e122
docs: Readme (#595) 2024-01-23 21:49:18 +01:00
Kroese
2a62d4e938
feat: Display console arguments (#594) 2024-01-23 19:32:15 +01:00
Kroese
9cbb51cc86
feat: Display QEMU version (#593) 2024-01-23 19:29:07 +01:00
Kroese
7790f81d15
fix: Trap exit code (#592) 2024-01-23 18:46:47 +01:00
Kroese
fdbff4879b
fix: Incorrect path (#591) 2024-01-23 03:06:29 +01:00
Kroese
739e679a66
feat: Check for SHM (#590) 2024-01-23 03:04:47 +01:00
Kroese
0dea507d85
feat: Shorter messages (#588) 2024-01-23 00:15:11 +01:00
Kroese
4f524c47d8
docs: Readme (#587) 2024-01-22 03:24:00 +01:00
Kroese
0c74201eb4
feat: CPU flags (#586) 2024-01-21 20:52:53 +01:00
Kroese
1e13258bc9
feat: Dynamic page content (#585) 2024-01-21 18:35:55 +01:00
Kroese
2c7cea042f
feat: Display progress via web (#584) 2024-01-20 19:59:44 +01:00
Kroese
fc92b66ff4
fix: Echo (#583) 2024-01-19 15:12:02 +01:00
Kroese
89ae24a2ac
feat: Add warning macro (#582) 2024-01-19 15:10:40 +01:00
Kroese
bd4a23b287
docs: Readme (#581) 2024-01-19 12:05:54 +01:00
Kroese
1b8c4d9f08
docs: Readme (#580) 2024-01-19 12:01:31 +01:00
Kroese
a1187decb5
docs: Readme (#579) 2024-01-19 08:09:49 +01:00
Kroese
b9d7aa182d
docs: Readme (#578) 2024-01-19 07:50:07 +01:00
Kroese
b908c1118d
fix: Sanitize filename (#577) 2024-01-19 04:17:09 +01:00
Kroese
fab776764f
fix: Strip query parameters from filename (#576) 2024-01-19 03:04:26 +01:00
Kroese
f3e17e399d
feat: Custom VGA adaptor (#573) 2024-01-17 19:42:30 +01:00
Kroese
3706ca873b
docs: Remove latest (#572) 2024-01-15 13:58:23 +01:00
Kroese
10c565f32b
docs: Grammar (#571) 2024-01-15 13:31:21 +01:00
Kroese
d237e5b9e6
docs: Disk passthrough (#570) 2024-01-15 11:54:57 +01:00
Kroese
3c7e1ce12f
docs: DHCP mode (#568) 2024-01-15 05:00:15 +01:00
Kroese
f422f64325
docs: Readme (#567) 2024-01-14 21:22:13 +01:00
Kroese
df0656b345
docs: Readme (#566) 2024-01-14 20:12:51 +01:00
Kroese
1fc9c56c8f
style: Quote variables (#565) 2024-01-14 16:01:15 +01:00
Kroese
893a013ae9
style: Quote variables (#563) 2024-01-14 01:11:58 +01:00
Kroese
6d3812d1d0
fix: Remove cat (#562) 2024-01-14 00:37:36 +01:00
Kroese
6422aec780
fix: Remove cat (#561) 2024-01-14 00:00:59 +01:00
Kroese
fcd7b8a825
fix: Use /tmp for server (#560) 2024-01-13 20:46:05 +01:00
Kroese
575da1f574
feat: Store config in RAM (#559) 2024-01-13 20:25:57 +01:00
Kroese
3a507f5bf6
fix: Display QEMU output (#558) 2024-01-13 18:16:17 +01:00
Kroese
a3d6e3740c
docs: Readme (#557) 2024-01-13 14:51:57 +01:00
Kroese
53e0330e21
feat: Change qcow allocation (#555) 2024-01-12 23:50:04 +01:00
Kroese
f935c1e28a
docs: Readme (#554) 2024-01-12 23:19:01 +01:00
Kroese
6a0c708224
fix: Device support (#552) 2024-01-12 18:01:20 +01:00
Kroese
a407d2d94d
build: Install apt-utils (#551) 2024-01-10 17:58:50 +01:00
Kroese
53605bd6e8
fix: Add package (#546) 2024-01-08 01:46:21 +01:00
Kroese
944faaa927
fix: Remove slash (#545) 2024-01-07 23:56:06 +01:00
Kroese
fb7cfc09de
fix: Set Debian flag (#544) 2024-01-07 21:23:42 +01:00
Kroese
b9ae73e322
build: Remove apt-get upgrade (#543) 2024-01-06 03:17:50 +01:00
36 changed files with 2231 additions and 769 deletions

6
.devcontainer.json Normal file
View File

@ -0,0 +1,6 @@
{
"name": "dsm",
"service": "dsm",
"forwardPorts": [5000],
"dockerComposeFile": "compose.yml"
}

View File

@ -6,7 +6,10 @@
.gitmodules
Dockerfile
Dockerfile.archive
compose.yml
compose.yaml
docker-compose.yml
docker-compose.yaml
*.md

41
.github/ISSUE_TEMPLATE/1-issue.yml vendored Normal file
View File

@ -0,0 +1,41 @@
name: "\U0001F6A8 Technical issue"
description: When you're experiencing problems using the container
body:
- type: input
id: os
attributes:
label: Operating system
description: Your Linux distribution (can be shown by `lsb_release -a`).
placeholder: e.g. Ubuntu 24.04
validations:
required: true
- type: textarea
id: summary
attributes:
label: Description
description: A clear and concise description of your issue.
validations:
required: true
- type: textarea
id: compose
attributes:
label: Docker compose
description: The compose file (or otherwise the `docker run` command used).
render: yaml
validations:
required: true
- type: textarea
id: log
attributes:
label: Docker log
description: The logfile of the container (as shown by `docker logs dsm`).
render: shell
validations:
required: true
- type: textarea
id: screenshot
attributes:
label: Screenshots (optional)
description: Screenshots that might help to make the problem more clear.
validations:
required: false

37
.github/ISSUE_TEMPLATE/2-feature.yml vendored Normal file
View File

@ -0,0 +1,37 @@
name: "\U0001F680 Feature request"
description: Suggest an idea for improving the container
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: textarea
id: problem
attributes:
label: Is your proposal related to a problem?
description: |
Provide a clear and concise description of what the problem is.
For example, "I'm always frustrated when..."
validations:
required: true
- type: textarea
id: solution
attributes:
label: Describe the solution you'd like.
description: |
Provide a clear and concise description of what you want to happen.
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Describe alternatives you've considered.
description: |
Let us know about other solutions you've tried or researched.
validations:
required: true
- type: textarea
id: context
attributes:
label: Additional context
description: |
Is there anything else you can add about the proposal?
You might want to link to related issues here, if you haven't already.

43
.github/ISSUE_TEMPLATE/3-bug.yml vendored Normal file
View File

@ -0,0 +1,43 @@
name: "\U0001F41E Bug report"
description: Create a report to help us improve the container
title: "[Bug]: "
labels: ["bug"]
body:
- type: input
id: os
attributes:
label: Operating system
description: Your Linux distribution (can be shown by `lsb_release -a`).
placeholder: e.g. Ubuntu 24.04
validations:
required: true
- type: textarea
id: summary
attributes:
label: Description
description: Describe the expected behaviour, the actual behaviour, and the steps to reproduce.
validations:
required: true
- type: textarea
id: compose
attributes:
label: Docker compose
description: The compose file (or otherwise the `docker run` command used).
render: yaml
validations:
required: true
- type: textarea
id: log
attributes:
label: Docker log
description: The logfile of the container (as shown by `docker logs dsm`).
render: shell
validations:
required: true
- type: textarea
id: screenshot
attributes:
label: Screenshots (optional)
description: Screenshots that might help to make the problem more clear.
validations:
required: false

26
.github/ISSUE_TEMPLATE/4-question.yml vendored Normal file
View File

@ -0,0 +1,26 @@
name: "\U00002753 General question"
description: Questions about the container not related to an issue
title: "[Question]: "
labels: ["question"]
body:
- type: checkboxes
attributes:
label: Is your question not already answered in the FAQ?
description: Please read the [FAQ](https://github.com/vdsm/virtual-dsm/blob/master/readme.md) carefully to avoid asking duplicate questions.
options:
- label: I made sure the question is not listed in the [FAQ](https://github.com/vdsm/virtual-dsm/blob/master/readme.md).
required: true
- type: checkboxes
attributes:
label: Is this a general question and not a technical issue?
description: For questions related to issues you must use the [technical issue](https://github.com/vdsm/virtual-dsm/issues/new?assignees=&labels=&projects=&template=1-issue.yml) form instead. It contains all the right fields (system info, logfiles, etc.) we need in order to be able to help you.
options:
- label: I am sure my question is not about a technical issue.
required: true
- type: textarea
id: question
attributes:
label: Question
description: What's the question you have about the container?
validations:
required: true

1
.github/ISSUE_TEMPLATE/config.yml vendored Normal file
View File

@ -0,0 +1 @@
blank_issues_enabled: false

View File

@ -8,6 +8,10 @@ on:
paths-ignore:
- '**/*.md'
- '**/*.yml'
- '**/*.js'
- '**/*.css'
- '**/*.html'
- 'web/**'
- '.gitignore'
- '.dockerignore'
- '.github/**'
@ -69,12 +73,12 @@ jobs:
password: ${{ secrets.GITHUB_TOKEN }}
-
name: Build Docker image
uses: docker/build-push-action@v5
uses: docker/build-push-action@v6
with:
context: .
push: true
provenance: false
platforms: linux/amd64,linux/arm64,linux/arm
platforms: linux/amd64,linux/arm64
tags: ${{ steps.meta.outputs.tags }}
labels: ${{ steps.meta.outputs.labels }}
annotations: ${{ steps.meta.outputs.annotations }}
@ -99,3 +103,15 @@ jobs:
url: ${{ secrets.GITLAB_URL }}
token: ${{ secrets.GITLAB_TOKEN }}
username: ${{ secrets.GITLAB_USERNAME }}
-
name: Send mail
uses: action-pack/send-mail@v1
with:
to: ${{secrets.MAILTO}}
from: Github Actions <${{secrets.MAILTO}}>
connection_url: ${{secrets.MAIL_CONNECTION}}
subject: Build of ${{ github.event.repository.name }} v${{ steps.meta.outputs.version }} completed
body: |
The build job of ${{ github.event.repository.name }} v${{ steps.meta.outputs.version }} was completed successfully!
See https://github.com/${{ github.repository }}/actions for more information.

View File

@ -7,8 +7,18 @@ jobs:
name: shellcheck
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Run ShellCheck
-
name: Checkout
uses: actions/checkout@v4
-
name: Run ShellCheck
uses: ludeeus/action-shellcheck@master
env:
SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2002 -e SC2223 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028
env:
SHELLCHECK_OPTS: -x --source-path=src -e SC2001 -e SC2034 -e SC2064 -e SC2317 -e SC2153 -e SC2028
-
name: Lint Dockerfile
uses: hadolint/hadolint-action@v3.1.0
with:
dockerfile: Dockerfile
ignore: DL3008,DL3003,DL3006,DL3013
failure-threshold: warning

View File

@ -15,7 +15,7 @@ jobs:
- uses: actions/checkout@v4
-
name: Docker Hub Description
uses: peter-evans/dockerhub-description@v3
uses: peter-evans/dockerhub-description@v4
with:
username: ${{ secrets.DOCKERHUB_USERNAME }}
password: ${{ secrets.DOCKERHUB_TOKEN }}

View File

@ -3,6 +3,7 @@ on:
pull_request:
paths:
- '**/*.sh'
- 'Dockerfile'
- '.github/workflows/test.yml'
- '.github/workflows/check.yml'

View File

@ -1,4 +1,4 @@
FROM qemux/qemu-host as builder
FROM qemux/qemu-host:2.05 AS builder
# FROM golang as builder
# WORKDIR /
@ -10,48 +10,60 @@ FROM qemux/qemu-host as builder
FROM debian:trixie-slim
ARG TARGETPLATFORM
ARG VERSION_ARG="0.0"
ARG DEBCONF_NOWARNINGS="yes"
ARG DEBIAN_FRONTEND noninteractive
ARG DEBIAN_FRONTEND="noninteractive"
ARG DEBCONF_NONINTERACTIVE_SEEN="true"
RUN apt-get update && apt-get -y upgrade \
&& if [ "$TARGETPLATFORM" != "linux/amd64" ]; then extra="qemu-user"; fi \
&& apt-get --no-install-recommends -y install \
RUN set -eu && \
apt-get update && \
apt-get --no-install-recommends -y install \
jq \
tini \
curl \
cpio \
wget \
fdisk \
unzip \
socat \
nginx \
procps \
python3 \
python3-pip \
python3-msgpack \
python3-pysodium \
xz-utils \
iptables \
iproute2 \
apt-utils \
dnsmasq \
fakeroot \
net-tools \
e2fsprogs \
qemu-utils \
iputils-ping \
ca-certificates \
netcat-openbsd \
qemu-system-x86 \
"$extra" \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
qemu-system-x86 && \
apt-get clean && \
pip3 install --no-cache-dir --break-system-packages --root-user-action=ignore dissect.cstruct && \
mkdir -p /etc/qemu && \
echo "allow br0" > /etc/qemu/bridge.conf && \
unlink /etc/nginx/sites-enabled/default && \
sed -i 's/^worker_processes.*/worker_processes 1;/' /etc/nginx/nginx.conf && \
echo "$VERSION_ARG" > /run/version && \
rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
COPY ./src /run/
COPY --from=builder /qemu-host.bin /run/host.bin
RUN chmod +x /run/*.sh && chmod +x /run/*.bin
COPY --chmod=755 ./src /run/
COPY --chmod=755 ./web /var/www/
COPY --chmod=755 --from=builder /qemu-host.bin /run/host.bin
COPY --chmod=744 ./web/conf/nginx.conf /etc/nginx/sites-enabled/web.conf
ADD --chmod=775 https://raw.githubusercontent.com/sud0woodo/patology/refs/heads/main/patology.py /run/extract.py
VOLUME /storage
EXPOSE 22 139 445 5000
ENV RAM_SIZE "1G"
ENV DISK_SIZE "16G"
ENV CPU_CORES "1"
ARG VERSION_ARG="0.0"
RUN echo "$VERSION_ARG" > /run/version
ENV RAM_SIZE="2G"
ENV CPU_CORES="2"
ENV DISK_SIZE="16G"
HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh

17
compose.yml Normal file
View File

@ -0,0 +1,17 @@
services:
dsm:
container_name: dsm
image: vdsm/virtual-dsm
environment:
DISK_SIZE: "16G"
devices:
- /dev/kvm
- /dev/net/tun
cap_add:
- NET_ADMIN
ports:
- 5000:5000
volumes:
- ./dsm:/storage
restart: always
stop_grace_period: 2m

View File

@ -1,21 +0,0 @@
version: "3"
services:
dsm:
container_name: dsm
image: vdsm/virtual-dsm:latest
environment:
DISK_SIZE: "16G"
RAM_SIZE: "1G"
CPU_CORES: "1"
devices:
- /dev/kvm
device_cgroup_rules:
- 'c *:* rwm'
cap_add:
- NET_ADMIN
ports:
- 5000:5000
volumes:
- /var/dsm:/storage
restart: on-failure
stop_grace_period: 2m

77
kubernetes.yml Normal file
View File

@ -0,0 +1,77 @@
---
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dsm-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 16Gi
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: dsm
labels:
name: dsm
spec:
replicas: 1
selector:
matchLabels:
app: dsm
template:
metadata:
labels:
app: dsm
spec:
containers:
- name: dsm
image: vdsm/virtual-dsm
env:
- name: DISK_SIZE
value: "16G"
ports:
- containerPort: 5000
name: http
protocol: TCP
securityContext:
capabilities:
add:
- NET_ADMIN
privileged: true
volumeMounts:
- mountPath: /storage
name: storage
- mountPath: /dev/kvm
name: dev-kvm
- mountPath: /dev/net/tun
name: dev-tun
terminationGracePeriodSeconds: 120
volumes:
- name: storage
persistentVolumeClaim:
claimName: dsm-pvc
- hostPath:
path: /dev/kvm
name: dev-kvm
- hostPath:
path: /dev/net/tun
type: CharDevice
name: dev-tun
---
apiVersion: v1
kind: Service
metadata:
name: dsm
spec:
internalTrafficPolicy: Cluster
ports:
- name: http
port: 5000
protocol: TCP
targetPort: 5000
selector:
app: dsm
type: ClusterIP

317
readme.md
View File

@ -1,223 +1,296 @@
<h1 align="center">Virtual DSM for Docker<br />
<h1 align="center">Virtual DSM<br />
<div align="center">
<img src="https://github.com/vdsm/virtual-dsm/raw/master/.github/screen.jpg" title="Screenshot" style="max-width:100%;" width="432" />
<a href="https://github.com/vdsm/virtual-dsm"><img src="https://github.com/vdsm/virtual-dsm/raw/master/.github/screen.jpg" title="Screenshot" style="max-width:100%;" width="432" /></a>
</div>
<div align="center">
[![Build]][build_url]
[![Version]][tag_url]
[![Size]][tag_url]
[![Package]][pkg_url]
[![Pulls]][hub_url]
</div></h1>
Virtual DSM in a docker container.
Virtual DSM in a Docker container.
## Features
## Features
- Multiple disks
- KVM acceleration
- GPU passthrough
- Upgrades supported
## Usage
## Usage 🐳
Via `docker-compose.yml`
##### Via Docker Compose:
```yaml
version: "3"
services:
dsm:
container_name: dsm
image: vdsm/virtual-dsm:latest
image: vdsm/virtual-dsm
environment:
DISK_SIZE: "16G"
devices:
- /dev/kvm
- /dev/net/tun
cap_add:
- NET_ADMIN
ports:
- 5000:5000
volumes:
- /var/dsm:/storage
restart: on-failure
- ./dsm:/storage
restart: always
stop_grace_period: 2m
```
Via `docker run`
##### Via Docker CLI:
```bash
docker run -it --rm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-timeout 60 vdsm/virtual-dsm:latest
docker run -it --rm --name dsm -p 5000:5000 --device=/dev/kvm --device=/dev/net/tun --cap-add NET_ADMIN -v "${PWD:-.}/dsm:/storage" --stop-timeout 120 vdsm/virtual-dsm
```
## FAQ
##### Via Kubernetes:
* ### How do I change the size of the virtual disk?
```shell
kubectl apply -f https://raw.githubusercontent.com/vdsm/virtual-dsm/refs/heads/master/kubernetes.yml
```
To expand the default size of 16 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity:
##### Via Github Codespaces:
```yaml
environment:
DISK_SIZE: "128G"
```
[![Open in GitHub Codespaces](https://github.com/codespaces/badge.svg)](https://codespaces.new/vdsm/virtual-dsm)
This can also be used to resize the existing disk to a larger capacity without any data loss.
## FAQ 💬
* ### How do I change the location of the virtual disk?
### How do I use it?
To change the location of the virtual disk, include the following bind mount in your compose file:
Very simple! These are the steps:
```yaml
volumes:
- /var/dsm:/storage
```
- Start the container and connect to [port 5000](http://127.0.0.1:5000/) using your web browser.
Replace the example path `/var/dsm` with the desired storage folder.
- Wait until DSM finishes its installation
* ### How do I add multiple disks?
- Choose an username and password, and you will be taken to the desktop.
To create additional disks, modify your compose file like this:
Enjoy your brand new NAS, and don't forget to star this repo!
```yaml
environment:
DISK2_SIZE: "32G"
DISK3_SIZE: "64G"
volumes:
- /home/example:/storage2
- /mnt/data/example:/storage3
```
### How do I change the storage location?
* ### How do I create a growable disk?
To change the storage location, include the following bind mount in your compose file:
By default, the entire capacity of the disk is reserved in advance.
```yaml
volumes:
- ./dsm:/storage
```
To create a growable disk that only allocates space that is actually used, add the following environment variables:
Replace the example path `./dsm` with the desired storage folder or named volume.
```yaml
environment:
ALLOCATE: "N"
DISK_FMT: "qcow2"
```
### How do I change the size of the disk?
Please note that this may reduce the write performance of the disk.
To expand the default size of 16 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity:
* ### How do I increase the amount of CPU or RAM?
```yaml
environment:
DISK_SIZE: "128G"
```
By default, a single core and 1 GB of RAM are allocated to the container.
> [!TIP]
> This can also be used to resize the existing disk to a larger capacity without any data loss.
To increase this, add the following environment variables:
### How do I create a growable disk?
```yaml
environment:
RAM_SIZE: "4G"
CPU_CORES: "4"
```
By default, the entire capacity of the disk will be reserved in advance.
* ### How do I verify if my system supports KVM?
To create a growable disk that only allocates space that is actually used, add the following environment variable:
To verify if your system supports KVM, run the following commands:
```yaml
environment:
DISK_FMT: "qcow2"
```
```bash
sudo apt install cpu-checker
sudo kvm-ok
```
### How do I add multiple disks?
If you receive an error from `kvm-ok` indicating that KVM acceleration can't be used, check the virtualization settings in the BIOS.
To create additional disks, modify your compose file like this:
* ### How do I assign an individual IP address to the container?
```yaml
environment:
DISK2_SIZE: "32G"
DISK3_SIZE: "64G"
volumes:
- ./example2:/storage2
- ./example3:/storage3
```
By default, the container uses bridge networking, which shares the IP address with the host.
### How do I pass-through a disk?
If you want to assign an individual IP address to the container, you can create a macvlan network as follows:
It is possible to pass-through disk devices or partitions directly by adding them to your compose file in this way:
```bash
docker network create -d macvlan \
--subnet=192.168.0.0/24 \
--gateway=192.168.0.1 \
--ip-range=192.168.0.100/28 \
-o parent=eth0 vdsm
```
```yaml
devices:
- /dev/sdb:/disk1
- /dev/sdc1:/disk2
```
Be sure to modify these values to match your local subnet.
Make sure it is totally empty (without any filesystem), otherwise DSM may not format it as a volume.
Once you have created the network, change your compose file to look as follows:
### How do I change the amount of CPU or RAM?
```yaml
services:
dsm:
container_name: dsm
..<snip>..
networks:
vdsm:
ipv4_address: 192.168.0.100
By default, the container will be allowed to use a maximum of 2 CPU cores and 2 GB of RAM.
networks:
vdsm:
external: true
```
If you want to adjust this, you can specify the desired amount using the following environment variables:
An added benefit of this approach is that you won't have to perform any port mapping anymore since all ports will be exposed by default.
```yaml
environment:
RAM_SIZE: "4G"
CPU_CORES: "4"
```
Please note that this IP address won't be accessible from the Docker host due to the design of macvlan, which doesn't permit communication between the two. If this is a concern, you need to create a [second macvlan](https://blog.oddbit.com/post/2018-03-12-using-docker-macvlan-networks/#host-access) as a workaround.
### How do I verify if my system supports KVM?
* ### How can the container acquire an IP address from my router?
First check if your software is compatible using this chart:
After configuring the container for macvlan (see above), it is possible for DSM to become part of your home network by requesting an IP from your router, just like your other devices.
| **Product** | **Linux** | **Win11** | **Win10** | **macOS** |
|---|---|---|---|---|
| Docker CLI | ✅ | ✅ | ❌ | ❌ |
| Docker Desktop | ❌ | ✅ | ❌ | ❌ |
| Podman CLI | ✅ | ✅ | ❌ | ❌ |
| Podman Desktop | ✅ | ✅ | ❌ | ❌ |
To enable this feature, add the following lines to your compose file:
After that you can run the following commands in Linux to check your system:
```yaml
environment:
DHCP: "Y"
device_cgroup_rules:
- 'c *:* rwm'
```
```bash
sudo apt install cpu-checker
sudo kvm-ok
```
Please note that even if you don't want DHCP, it's still recommended to enable this feature as it prevents NAT issues and increases performance by using a `macvtap` interface. In that case just set a static IP from the DSM control panel after you enabled this mode.
If you receive an error from `kvm-ok` indicating that KVM cannot be used, please check whether:
* ### How do I passthrough the GPU?
- the virtualization extensions (`Intel VT-x` or `AMD SVM`) are enabled in your BIOS.
To passthrough your Intel GPU, add the following lines to your compose file:
- you enabled "nested virtualization" if you are running the container inside a virtual machine.
```yaml
environment:
GPU: "Y"
devices:
- /dev/dri
```
- you are not using a cloud provider, as most of them do not allow nested virtualization for their VPS's.
This can be used to enable the facial recognition function in Synology Photos for example.
If you did not receive any error from `kvm-ok` but the container still complains about a missing KVM device, it could help to add `privileged: true` to your compose file (or `sudo` to your `docker` command) to rule out any permission issue.
* ### How do I install a specific version of vDSM?
### How do I assign an individual IP address to the container?
By default, version 7.2.1 will be installed, but if you prefer an older version, you can add its download URL to your compose file as follows:
By default, the container uses bridge networking, which shares the IP address with the host.
```yaml
environment:
URL: "https://global.synologydownload.com/download/DSM/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
```
If you want to assign an individual IP address to the container, you can create a macvlan network as follows:
With this method, it is even possible to switch between different versions while keeping all your file data intact.
```bash
docker network create -d macvlan \
--subnet=192.168.0.0/24 \
--gateway=192.168.0.1 \
--ip-range=192.168.0.100/28 \
-o parent=eth0 vdsm
```
* ### What are the differences compared to the standard DSM?
Be sure to modify these values to match your local subnet.
There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses.
Once you have created the network, change your compose file to look as follows:
* ### Is this project legal?
```yaml
services:
dsm:
container_name: dsm
..<snip>..
networks:
vdsm:
ipv4_address: 192.168.0.100
Yes, this project contains only open-source code and does not distribute any copyrighted material. Neither does it try to circumvent any copyright protection measures. So under all applicable laws, this project would be considered legal.
networks:
vdsm:
external: true
```
However, by installing Synology's Virtual DSM, you must accept their end-user license agreement, which does not permit installation on non-Synology hardware. So only run this project on an official Synology NAS, as any other use will be a violation of their terms and conditions.
An added benefit of this approach is that you won't have to perform any port mapping anymore, since all ports will be exposed by default.
## Disclaimer
> [!IMPORTANT]
> This IP address won't be accessible from the Docker host due to the design of macvlan, which doesn't permit communication between the two. If this is a concern, you need to create a [second macvlan](https://blog.oddbit.com/post/2018-03-12-using-docker-macvlan-networks/#host-access) as a workaround.
Only run this container on Synology hardware, any other use is not permitted by their EULA. The product names, logos, brands, and other trademarks referred to within this project are the property of their respective trademark holders. This project is not affiliated, sponsored, or endorsed by Synology, Inc.
### How can DSM acquire an IP address from my router?
After configuring the container for [macvlan](#how-do-i-assign-an-individual-ip-address-to-the-container), it is possible for DSM to become part of your home network by requesting an IP from your router, just like your other devices.
To enable this mode, in which the container and DSM will have separate IP addresses, add the following lines to your compose file:
```yaml
environment:
DHCP: "Y"
devices:
- /dev/vhost-net
device_cgroup_rules:
- 'c *:* rwm'
```
### How do I pass-through the GPU?
To pass-through your Intel GPU, add the following lines to your compose file:
```yaml
environment:
GPU: "Y"
devices:
- /dev/dri
```
> [!NOTE]
> This can be used to enable the facial recognition function in Synology Photos, but does not provide hardware transcoding for video.
### How do I install a specific version of vDSM?
By default, version 7.2 will be installed, but if you prefer an older version, you can add the download URL of the `.pat` file to your compose file as follows:
```yaml
environment:
URL: "https://global.synologydownload.com/download/DSM/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
```
With this method, it is even possible to switch back and forth between versions while keeping your file data intact.
Alternatively, you can also skip the download and use a local file instead, by binding it in your compose file in this way:
```yaml
volumes:
- ./DSM_VirtualDSM_42218.pat:/boot.pat
```
Replace the example path `./DSM_VirtualDSM_42218.pat` with the filename of your desired `.pat` file. The value of `URL` will be ignored in this case.
### What are the differences compared to the standard DSM?
There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses.
### How do I run Windows in a container?
You can use [dockur/windows](https://github.com/dockur/windows) for that. It shares many of the same features, and even has completely automatic installation.
### How do I run a Linux desktop in a container?
You can use [qemus/qemu](https://github.com/qemus/qemu) in that case.
### Is this project legal?
Yes, this project contains only open-source code and does not distribute any copyrighted material. Neither does it try to circumvent any copyright protection measures. So under all applicable laws, this project will be considered legal.
However, by installing Synology's Virtual DSM, you must accept their end-user license agreement, which does not permit installation on non-Synology hardware. So only run this container on an official Synology NAS, as any other use will be a violation of their terms and conditions.
## Stars 🌟
[![Stars](https://starchart.cc/vdsm/virtual-dsm.svg?variant=adaptive)](https://starchart.cc/vdsm/virtual-dsm)
## Disclaimer ⚖️
*Only run this container on Synology hardware, any other use is not permitted by their EULA. The product names, logos, brands, and other trademarks referred to within this project are the property of their respective trademark holders. This project is not affiliated, sponsored, or endorsed by Synology, Inc.*
[build_url]: https://github.com/vdsm/virtual-dsm/
[hub_url]: https://hub.docker.com/r/vdsm/virtual-dsm
[tag_url]: https://hub.docker.com/r/vdsm/virtual-dsm/tags
[pkg_url]: https://github.com/vdsm/virtual-dsm/pkgs/container/virtual-dsm
[Build]: https://github.com/vdsm/virtual-dsm/actions/workflows/build.yml/badge.svg
[Size]: https://img.shields.io/docker/image-size/vdsm/virtual-dsm/latest?color=066da5&label=size
[Pulls]: https://img.shields.io/docker/pulls/kroese/virtual-dsm.svg?style=flat&label=pulls&logo=docker
[Pulls]: https://img.shields.io/docker/pulls/vdsm/virtual-dsm.svg?style=flat&label=pulls&logo=docker
[Version]: https://img.shields.io/docker/v/vdsm/virtual-dsm/latest?arch=amd64&sort=semver&color=066da5
[Package]: https://img.shields.io/badge/dynamic/json?url=https%3A%2F%2Fipitio.github.io%2Fbackage%2Fvdsm%2Fvirtual-dsm%2Fvirtual-dsm.json&query=%24.downloads&logo=github&style=flat&color=066da5&label=pulls

View File

@ -1,15 +1,18 @@
#!/usr/bin/env bash
set -Eeuo pipefail
[ -f "/run/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
[ ! -f "/run/qemu.pid" ] && echo "QEMU is not running yet.." && exit 0
: "${NETWORK:="Y"}"
file="/run/dsm.url"
address="/run/qemu.ip"
[ -f "/run/shm/qemu.end" ] && echo "QEMU is shutting down.." && exit 1
[ ! -s "/run/shm/qemu.pid" ] && echo "QEMU is not running yet.." && exit 0
[[ "$NETWORK" == [Nn]* ]] && echo "Networking is disabled.." && exit 0
[ ! -f "$file" ] && echo "DSM has not enabled networking yet.." && exit 1
file="/run/shm/dsm.url"
address="/run/shm/qemu.ip"
location=$(cat "$file")
[ ! -s "$file" ] && echo "DSM has not enabled networking yet.." && exit 1
location=$(<"$file")
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
@ -19,7 +22,7 @@ if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
echo "Failed to reach DSM at port $port"
else
echo "Failed to reach DSM at http://$location"
ip="$(cat "$address")"
ip=$(<"$address")
fi
echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1

View File

@ -2,14 +2,42 @@
set -Eeuo pipefail
DEF_OPTS="-nodefaults -boot strict=on"
RAM_OPTS=$(echo "-m $RAM_SIZE" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
CPU_OPTS="-cpu $CPU_MODEL -smp $CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"
MAC_OPTS="-machine type=q35,usb=off,dump-guest-core=off,hpet=off${KVM_OPTS}"
RAM_OPTS=$(echo "-m ${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
CPU_OPTS="-cpu $CPU_FLAGS -smp $CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"
MAC_OPTS="-machine type=q35,smm=off,usb=off,vmport=off,dump-guest-core=off,hpet=off${KVM_OPTS}"
DEV_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
DEV_OPTS="$DEV_OPTS -object rng-random,id=objrng0,filename=/dev/urandom"
DEV_OPTS="$DEV_OPTS -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
DEV_OPTS+=" -object rng-random,id=objrng0,filename=/dev/urandom"
DEV_OPTS+=" -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $DISPLAY_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $DEV_OPTS $ARGUMENTS"
ARGS=$(echo "$ARGS" | sed 's/\t/ /g' | tr -s ' ')
# Check available memory as the very last step
if [[ "$RAM_CHECK" != [Nn]* ]]; then
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
if (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of memory available, please set a lower value."
[[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
info "$msg"
else
if (( (RAM_WANTED + (RAM_SPARE * 3)) > RAM_AVAIL )); then
msg="your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is very close to the $AVAIL_MEM of memory available, please consider a lower value."
if [[ "${FS,,}" != "zfs" ]]; then
warn "$msg"
else
info "$msg"
fi
fi
fi
fi
if [[ "$DEBUG" == [Yy1]* ]]; then
printf "Arguments:\n\n%s\n\n" "${ARGS// -/$'\n-'}"
fi
return 0

View File

@ -3,27 +3,19 @@ set -Eeuo pipefail
# Docker environment variables
: ${DISK_IO:='native'} # I/O Mode, can be set to 'native', 'threads' or 'io_turing'
: ${DISK_FMT:='raw'} # Disk file format, 'raw' by default for best performance
: ${DISK_FLAGS:=''} # Specifies the options for use with the qcow2 disk format
: ${DISK_CACHE:='none'} # Caching mode, can be set to 'writeback' for better performance
: ${DISK_DISCARD:='on'} # Controls whether unmap (TRIM) commands are passed to the host.
: ${DISK_ROTATION:='1'} # Rotation rate, set to 1 for SSD storage and increase for HDD
: "${DISK_IO:="native"}" # I/O Mode, can be set to 'native', 'threads' or 'io_uring'
: "${DISK_FMT:="raw"}" # Disk file format, 'raw' by default for best performance
: "${DISK_TYPE:=""}" # Device type to be used, "sata", "nvme", "blk" or "scsi"
: "${DISK_FLAGS:=""}" # Specifies the options for use with the qcow2 disk format
: "${DISK_CACHE:="none"}" # Caching mode, can be set to 'writeback' for better performance
: "${DISK_DISCARD:="on"}" # Controls whether unmap (TRIM) commands are passed to the host.
: "${DISK_ROTATION:="1"}" # Rotation rate, set to 1 for SSD storage and increase for HDD
BOOT="$STORAGE/$BASE.boot.img"
SYSTEM="$STORAGE/$BASE.system.img"
[ ! -f "$BOOT" ] && error "Virtual DSM boot-image does not exist ($BOOT)" && exit 81
[ ! -f "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82
DISK_OPTS="\
-object iothread,id=io2 \
-device virtio-scsi-pci,id=hw-synoboot,iothread=io2,bus=pcie.0,addr=0xa \
-drive file=$BOOT,if=none,id=drive-synoboot,format=raw,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on \
-device scsi-hd,bus=hw-synoboot.0,channel=0,scsi-id=0,lun=0,drive=drive-synoboot,id=synoboot0,rotation_rate=$DISK_ROTATION,bootindex=1 \
-device virtio-scsi-pci,id=hw-synosys,iothread=io2,bus=pcie.0,addr=0xb \
-drive file=$SYSTEM,if=none,id=drive-synosys,format=raw,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on \
-device scsi-hd,bus=hw-synosys.0,channel=0,scsi-id=0,lun=0,drive=drive-synosys,id=synosys0,rotation_rate=$DISK_ROTATION,bootindex=2"
[ ! -s "$BOOT" ] && error "Virtual DSM boot-image does not exist ($BOOT)" && exit 81
[ ! -s "$SYSTEM" ] && error "Virtual DSM system-image does not exist ($SYSTEM)" && exit 82
fmt2ext() {
local DISK_FMT=$1
@ -61,8 +53,8 @@ getSize() {
local DISK_FILE=$1
local DISK_EXT DISK_FMT
DISK_EXT="$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')"
DISK_FMT="$(ext2fmt "$DISK_EXT")"
DISK_EXT=$(echo "${DISK_FILE//*./}" | sed 's/^.*\.//')
DISK_FMT=$(ext2fmt "$DISK_EXT")
case "${DISK_FMT,,}" in
raw)
@ -80,20 +72,31 @@ getSize() {
isCow() {
local FS=$1
if [[ "${FS,,}" == "xfs" || "${FS,,}" == "zfs" || "${FS,,}" == "btrfs" || "${FS,,}" == "bcachefs" ]]; then
if [[ "${FS,,}" == "btrfs" ]]; then
return 0
fi
return 1
}
supportsDirect() {
local FS=$1
if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then
return 1
fi
return 0
}
createDisk() {
local DISK_FILE=$1
local DISK_SPACE=$2
local DISK_DESC=$3
local DISK_FMT=$4
local FS=$5
local DATA_SIZE DIR SPACE FA
local DATA_SIZE DIR SPACE GB FA
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
@ -106,14 +109,16 @@ createDisk() {
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
if (( DATA_SIZE > SPACE )); then
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
error "Not enough free space to create a $DISK_DESC of $DISK_SPACE in $DIR, it has only $SPACE_GB GB available..."
GB=$(formatBytes "$SPACE")
error "Not enough free space to create a $DISK_DESC of ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available..."
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76
fi
fi
info "Creating a $DISK_TYPE $DISK_DESC image in $DISK_FMT format with a size of $DISK_SPACE..."
local FAIL="Could not create a $DISK_TYPE $DISK_FMT $DISK_DESC image of $DISK_SPACE ($DISK_FILE)"
html "Creating a $DISK_DESC image..."
info "Creating a ${DISK_SPACE/G/ GB} $DISK_STYLE $DISK_DESC image in $DISK_FMT format..."
local FAIL="Could not create a $DISK_STYLE $DISK_FMT $DISK_DESC image of ${DISK_SPACE/G/ GB} ($DISK_FILE)"
case "${DISK_FMT,,}" in
raw)
@ -136,10 +141,12 @@ createDisk() {
else
# Create an empty file
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE"; then
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
rm -f "$DISK_FILE"
error "$FAIL" && exit 77
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE" &>/dev/null; then
if ! fallocate -l -x "$DATA_SIZE" "$DISK_FILE"; then
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
rm -f "$DISK_FILE"
error "$FAIL" && exit 77
fi
fi
fi
@ -148,8 +155,8 @@ createDisk() {
qcow2)
local DISK_PARAM="$DISK_ALLOC"
isCow "$FS" && DISK_PARAM="$DISK_PARAM,nocow=on"
[ -n "$DISK_FLAGS" ] && DISK_PARAM="$DISK_PARAM,$DISK_FLAGS"
isCow "$FS" && DISK_PARAM+=",nocow=on"
[ -n "$DISK_FLAGS" ] && DISK_PARAM+=",$DISK_FLAGS"
if ! qemu-img create -f "$DISK_FMT" -o "$DISK_PARAM" -- "$DISK_FILE" "$DATA_SIZE" ; then
rm -f "$DISK_FILE"
@ -169,12 +176,13 @@ createDisk() {
}
resizeDisk() {
local DISK_FILE=$1
local DISK_SPACE=$2
local DISK_DESC=$3
local DISK_FMT=$4
local FS=$5
local CUR_SIZE DATA_SIZE DIR SPACE
local CUR_SIZE DATA_SIZE DIR SPACE GB
CUR_SIZE=$(getSize "$DISK_FILE")
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
@ -188,15 +196,17 @@ resizeDisk() {
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
if (( REQ > SPACE )); then
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
error "Not enough free space to resize $DISK_DESC to $DISK_SPACE in $DIR, it has only $SPACE_GB GB available.."
GB=$(formatBytes "$SPACE")
error "Not enough free space to resize $DISK_DESC to ${DISK_SPACE/G/ GB} in $DIR, it has only $GB available.."
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 74
fi
fi
local GB=$(( (CUR_SIZE + 1073741823)/1073741824 ))
info "Resizing $DISK_DESC from ${GB}G to $DISK_SPACE..."
local FAIL="Could not resize the $DISK_TYPE $DISK_FMT $DISK_DESC image from ${GB}G to $DISK_SPACE ($DISK_FILE)"
GB=$(formatBytes "$CUR_SIZE")
MSG="Resizing $DISK_DESC from $GB to ${DISK_SPACE/G/ GB}..."
info "$MSG" && html "$MSG"
local FAIL="Could not resize the $DISK_STYLE $DISK_FMT $DISK_DESC image from ${GB} to ${DISK_SPACE/G/ GB} ($DISK_FILE)"
case "${DISK_FMT,,}" in
raw)
@ -211,9 +221,11 @@ resizeDisk() {
else
# Resize file by allocating more space
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE"; then
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
error "$FAIL" && exit 75
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE" &>/dev/null; then
if ! fallocate -l -x "$DATA_SIZE" "$DISK_FILE"; then
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
error "$FAIL" && exit 75
fi
fi
fi
@ -232,6 +244,7 @@ resizeDisk() {
}
convertDisk() {
local SOURCE_FILE=$1
local SOURCE_FMT=$2
local DST_FILE=$3
@ -248,7 +261,7 @@ convertDisk() {
if [[ "$ALLOCATE" != [Nn]* ]]; then
local DIR CUR_SIZE SPACE
local DIR CUR_SIZE SPACE GB
# Check free diskspace
DIR=$(dirname "$TMP_FILE")
@ -256,39 +269,43 @@ convertDisk() {
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
if (( CUR_SIZE > SPACE )); then
local SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $SPACE_GB GB available..."
GB=$(formatBytes "$SPACE")
error "Not enough free space to convert $DISK_DESC to $DST_FMT in $DIR, it has only $GB available..."
error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76
fi
fi
info "Converting $DISK_DESC to $DST_FMT, please wait until completed..."
local msg="Converting $DISK_DESC to $DST_FMT"
html "$msg..."
info "$msg, please wait until completed..."
local CONV_FLAGS="-p"
local DISK_PARAM="$DISK_ALLOC"
isCow "$FS" && DISK_PARAM="$DISK_PARAM,nocow=on"
isCow "$FS" && DISK_PARAM+=",nocow=on"
if [[ "$DST_FMT" != "raw" ]]; then
if [[ "$ALLOCATE" == [Nn]* ]]; then
CONV_FLAGS="$CONV_FLAGS -c"
fi
[ -n "$DISK_FLAGS" ] && DISK_PARAM="$DISK_PARAM,$DISK_FLAGS"
if [[ "$ALLOCATE" == [Nn]* ]]; then
CONV_FLAGS+=" -c"
fi
[ -n "$DISK_FLAGS" ] && DISK_PARAM+=",$DISK_FLAGS"
fi
# shellcheck disable=SC2086
if ! qemu-img convert -f "$SOURCE_FMT" $CONV_FLAGS -o "$DISK_PARAM" -O "$DST_FMT" -- "$SOURCE_FILE" "$TMP_FILE"; then
rm -f "$TMP_FILE"
error "Failed to convert $DISK_TYPE $DISK_DESC image to $DST_FMT format in $DIR, is there enough space available?" && exit 79
error "Failed to convert $DISK_STYLE $DISK_DESC image to $DST_FMT format in $DIR, is there enough space available?" && exit 79
fi
if [[ "$DST_FMT" == "raw" ]]; then
if [[ "$ALLOCATE" != [Nn]* ]]; then
# Work around qemu-img bug
CUR_SIZE=$(stat -c%s "$TMP_FILE")
if ! fallocate -l "$CUR_SIZE" "$TMP_FILE"; then
error "Failed to allocate $CUR_SIZE bytes for $DISK_DESC image $TMP_FILE"
if [[ "$ALLOCATE" != [Nn]* ]]; then
# Work around qemu-img bug
CUR_SIZE=$(stat -c%s "$TMP_FILE")
if ! fallocate -l "$CUR_SIZE" "$TMP_FILE" &>/dev/null; then
if ! fallocate -l -x "$CUR_SIZE" "$TMP_FILE"; then
error "Failed to allocate $CUR_SIZE bytes for $DISK_DESC image $TMP_FILE"
fi
fi
fi
fi
rm -f "$SOURCE_FILE"
@ -301,12 +318,15 @@ convertDisk() {
fi
fi
info "Conversion of $DISK_DESC to $DST_FMT completed succesfully!"
msg="Conversion of $DISK_DESC"
html "$msg completed..."
info "$msg to $DST_FMT completed successfully!"
return 0
}
checkFS () {
local FS=$1
local DISK_FILE=$2
local DISK_DESC=$3
@ -323,6 +343,10 @@ checkFS () {
info "Warning: the filesystem of $DIR is FUSE, this extra layer will negatively affect performance!"
fi
if ! supportsDirect "$FS"; then
info "Warning: the filesystem of $DIR is $FS, which does not support O_DIRECT mode, adjusting settings..."
fi
if isCow "$FS"; then
if [ -f "$DISK_FILE" ]; then
FA=$(lsattr "$DISK_FILE")
@ -335,89 +359,207 @@ checkFS () {
return 0
}
createDevice () {
local DISK_FILE=$1
local DISK_TYPE=$2
local DISK_INDEX=$3
local DISK_ADDRESS=$4
local DISK_FMT=$5
local DISK_IO=$6
local DISK_CACHE=$7
local DISK_SERIAL=$8
local DISK_SECTORS=$9
local DISK_ID="data$DISK_INDEX"
local index=""
[ -n "$DISK_INDEX" ] && index=",bootindex=$DISK_INDEX"
local result=" -drive file=$DISK_FILE,id=$DISK_ID,format=$DISK_FMT,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on"
case "${DISK_TYPE,,}" in
"none" ) ;;
"auto" )
echo "$result"
;;
"usb" )
result+=",if=none \
-device usb-storage,drive=${DISK_ID}${index}${DISK_SERIAL}${DISK_SECTORS}"
echo "$result"
;;
"nvme" )
result+=",if=none \
-device nvme,drive=${DISK_ID}${index},serial=deadbeaf${DISK_INDEX}${DISK_SERIAL}${DISK_SECTORS}"
echo "$result"
;;
"ide" | "sata" )
result+=",if=none \
-device ich9-ahci,id=ahci${DISK_INDEX},addr=$DISK_ADDRESS \
-device ide-hd,drive=${DISK_ID},bus=ahci$DISK_INDEX.0,rotation_rate=$DISK_ROTATION${index}${DISK_SERIAL}${DISK_SECTORS}"
echo "$result"
;;
"blk" | "virtio-blk" )
result+=",if=none \
-device virtio-blk-pci,drive=${DISK_ID},bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2${index}${DISK_SERIAL}${DISK_SECTORS}"
echo "$result"
;;
"scsi" | "virtio-scsi" )
result+=",if=none \
-device virtio-scsi-pci,id=${DISK_ID}b,bus=pcie.0,addr=$DISK_ADDRESS,iothread=io2 \
-device scsi-hd,drive=${DISK_ID},bus=${DISK_ID}b.0,channel=0,scsi-id=0,lun=0,rotation_rate=$DISK_ROTATION${index}${DISK_SERIAL}${DISK_SECTORS}"
echo "$result"
;;
esac
return 0
}
addDisk () {
local DISK_ID=$1
local DISK_BASE=$2
local DISK_EXT=$3
local DISK_DESC=$4
local DISK_SPACE=$5
local DISK_INDEX=$6
local DISK_ADDRESS=$7
local DISK_FMT=$8
local DISK_BASE=$1
local DISK_TYPE=$2
local DISK_DESC=$3
local DISK_SPACE=$4
local DISK_INDEX=$5
local DISK_ADDRESS=$6
local DISK_FMT=$7
local DISK_IO=$8
local DISK_CACHE=$9
local DISK_EXT DIR SPACE DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
DISK_EXT=$(fmt2ext "$DISK_FMT")
local DISK_FILE="$DISK_BASE.$DISK_EXT"
local DIR DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE
DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0
[ -z "$DISK_SPACE" ] && DISK_SPACE="16G"
DISK_SPACE=$(echo "${DISK_SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
SPACE="${DISK_SPACE// /}"
[ -z "$SPACE" ] && SPACE="16G"
[ -z "${SPACE//[0-9. ]}" ] && SPACE="${SPACE}G"
SPACE=$(echo "${SPACE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
if ! numfmt --from=iec "$SPACE" &>/dev/null; then
error "Invalid value for ${DISK_DESC^^}_SIZE: $DISK_SPACE" && exit 73
fi
DATA_SIZE=$(numfmt --from=iec "$SPACE")
if (( DATA_SIZE < 6442450944 )); then
if (( DATA_SIZE < 1 )); then
error "Invalid value for ${DISK_DESC^^}_SIZE: $DISK_SPACE" && exit 73
else
error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 73
fi
error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 73
fi
FS=$(stat -f -c %T "$DIR")
checkFS "$FS" "$DISK_FILE" "$DISK_DESC" || exit $?
if ! [ -f "$DISK_FILE" ] ; then
if ! supportsDirect "$FS"; then
DISK_IO="threads"
DISK_CACHE="writeback"
fi
if ! [ -s "$DISK_FILE" ] ; then
if [[ "${DISK_FMT,,}" != "raw" ]]; then
PREV_FMT="raw"
else
PREV_FMT="qcow2"
fi
PREV_EXT="$(fmt2ext "$PREV_FMT")"
if [ -f "$DISK_BASE.$PREV_EXT" ] ; then
PREV_EXT=$(fmt2ext "$PREV_FMT")
if [ -s "$DISK_BASE.$PREV_EXT" ] ; then
convertDisk "$DISK_BASE.$PREV_EXT" "$PREV_FMT" "$DISK_FILE" "$DISK_FMT" "$DISK_BASE" "$DISK_DESC" "$FS" || exit $?
fi
fi
if [ -f "$DISK_FILE" ]; then
if [ -s "$DISK_FILE" ]; then
CUR_SIZE=$(getSize "$DISK_FILE")
if (( DATA_SIZE > CUR_SIZE )); then
resizeDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
resizeDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
fi
else
createDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
createDisk "$DISK_FILE" "$SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
fi
DISK_OPTS="$DISK_OPTS \
-device virtio-scsi-pci,id=hw-$DISK_ID,iothread=io2,bus=pcie.0,addr=$DISK_ADDRESS \
-drive file=$DISK_FILE,if=none,id=drive-$DISK_ID,format=$DISK_FMT,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on \
-device scsi-hd,bus=hw-$DISK_ID.0,channel=0,scsi-id=0,lun=0,drive=drive-$DISK_ID,id=$DISK_ID,rotation_rate=$DISK_ROTATION,bootindex=$DISK_INDEX"
DISK_OPTS+=$(createDevice "$DISK_FILE" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" "" "")
return 0
}
DISK_EXT="$(fmt2ext "$DISK_FMT")" || exit $?
addDevice () {
local DISK_DEV=$1
local DISK_TYPE=$2
local DISK_INDEX=$3
local DISK_ADDRESS=$4
[ -z "$DISK_DEV" ] && return 0
[ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
local sectors=""
local result logical physical
result=$(fdisk -l "$DISK_DEV" | grep -m 1 -o "(logical/physical): .*" | cut -c 21-)
logical="${result%% *}"
physical=$(echo "$result" | grep -m 1 -o "/ .*" | cut -c 3-)
physical="${physical%% *}"
if [ -n "$physical" ]; then
if [[ "$physical" == "512" ]] || [[ "$physical" == "4096" ]]; then
if [[ "$physical" == "4096" ]]; then
sectors=",logical_block_size=$logical,physical_block_size=$physical"
fi
else
warn "Unknown physical sector size: $physical for $DISK_DEV"
fi
else
warn "Failed to determine the sector size for $DISK_DEV"
fi
DISK_OPTS+=$(createDevice "$DISK_DEV" "$DISK_TYPE" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE" "" "$sectors")
return 0
}
html "Initializing disks..."
[ -z "${DISK_OPTS:-}" ] && DISK_OPTS=""
[ -z "${DISK_TYPE:-}" ] && DISK_TYPE="scsi"
[ -z "${DISK_NAME:-}" ] && DISK_NAME="data"
case "${DISK_TYPE,,}" in
"ide" | "sata" | "nvme" | "usb" | "scsi" | "blk" | "auto" | "none" ) ;;
* ) error "Invalid DISK_TYPE specified, value \"$DISK_TYPE\" is not recognized!" && exit 80 ;;
esac
if [ -z "$ALLOCATE" ]; then
if [[ "${DISK_FMT,,}" == "raw" ]]; then
ALLOCATE="Y"
else
ALLOCATE="N"
fi
fi
if [[ "$ALLOCATE" == [Nn]* ]]; then
DISK_TYPE="growable"
DISK_STYLE="growable"
DISK_ALLOC="preallocation=off"
else
DISK_TYPE="preallocated"
DISK_STYLE="preallocated"
DISK_ALLOC="preallocation=falloc"
fi
DISK1_FILE="$STORAGE/data"
DISK_OPTS+=$(createDevice "$BOOT" "$DISK_TYPE" "1" "0xa" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
DISK_OPTS+=$(createDevice "$SYSTEM" "$DISK_TYPE" "2" "0xb" "raw" "$DISK_IO" "$DISK_CACHE" "" "")
DISK1_FILE="$STORAGE/${DISK_NAME}"
if [[ ! -f "$DISK1_FILE.img" ]] && [[ -f "$STORAGE/data${DISK_SIZE}.img" ]]; then
# Fallback for legacy installs
mv "$STORAGE/data${DISK_SIZE}.img" "$DISK1_FILE.img"
fi
DISK2_FILE="/storage2/data2"
DISK2_FILE="/storage2/${DISK_NAME}2"
if [ ! -f "$DISK2_FILE.img" ]; then
# Fallback for legacy installs
FALLBACK="/storage2/data.img"
@ -430,7 +572,7 @@ if [ ! -f "$DISK2_FILE.img" ]; then
fi
fi
DISK3_FILE="/storage3/data3"
DISK3_FILE="/storage3/${DISK_NAME}3"
if [ ! -f "$DISK3_FILE.img" ]; then
# Fallback for legacy installs
FALLBACK="/storage3/data.img"
@ -443,53 +585,53 @@ if [ ! -f "$DISK3_FILE.img" ]; then
fi
fi
DISK4_FILE="/storage4/data4"
DISK5_FILE="/storage5/data5"
DISK6_FILE="/storage6/data6"
DISK4_FILE="/storage4/${DISK_NAME}4"
: ${DISK2_SIZE:=''}
: ${DISK3_SIZE:=''}
: ${DISK4_SIZE:=''}
: ${DISK5_SIZE:=''}
: ${DISK6_SIZE:=''}
: "${DISK2_SIZE:=""}"
: "${DISK3_SIZE:=""}"
: "${DISK4_SIZE:=""}"
addDisk "userdata" "$DISK1_FILE" "$DISK_EXT" "disk" "$DISK_SIZE" "3" "0xc" "$DISK_FMT" || exit $?
addDisk "userdata2" "$DISK2_FILE" "$DISK_EXT" "disk2" "$DISK2_SIZE" "4" "0xd" "$DISK_FMT" || exit $?
addDisk "userdata3" "$DISK3_FILE" "$DISK_EXT" "disk3" "$DISK3_SIZE" "5" "0xe" "$DISK_FMT" || exit $?
addDisk "userdata4" "$DISK4_FILE" "$DISK_EXT" "disk4" "$DISK4_SIZE" "9" "0x7" "$DISK_FMT" || exit $?
addDisk "userdata5" "$DISK5_FILE" "$DISK_EXT" "disk5" "$DISK5_SIZE" "10" "0x8" "$DISK_FMT" || exit $?
addDisk "userdata6" "$DISK6_FILE" "$DISK_EXT" "disk6" "$DISK6_SIZE" "11" "0x9" "$DISK_FMT" || exit $?
: "${DEVICE:=""}" # Docker variables to passthrough a block device, like /dev/vdc1.
: "${DEVICE2:=""}"
: "${DEVICE3:=""}"
: "${DEVICE4:=""}"
addDevice () {
[ -z "$DEVICE" ] && [ -b "/disk" ] && DEVICE="/disk"
[ -z "$DEVICE" ] && [ -b "/disk1" ] && DEVICE="/disk1"
[ -z "$DEVICE2" ] && [ -b "/disk2" ] && DEVICE2="/disk2"
[ -z "$DEVICE3" ] && [ -b "/disk3" ] && DEVICE3="/disk3"
[ -z "$DEVICE4" ] && [ -b "/disk4" ] && DEVICE4="/disk4"
local DISK_ID=$1
local DISK_DEV=$2
local DISK_INDEX=$3
local DISK_ADDRESS=$4
[ -z "$DEVICE" ] && [ -b "/dev/disk1" ] && DEVICE="/dev/disk1"
[ -z "$DEVICE2" ] && [ -b "/dev/disk2" ] && DEVICE2="/dev/disk2"
[ -z "$DEVICE3" ] && [ -b "/dev/disk3" ] && DEVICE3="/dev/disk3"
[ -z "$DEVICE4" ] && [ -b "/dev/disk4" ] && DEVICE4="/dev/disk4"
[ -z "$DISK_DEV" ] && return 0
[ ! -b "$DISK_DEV" ] && error "Device $DISK_DEV cannot be found! Please add it to the 'devices' section of your compose file." && exit 55
if [ -n "$DEVICE" ]; then
addDevice "$DEVICE" "$DISK_TYPE" "3" "0xc" || exit $?
else
addDisk "$DISK1_FILE" "$DISK_TYPE" "disk" "$DISK_SIZE" "3" "0xc" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi
DISK_OPTS="$DISK_OPTS \
-device virtio-scsi-pci,id=hw-$DISK_ID,iothread=io2,bus=pcie.0,addr=$DISK_ADDRESS \
-drive file=$DISK_DEV,if=none,id=drive-$DISK_ID,format=raw,cache=$DISK_CACHE,aio=$DISK_IO,discard=$DISK_DISCARD,detect-zeroes=on \
-device scsi-hd,bus=hw-$DISK_ID.0,channel=0,scsi-id=0,lun=0,drive=drive-$DISK_ID,id=$DISK_ID,rotation_rate=$DISK_ROTATION,bootindex=$DISK_INDEX"
if [ -n "$DEVICE2" ]; then
addDevice "$DEVICE2" "$DISK_TYPE" "4" "0xd" || exit $?
else
addDisk "$DISK2_FILE" "$DISK_TYPE" "disk2" "$DISK2_SIZE" "4" "0xd" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi
return 0
}
if [ -n "$DEVICE3" ]; then
addDevice "$DEVICE3" "$DISK_TYPE" "5" "0xe" || exit $?
else
addDisk "$DISK3_FILE" "$DISK_TYPE" "disk3" "$DISK3_SIZE" "5" "0xe" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi
: ${DEVICE:=''} # Docker variable to passthrough a block device, like /dev/vdc1.
: ${DEVICE2:=''}
: ${DEVICE3:=''}
: ${DEVICE4:=''}
: ${DEVICE5:=''}
: ${DEVICE6:=''}
if [ -n "$DEVICE4" ]; then
addDevice "$DEVICE4" "$DISK_TYPE" "6" "0xf" || exit $?
else
addDisk "$DISK4_FILE" "$DISK_TYPE" "disk4" "$DISK4_SIZE" "6" "0xf" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi
addDevice "userdata7" "$DEVICE" "6" "0xf" || exit $?
addDevice "userdata8" "$DEVICE2" "7" "0x5" || exit $?
addDevice "userdata9" "$DEVICE3" "8" "0x6" || exit $?
addDevice "userdata4" "$DEVICE4" "9" "0x7" || exit $?
addDevice "userdata5" "$DEVICE5" "10" "0x8" || exit $?
addDevice "userdata6" "$DEVICE6" "11" "0x9" || exit $?
DISK_OPTS+=" -object iothread,id=io2"
html "Initialized disks successfully..."
return 0

View File

@ -3,29 +3,39 @@ set -Eeuo pipefail
# Docker environment variables
: ${GPU:='N'} # GPU passthrough
: ${DISPLAY:='none'} # Display type
: "${GPU:="N"}" # GPU passthrough
: "${VGA:="virtio"}" # VGA adaptor
: "${DISPLAY:="none"}" # Display type
: "${RENDERNODE:="/dev/dri/renderD128"}" # Render node
if [[ "$GPU" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then
CPU_VENDOR=$(lscpu | awk '/Vendor ID/{print $3}')
DISPLAY_OPTS="-display $DISPLAY -vga none"
if [[ "$GPU" != [Yy1]* ]] || [[ "$CPU_VENDOR" != "GenuineIntel" ]] || [[ "$ARCH" != "amd64" ]]; then
[[ "${DISPLAY,,}" == "none" ]] && VGA="none"
DISPLAY_OPTS="-display $DISPLAY -vga $VGA"
return 0
fi
DISPLAY_OPTS="-display egl-headless,rendernode=/dev/dri/renderD128 -vga virtio"
DISPLAY_OPTS="-display egl-headless,rendernode=$RENDERNODE"
DISPLAY_OPTS+=" -vga $VGA"
[ ! -d /dev/dri ] && mkdir -m 755 /dev/dri
if [ ! -c /dev/dri/card0 ]; then
if mknod /dev/dri/card0 c 226 0; then
chmod 666 /dev/dri/card0
# Extract the card number from the render node
CARD_NUMBER=$(echo "$RENDERNODE" | grep -oP '(?<=renderD)\d+')
CARD_DEVICE="/dev/dri/card$((CARD_NUMBER - 128))"
if [ ! -c "$CARD_DEVICE" ]; then
if mknod "$CARD_DEVICE" c 226 $((CARD_NUMBER - 128)); then
chmod 666 "$CARD_DEVICE"
fi
fi
if [ ! -c /dev/dri/renderD128 ]; then
if mknod /dev/dri/renderD128 c 226 128; then
chmod 666 /dev/dri/renderD128
if [ ! -c "$RENDERNODE" ]; then
if mknod "$RENDERNODE" c 226 "$CARD_NUMBER"; then
chmod 666 "$RENDERNODE"
fi
fi

View File

@ -1,11 +1,12 @@
#!/usr/bin/env bash
set -Eeuo pipefail
echo " Starting Virtual DSM for Docker v$(</run/version)..."
echo " For support visit https://github.com/vdsm/virtual-dsm/"
: "${APP:="Virtual DSM"}"
: "${SUPPORT:="https://github.com/vdsm/virtual-dsm"}"
cd /run
. utils.sh # Load functions
. reset.sh # Initialize system
. install.sh # Run installation
. disk.sh # Initialize disks
@ -18,15 +19,19 @@ cd /run
trap - ERR
version=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1 | awk '{ print $NF }')
info "Booting $APP using QEMU v$version..."
if [[ "$CONSOLE" == [Yy]* ]]; then
exec qemu-system-x86_64 ${ARGS:+ $ARGS}
fi
[[ "$DEBUG" == [Yy1]* ]] && info "$VERS" && set -x
msg=$(qemu-system-x86_64 ${ARGS:+ $ARGS})
{ qemu-system-x86_64 ${ARGS:+ $ARGS} >"$QEMU_OUT" 2>"$QEMU_LOG"; rc=$?; } || :
(( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15
{ set +x; } 2>/dev/null && terminal "$msg"
terminal
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
cat "$QEMU_TERM" 2>/dev/null & wait $! || true
cat "$QEMU_TERM" 2>/dev/null & wait $! || :
sleep 1 && finish 0
sleep 1 & wait $!
[ ! -f "$QEMU_END" ] && finish 0

View File

@ -1,23 +1,50 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: ${URL:=''} # URL of the PAT file to be downloaded.
: "${URL:=""}" # URL of the PAT file to be downloaded.
if [ -f "$STORAGE/dsm.ver" ]; then
BASE=$(cat "$STORAGE/dsm.ver")
BASE=$(<"$STORAGE/dsm.ver")
BASE="${BASE//[![:print:]]/}"
[ -z "$BASE" ] && BASE="DSM_VirtualDSM_69057"
else
# Fallback for old installs
BASE="DSM_VirtualDSM_42962"
fi
[ -n "$URL" ] && BASE=$(basename "$URL" .pat)
FN="boot.pat"
DIR=$(find / -maxdepth 1 -type d -iname "$FN" -print -quit)
[ ! -d "$DIR" ] && DIR=$(find "$STORAGE" -maxdepth 1 -type d -iname "$FN" -print -quit)
if [[ -f "$STORAGE/$BASE.boot.img" ]] && [[ -f "$STORAGE/$BASE.system.img" ]]; then
if [ -d "$DIR" ]; then
BASE="DSM_VirtualDSM" && URL="file://$DIR"
if [[ ! -s "$STORAGE/$BASE.boot.img" ]] || [[ ! -s "$STORAGE/$BASE.system.img" ]]; then
error "The bind $DIR maps to a file that does not exist!" && exit 65
fi
fi
FILE=$(find / -maxdepth 1 -type f -iname "$FN" -print -quit)
[ ! -s "$FILE" ] && FILE=$(find "$STORAGE" -maxdepth 1 -type f -iname "$FN" -print -quit)
[ -s "$FILE" ] && BASE="DSM_VirtualDSM" && URL="file://$FILE"
if [ -n "$URL" ] && [ ! -s "$FILE" ] && [ ! -d "$DIR" ]; then
BASE=$(basename "$URL" .pat)
if [ ! -s "$STORAGE/$BASE.system.img" ]; then
BASE=$(basename "${URL%%\?*}" .pat)
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
fi
if [[ "${URL,,}" != "http"* ]] && [[ "${URL,,}" != "file:"* ]] ; then
[ ! -s "$STORAGE/$BASE.pat" ] && error "Invalid URL: $URL" && exit 65
URL="file://$STORAGE/$BASE.pat"
fi
fi
if [[ -s "$STORAGE/$BASE.boot.img" ]] && [[ -s "$STORAGE/$BASE.system.img" ]]; then
return 0 # Previous installation found
fi
# Display wait message
/run/server.sh 5000 install &
html "Please wait while Virtual DSM is being installed..."
DL=""
DL_CHINA="https://cndl.synology.cn/download/DSM"
@ -32,9 +59,15 @@ if [ -z "$DL" ]; then
[[ "${COUNTRY^^}" == "CN" ]] && DL="$DL_CHINA" || DL="$DL_GLOBAL"
fi
[ -z "$URL" ] && URL="$DL/release/7.2.1/69057-1/DSM_VirtualDSM_69057.pat"
if [ -z "$URL" ]; then
URL="$DL/release/7.2.2/72806/DSM_VirtualDSM_72806.pat"
fi
BASE=$(basename "$URL" .pat)
if [ ! -s "$FILE" ]; then
BASE=$(basename "${URL%%\?*}" .pat)
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
fi
if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
rm -f "$STORAGE/$BASE.pat"
@ -44,8 +77,6 @@ rm -f "$STORAGE/$BASE.agent"
rm -f "$STORAGE/$BASE.boot.img"
rm -f "$STORAGE/$BASE.system.img"
[[ "$DEBUG" == [Yy1]* ]] && set -x
# Check filesystem
FS=$(stat -f -c %T "$STORAGE")
@ -57,15 +88,23 @@ if [[ "${FS,,}" == "fuse"* ]]; then
info "Warning: the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!"
fi
if [[ "${FS,,}" != "fat"* && "${FS,,}" != "vfat"* && "${FS,,}" != "exfat"* && "${FS,,}" != "ntfs"* && "${FS,,}" != "msdos"* ]]; then
if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then
info "Warning: the filesystem of $STORAGE is $FS, which does not support O_DIRECT mode, adjusting settings..."
fi
if [[ "${FS,,}" == "fat"* || "${FS,,}" == "vfat"* || "${FS,,}" == "msdos"* ]]; then
error "Unable to install on $FS filesystems, please use a different filesystem for /storage." && exit 61
fi
if [[ "${FS,,}" != "exfat"* && "${FS,,}" != "ntfs"* && "${FS,,}" != "unknown"* ]]; then
TMP="$STORAGE/tmp"
else
TMP="/tmp/dsm"
TMP_SPACE=2147483648
SPACE=$(df --output=avail -B 1 /tmp | tail -n 1)
SPACE_MB=$(( (SPACE + 1048575)/1048576 ))
SPACE_MB=$(formatBytes "$SPACE")
if (( TMP_SPACE > SPACE )); then
error "Not enough free space inside the container, have $SPACE_MB MB available but need at least 2 GB." && exit 93
error "Not enough free space inside the container, have $SPACE_MB available but need at least 2 GB." && exit 93
fi
fi
@ -74,13 +113,13 @@ rm -rf "$TMP" && mkdir -p "$TMP"
# Check free diskspace
ROOT_SPACE=536870912
SPACE=$(df --output=avail -B 1 / | tail -n 1)
SPACE_MB=$(( (SPACE + 1048575)/1048576 ))
(( ROOT_SPACE > SPACE )) && error "Not enough free space inside the container, have $SPACE_MB MB available but need at least 500 MB." && exit 96
SPACE_MB=$(formatBytes "$SPACE" "down")
(( ROOT_SPACE > SPACE )) && error "Not enough free space inside the container, have $SPACE_MB available but need at least 500 MB." && exit 96
MIN_SPACE=8589934592
MIN_SPACE=15032385536
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
(( MIN_SPACE > SPACE )) && error "Not enough free space for installation in $STORAGE, have $SPACE_GB GB available but need at least 8 GB." && exit 94
SPACE_GB=$(formatBytes "$SPACE")
(( MIN_SPACE > SPACE )) && error "Not enough free space for installation in $STORAGE, have $SPACE_GB available but need at least 14 GB." && exit 94
# Check if output is to interactive TTY
if [ -t 1 ]; then
@ -89,99 +128,50 @@ else
PROGRESS="--progress=dot:giga"
fi
# Download the required files from the Synology website
ROOT="Y"
RDC="$STORAGE/dsm.rd"
if [ ! -f "$RDC" ]; then
info "Install: Downloading installer..."
RD="$TMP/rd.gz"
POS="65627648-71021835"
VERIFY="b4215a4b213ff5154db0488f92c87864"
LOC="$DL/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
{ curl -r "$POS" -sfk -S -o "$RD" "$LOC"; rc=$?; } || :
(( rc != 0 )) && error "Failed to download $LOC, reason: $rc" && exit 60
SUM=$(md5sum "$RD" | cut -f 1 -d " ")
if [ "$SUM" != "$VERIFY" ]; then
PAT="/install.pat"
rm "$RD"
rm -f "$PAT"
{ wget "$LOC" -O "$PAT" -q --no-check-certificate --show-progress "$PROGRESS"; rc=$?; } || :
(( rc != 0 )) && error "Failed to download $LOC, reason: $rc" && exit 60
tar --extract --file="$PAT" --directory="$(dirname "$RD")"/. "$(basename "$RD")"
rm "$PAT"
fi
cp "$RD" "$RDC"
if [[ "$URL" == "file://"* ]]; then
MSG="Copying DSM"
ERR="Failed to copy ${URL:7}"
info "Install: Copying installation image..."
else
MSG="Downloading DSM"
ERR="Failed to download $URL"
info "Install: Downloading $BASE.pat..."
fi
if [ -f "$RDC" ]; then
{ xz -dc <"$RDC" >"$TMP/rd" 2>/dev/null; rc=$?; } || :
(( rc != 1 )) && error "Failed to unxz $RDC, reason $rc" && exit 91
{ (cd "$TMP" && cpio -idm <"$TMP/rd" 2>/dev/null); rc=$?; } || :
if (( rc != 0 )); then
ROOT="N"
{ (cd "$TMP" && fakeroot cpio -idmu <"$TMP/rd" 2>/dev/null); rc=$?; } || :
(( rc != 0 )) && error "Failed to extract $RDC, reason $rc" && exit 92
fi
mkdir -p /run/extract
for file in $TMP/usr/lib/libcurl.so.4 \
$TMP/usr/lib/libmbedcrypto.so.5 \
$TMP/usr/lib/libmbedtls.so.13 \
$TMP/usr/lib/libmbedx509.so.1 \
$TMP/usr/lib/libmsgpackc.so.2 \
$TMP/usr/lib/libsodium.so \
$TMP/usr/lib/libsynocodesign-ng-virtual-junior-wins.so.7 \
$TMP/usr/syno/bin/scemd; do
cp "$file" /run/extract/
done
if [ "$ARCH" != "amd64" ]; then
mkdir -p /lib64/
cp "$TMP/usr/lib/libc.so.6" /lib64/
cp "$TMP/usr/lib/libpthread.so.0" /lib64/
cp "$TMP/usr/lib/ld-linux-x86-64.so.2" /lib64/
fi
mv /run/extract/scemd /run/extract/syno_extract_system_patch
chmod +x /run/extract/syno_extract_system_patch
fi
rm -rf "$TMP" && mkdir -p "$TMP"
info "Install: Downloading $(basename "$URL")..."
html "$MSG..."
PAT="/$BASE.pat"
rm -f "$PAT"
if [[ "$URL" == "file://"* ]]; then
if [ ! -f "${URL:7}" ]; then
error "File '${URL:7}' does not exist!" && exit 65
fi
cp "${URL:7}" "$PAT"
else
{ wget "$URL" -O "$PAT" -q --no-check-certificate --show-progress "$PROGRESS"; rc=$?; } || :
(( rc != 0 )) && error "Failed to download $URL, reason: $rc" && exit 69
SIZE=0
[[ "${URL,,}" == *"_72806.pat" ]] && SIZE=361010261
[[ "${URL,,}" == *"_69057.pat" ]] && SIZE=363837333
[[ "${URL,,}" == *"_42218.pat" ]] && SIZE=379637760
/run/progress.sh "$PAT" "$SIZE" "$MSG ([P])..." &
{ wget "$URL" -O "$PAT" -q --no-check-certificate --timeout=10 --no-http-keep-alive --show-progress "$PROGRESS"; rc=$?; } || :
fKill "progress.sh"
(( rc == 3 )) && error "$ERR , cannot write file (disk full?)" && exit 69
(( rc == 4 )) && error "$ERR , network failure!" && exit 69
(( rc == 8 )) && error "$ERR , server issued an error response!" && exit 69
(( rc != 0 )) && error "$ERR , reason: $rc" && exit 69
fi
[ ! -f "$PAT" ] && error "Failed to download $URL" && exit 69
[ ! -s "$PAT" ] && error "$ERR" && exit 69
SIZE=$(stat -c%s "$PAT")
@ -189,7 +179,8 @@ if ((SIZE<250000000)); then
error "The specified PAT file is probably an update pack as it's too small." && exit 62
fi
info "Install: Extracting downloaded image..."
MSG="Extracting installation image..."
info "Install: $MSG" && html "$MSG"
if { tar tf "$PAT"; } >/dev/null 2>&1; then
@ -197,24 +188,20 @@ if { tar tf "$PAT"; } >/dev/null 2>&1; then
else
export LD_LIBRARY_PATH="/run/extract"
{ (cd "$TMP" && python3 /run/extract.py -i "$PAT" -d 2>/run/extract.log); rc=$?; } || :
if [ "$ARCH" == "amd64" ]; then
{ /run/extract/syno_extract_system_patch "$PAT" "$TMP/."; rc=$?; } || :
else
{ qemu-x86_64 /run/extract/syno_extract_system_patch "$PAT" "$TMP/."; rc=$?; } || :
if (( rc != 0 )); then
cat /run/extract.log
error "Failed to extract PAT file, reason $rc" && exit 63
fi
export LD_LIBRARY_PATH=""
(( rc != 0 )) && error "Failed to extract PAT file, reason $rc" && exit 63
fi
info "Install: Preparing system partition..."
MSG="Preparing system partition..."
info "Install: $MSG" && html "$MSG"
BOOT=$(find "$TMP" -name "*.bin.zip")
[ ! -f "$BOOT" ] && error "The PAT file contains no boot image." && exit 67
[ ! -s "$BOOT" ] && error "The PAT file contains no boot image." && exit 67
BOOT=$(echo "$BOOT" | head -c -5)
unzip -q -o "$BOOT".zip -d "$TMP"
@ -223,30 +210,32 @@ SYSTEM="$STORAGE/$BASE.system.img"
rm -f "$SYSTEM"
# Check free diskspace
SYSTEM_SIZE=4954537983
SYSTEM_SIZE=10738466816
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
SPACE_MB=$(( (SPACE + 1048575)/1048576 ))
SPACE_MB=$(formatBytes "$SPACE")
if (( SYSTEM_SIZE > SPACE )); then
error "Not enough free space in $STORAGE to create a 5 GB system disk, have only $SPACE_MB MB available." && exit 97
error "Not enough free space in $STORAGE to create a 10 GB system disk, have only $SPACE_MB available." && exit 97
fi
if ! touch "$SYSTEM"; then
error "Could not create file $SYSTEM for the system disk." && exit 98
fi
if [[ "${FS,,}" == "xfs" || "${FS,,}" == "zfs" || "${FS,,}" == "btrfs" || "${FS,,}" == "bcachefs" ]]; then
if [[ "${FS,,}" == "btrfs" ]]; then
{ chattr +C "$SYSTEM"; } || :
FA=$(lsattr "$SYSTEM")
if [[ "$FA" != *"C"* ]]; then
error "Failed to disable COW for system image $SYSTEM on ${FS^^} filesystem (returned $FA)"
error "Failed to disable COW for system image $SYSTEM on ${FS^^} filesystem."
fi
fi
if ! fallocate -l "$SYSTEM_SIZE" "$SYSTEM"; then
if ! truncate -s "$SYSTEM_SIZE" "$SYSTEM"; then
rm -f "$SYSTEM"
error "Could not allocate file $SYSTEM for the system disk." && exit 98
if ! fallocate -l "$SYSTEM_SIZE" "$SYSTEM" &>/dev/null; then
if ! fallocate -l -x "$SYSTEM_SIZE" "$SYSTEM"; then
if ! truncate -s "$SYSTEM_SIZE" "$SYSTEM"; then
rm -f "$SYSTEM"
error "Could not allocate file $SYSTEM for the system disk." && exit 98
fi
fi
fi
@ -258,8 +247,8 @@ PART="$TMP/partition.fdisk"
echo "unit: sectors"
echo "sector-size: 512"
echo ""
echo "${SYSTEM}1 : start= 2048, size= 4980480, type=83"
echo "${SYSTEM}2 : start= 4982528, size= 4194304, type=82"
echo "${SYSTEM}1 : start= 2048, size= 16777216, type=83"
echo "${SYSTEM}2 : start= 16779264, size= 4194304, type=82"
} > "$PART"
sfdisk -q "$SYSTEM" < "$PART"
@ -267,55 +256,38 @@ sfdisk -q "$SYSTEM" < "$PART"
MOUNT="$TMP/system"
rm -rf "$MOUNT" && mkdir -p "$MOUNT"
info "Install: Extracting system partition..."
MSG="Extracting system partition..."
info "Install: $MSG" && html "$MSG"
HDA="$TMP/hda1"
IDB="$TMP/indexdb"
PKG="$TMP/packages"
HDP="$TMP/synohdpack_img"
[ ! -f "$HDA.tgz" ] && error "The PAT file contains no OS image." && exit 64
[ ! -s "$HDA.tgz" ] && error "The PAT file contains no OS image." && exit 64
mv "$HDA.tgz" "$HDA.txz"
if [[ "$ROOT" != [Nn]* ]]; then
tar xpfJ "$HDA.txz" --absolute-names -C "$MOUNT/"
fi
[ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/"
rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"*
[ -f "$HDP.txz" ] && tar xpfJ "$HDP.txz" --absolute-names -C "$MOUNT/"
if [ -f "$IDB.txz" ]; then
INDEX_DB="$MOUNT/usr/syno/synoman/indexdb/"
if [ -s "$IDB.txz" ]; then
INDEX_DB="$MOUNT/usr/syno/synoman/indexdb"
mkdir -p "$INDEX_DB"
tar xpfJ "$IDB.txz" --absolute-names -C "$INDEX_DB"
fi
LABEL="1.44.1-42218"
OFFSET="1048576" # 2048 * 512
NUMBLOCKS="622560" # (4980480 * 512) / 4096
OFFSET="1048576" # 2048 * 512
NUMBLOCKS="2097152" # (16777216 * 512) / 4096
MSG="Installing system partition..."
if [[ "$ROOT" != [Nn]* ]]; then
info "Install: Installing system partition..."
mke2fs -q -t ext4 -b 4096 -d "$MOUNT/" -L "$LABEL" -F -E "offset=$OFFSET" "$SYSTEM" "$NUMBLOCKS"
else
fakeroot -- bash -c "set -Eeu;\
tar xpfJ $HDA.txz --absolute-names --skip-old-files -C $MOUNT/;\
printf '%b%s%b' '\E[1;34m \E[1;36m' 'Install: Installing system partition...' '\E[0m\n';\
mke2fs -q -t ext4 -b 4096 -d $MOUNT/ -L $LABEL -F -E offset=$OFFSET $SYSTEM $NUMBLOCKS"
fi
fakeroot -- bash -c "set -Eeu;\
[ -s $HDP.txz ] && tar xpfJ $HDP.txz --absolute-names -C $MOUNT/;\
[ -s $IDB.txz ] && tar xpfJ $IDB.txz --absolute-names -C $INDEX_DB/;\
tar xpfJ $HDA.txz --absolute-names --skip-old-files -C $MOUNT/;\
printf '%b%s%b' '\E[1;34m \E[1;36m' 'Install: $MSG' '\E[0m\n';\
mke2fs -q -t ext4 -b 4096 -d $MOUNT/ -L $LABEL -F -E offset=$OFFSET $SYSTEM $NUMBLOCKS"
rm -rf "$MOUNT"
echo "$BASE" > "$STORAGE/dsm.ver"
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
@ -325,10 +297,9 @@ else
fi
mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
rm -rf "$TMP"
{ set +x; } 2>/dev/null
[[ "$DEBUG" == [Yy1]* ]] && echo
html "Booting DSM instance..."
sleep 1.2
return 0

View File

@ -3,17 +3,23 @@ set -Eeuo pipefail
# Docker environment variables
: ${DHCP:='N'}
: ${MAC:='02:11:32:AA:BB:CC'}
: "${MAC:=""}"
: "${MTU:=""}"
: "${DHCP:="N"}"
: "${NETWORK:="Y"}"
: "${USER_PORTS:=""}"
: "${HOST_PORTS:=""}"
: "${ADAPTER:="virtio-net-pci"}"
: ${VM_NET_DEV:=''}
: ${VM_NET_TAP:='dsm'}
: ${VM_NET_MAC:="$MAC"}
: ${VM_NET_HOST:='VirtualDSM'}
: "${VM_NET_DEV:=""}"
: "${VM_NET_TAP:="dsm"}"
: "${VM_NET_MAC:="$MAC"}"
: "${VM_NET_IP:="20.20.20.21"}"
: "${VM_NET_HOST:="VirtualDSM"}"
: ${DNSMASQ_OPTS:=''}
: ${DNSMASQ:='/usr/sbin/dnsmasq'}
: ${DNSMASQ_CONF_DIR:='/etc/dnsmasq.d'}
: "${DNSMASQ_OPTS:=""}"
: "${DNSMASQ:="/usr/sbin/dnsmasq"}"
: "${DNSMASQ_CONF_DIR:="/etc/dnsmasq.d"}"
ADD_ERR="Please add the following setting to your container:"
@ -23,17 +29,44 @@ ADD_ERR="Please add the following setting to your container:"
configureDHCP() {
# Create the necessary file structure for /dev/vhost-net
if [ ! -c /dev/vhost-net ]; then
if mknod /dev/vhost-net c 10 238; then
chmod 660 /dev/vhost-net
fi
fi
# Create a macvtap network for the VM guest
{ msg=$(ip link add link "$VM_NET_DEV" name "$VM_NET_TAP" address "$VM_NET_MAC" type macvtap mode bridge 2>&1); rc=$?; } || :
{ ip link add link "$VM_NET_DEV" name "$VM_NET_TAP" address "$VM_NET_MAC" type macvtap mode bridge ; rc=$?; } || :
case "$msg" in
"RTNETLINK answers: File exists"* )
while ! ip link add link "$VM_NET_DEV" name "$VM_NET_TAP" address "$VM_NET_MAC" type macvtap mode bridge; do
info "Waiting for macvtap interface to become available.."
sleep 5
done ;;
"RTNETLINK answers: Invalid argument"* )
error "Cannot create macvtap interface. Please make sure that the network type of the container is 'macvlan' and not 'ipvlan'."
return 1 ;;
"RTNETLINK answers: Operation not permitted"* )
error "No permission to create macvtap interface. Please make sure that your host kernel supports it and that the NET_ADMIN capability is set."
return 1 ;;
*)
[ -n "$msg" ] && echo "$msg" >&2
if (( rc != 0 )); then
error "Cannot create macvtap interface."
return 1
fi ;;
esac
if (( rc != 0 )); then
error "Cannot create macvtap interface. Please make sure the network type is 'macvlan' and not 'ipvlan',"
error "and that the NET_ADMIN capability has been added to the container: --cap-add NET_ADMIN" && exit 16
if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
warn "Failed to set MTU size.."
fi
fi
while ! ip link set "$VM_NET_TAP" up; do
info "Waiting for address to become available..."
info "Waiting for MAC address $VM_NET_MAC to become available..."
sleep 2
done
@ -43,25 +76,25 @@ configureDHCP() {
# Create dev file (there is no udev in container: need to be done manually)
IFS=: read -r MAJOR MINOR < <(cat /sys/devices/virtual/net/"$VM_NET_TAP"/tap*/dev)
(( MAJOR < 1)) && error "Cannot find: sys/devices/virtual/net/$VM_NET_TAP" && exit 18
(( MAJOR < 1)) && error "Cannot find: sys/devices/virtual/net/$VM_NET_TAP" && return 1
[[ ! -e "$TAP_PATH" ]] && [[ -e "/dev0/${TAP_PATH##*/}" ]] && ln -s "/dev0/${TAP_PATH##*/}" "$TAP_PATH"
if [[ ! -e "$TAP_PATH" ]]; then
{ mknod "$TAP_PATH" c "$MAJOR" "$MINOR" ; rc=$?; } || :
(( rc != 0 )) && error "Cannot mknod: $TAP_PATH ($rc)" && exit 20
(( rc != 0 )) && error "Cannot mknod: $TAP_PATH ($rc)" && return 1
fi
{ exec 30>>"$TAP_PATH"; rc=$?; } 2>/dev/null || :
if (( rc != 0 )); then
error "Cannot create TAP interface ($rc). $ADD_ERR --device-cgroup-rule='c *:* rwm'" && exit 21
error "Cannot create TAP interface ($rc). $ADD_ERR --device-cgroup-rule='c *:* rwm'" && return 1
fi
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
if (( rc != 0 )); then
error "VHOST can not be found ($rc). $ADD_ERR --device=/dev/vhost-net" && exit 22
error "VHOST can not be found ($rc). $ADD_ERR --device=/dev/vhost-net" && return 1
fi
NET_OPTS="-netdev tap,id=hostnet0,vhost=on,vhostfd=40,fd=30"
@ -71,29 +104,99 @@ configureDHCP() {
configureDNS() {
# dnsmasq configuration:
DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-range=$VM_NET_IP,$VM_NET_IP --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite --dhcp-option=option:netmask,255.255.255.0"
# Create lease file for faster resolve
echo "0 $VM_NET_MAC $VM_NET_IP $VM_NET_HOST 01:$VM_NET_MAC" > /var/lib/misc/dnsmasq.leases
chmod 644 /var/lib/misc/dnsmasq.leases
# dnsmasq configuration:
DNSMASQ_OPTS+=" --dhcp-authoritative"
# Set DHCP range and host
DNSMASQ_OPTS+=" --dhcp-range=$VM_NET_IP,$VM_NET_IP"
DNSMASQ_OPTS+=" --dhcp-host=$VM_NET_MAC,,$VM_NET_IP,$VM_NET_HOST,infinite"
# Set DNS server and gateway
DNSMASQ_OPTS="$DNSMASQ_OPTS --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1 --dhcp-option=option:router,${VM_NET_IP%.*}.1"
DNSMASQ_OPTS+=" --dhcp-option=option:netmask,255.255.255.0"
DNSMASQ_OPTS+=" --dhcp-option=option:router,${VM_NET_IP%.*}.1"
DNSMASQ_OPTS+=" --dhcp-option=option:dns-server,${VM_NET_IP%.*}.1"
# Add DNS entry for container
DNSMASQ_OPTS+=" --address=/host.lan/${VM_NET_IP%.*}.1"
DNSMASQ_OPTS=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
[[ "$DEBUG" == [Yy1]* ]] && set -x
$DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}
{ set +x; } 2>/dev/null
[[ "$DEBUG" == [Yy1]* ]] && echo
if [[ "${DEBUG_DNS:-}" == [Yy1]* ]]; then
DNSMASQ_OPTS+=" -d"
$DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS} &
return 0
fi
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
error "Failed to start dnsmasq, reason: $?" && return 1
fi
return 0
}
getUserPorts() {
local args=""
local list=$1
local ssh="22"
local dsm="5000"
[ -z "$list" ] && list="$ssh,$dsm" || list+=",$ssh,$dsm"
list="${list//,/ }"
list="${list## }"
list="${list%% }"
for port in $list; do
args+="hostfwd=tcp::$port-$VM_NET_IP:$port,"
done
echo "${args%?}"
return 0
}
getHostPorts() {
local list=$1
[ -z "$list" ] && echo "" && return 0
if [[ "$list" != *","* ]]; then
echo " ! --dport $list"
else
echo " -m multiport ! --dports $list"
fi
return 0
}
configureUser() {
if [ -z "$IP6" ]; then
NET_OPTS="-netdev user,id=hostnet0,host=${VM_NET_IP%.*}.1,net=${VM_NET_IP%.*}.0/24,dhcpstart=$VM_NET_IP,hostname=$VM_NET_HOST"
else
NET_OPTS="-netdev user,id=hostnet0,ipv4=on,host=${VM_NET_IP%.*}.1,net=${VM_NET_IP%.*}.0/24,dhcpstart=$VM_NET_IP,ipv6=on,hostname=$VM_NET_HOST"
fi
local forward
forward=$(getUserPorts "$USER_PORTS")
[ -n "$forward" ] && NET_OPTS+=",$forward"
return 0
}
configureNAT() {
local tuntap="TUN device is missing. $ADD_ERR --device /dev/net/tun"
local tables="The 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
# Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then
[[ "$PODMAN" == [Yy1]* ]] && return 1
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
if mknod /dev/net/tun c 10 200; then
chmod 666 /dev/net/tun
@ -101,93 +204,160 @@ configureNAT() {
fi
if [ ! -c /dev/net/tun ]; then
error "TUN device missing. $ADD_ERR --cap-add NET_ADMIN" && exit 25
error "$tuntap" && return 1
fi
# Check port forwarding flag
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 ; rc=$?; } || :
if (( rc != 0 )); then
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1" && exit 24
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null 2>&1; rc=$?; } || :
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
[[ "$PODMAN" == [Yy1]* ]] && return 1
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1"
return 1
fi
fi
# Create a bridge with a static IP for the VM guest
VM_NET_IP='20.20.20.21'
[[ "$DEBUG" == [Yy1]* ]] && set -x
{ ip link add dev dockerbridge type bridge ; rc=$?; } || :
if (( rc != 0 )); then
error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && exit 23
error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && return 1
fi
ip address add ${VM_NET_IP%.*}.1/24 broadcast ${VM_NET_IP%.*}.255 dev dockerbridge
if ! ip address add "${VM_NET_IP%.*}.1/24" broadcast "${VM_NET_IP%.*}.255" dev dockerbridge; then
error "Failed to add IP address pool!" && return 1
fi
while ! ip link set dockerbridge up; do
info "Waiting for address to become available..."
info "Waiting for IP address to become available..."
sleep 2
done
# QEMU Works with taps, set tap to the bridge created
ip tuntap add dev "$VM_NET_TAP" mode tap
if ! ip tuntap add dev "$VM_NET_TAP" mode tap; then
error "$tuntap" && return 1
fi
if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
if ! ip link set dev "$VM_NET_TAP" mtu "$MTU"; then
warn "Failed to set MTU size.."
fi
fi
GATEWAY_MAC=$(echo "$VM_NET_MAC" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:\1:\2:\3:\4:\5/')
if ! ip link set dev "$VM_NET_TAP" address "$GATEWAY_MAC"; then
warn "Failed to set gateway MAC address.."
fi
while ! ip link set "$VM_NET_TAP" up promisc on; do
info "Waiting for tap to become available..."
info "Waiting for TAP to become available..."
sleep 2
done
ip link set dev "$VM_NET_TAP" master dockerbridge
if ! ip link set dev "$VM_NET_TAP" master dockerbridge; then
error "Failed to set IP link!" && return 1
fi
# Add internet connection to the VM
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE
iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp -j DNAT --to "$VM_NET_IP"
iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"
exclude=$(getHostPorts "$HOST_PORTS")
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
error "$tables" && return 1
fi
# shellcheck disable=SC2086
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -j DNAT --to "$VM_NET_IP"; then
error "Failed to configure IP tables!" && return 1
fi
if ! iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p udp -j DNAT --to "$VM_NET_IP"; then
error "Failed to configure IP tables!" && return 1
fi
if (( KERNEL > 4 )); then
# Hack for guest VMs complaining about "bad udp checksums in 5 packets"
iptables -A POSTROUTING -t mangle -p udp --dport bootpc -j CHECKSUM --checksum-fill || true
iptables -A POSTROUTING -t mangle -p udp --dport bootpc -j CHECKSUM --checksum-fill > /dev/null 2>&1 || true
fi
{ set +x; } 2>/dev/null
[[ "$DEBUG" == [Yy1]* ]] && echo
NET_OPTS="-netdev tap,id=hostnet0,ifname=$VM_NET_TAP"
NET_OPTS="-netdev tap,ifname=$VM_NET_TAP,script=no,downscript=no,id=hostnet0"
if [ -c /dev/vhost-net ]; then
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
(( rc == 0 )) && NET_OPTS+=",vhost=on,vhostfd=40"
fi
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
(( rc == 0 )) && NET_OPTS="$NET_OPTS,vhost=on,vhostfd=40"
NET_OPTS+=",script=no,downscript=no"
configureDNS
configureDNS || return 1
return 0
}
closeBridge() {
local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
[[ "${NETWORK,,}" == "user"* ]] && return 0
ip link set "$VM_NET_TAP" down promisc off &> null || true
ip link delete "$VM_NET_TAP" &> null || true
ip link set dockerbridge down &> null || true
ip link delete dockerbridge &> null || true
return 0
}
closeNetwork() {
if [[ "$DHCP" == [Yy1]* ]]; then
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
fi
[[ "$NETWORK" == [Nn]* ]] && return 0
exec 30<&- || true
exec 40<&- || true
if [[ "$DHCP" != [Yy1]* ]]; then
closeBridge
return 0
fi
ip link set "$VM_NET_TAP" down || true
ip link delete "$VM_NET_TAP" || true
return 0
}
checkOS() {
local kernel
local os=""
local if="macvlan"
kernel=$(uname -a)
[[ "${kernel,,}" == *"darwin"* ]] && os="Docker Desktop for macOS"
[[ "${kernel,,}" == *"microsoft"* ]] && os="Docker Desktop for Windows"
if [[ "$DHCP" == [Yy1]* ]]; then
if="macvtap"
[[ "${kernel,,}" == *"synology"* ]] && os="Synology Container Manager"
fi
fKill "server.sh"
ip link set "$VM_NET_TAP" down || true
ip link delete "$VM_NET_TAP" || true
else
fKill "dnsmasq"
ip link set "$VM_NET_TAP" down promisc off || true
ip link delete "$VM_NET_TAP" || true
ip link set dockerbridge down || true
ip link delete dockerbridge || true
if [ -n "$os" ]; then
warn "you are using $os which does not support $if, please revert to bridge networking!"
fi
return 0
@ -196,29 +366,76 @@ closeNetwork() {
getInfo() {
if [ -z "$VM_NET_DEV" ]; then
# Give Kubernetes priority over the default interface
[ -d "/sys/class/net/net0" ] && VM_NET_DEV="net0"
[ -d "/sys/class/net/net1" ] && VM_NET_DEV="net1"
[ -d "/sys/class/net/net2" ] && VM_NET_DEV="net2"
[ -d "/sys/class/net/net3" ] && VM_NET_DEV="net3"
# Automaticly detect the default network interface
VM_NET_DEV=$(awk '$2 == 00000000 { print $1 }' /proc/net/route)
[ -z "$VM_NET_DEV" ] && VM_NET_DEV=$(awk '$2 == 00000000 { print $1 }' /proc/net/route)
[ -z "$VM_NET_DEV" ] && VM_NET_DEV="eth0"
fi
if [ ! -d "/sys/class/net/$VM_NET_DEV" ]; then
error "Network interface '$VM_NET_DEV' does not exist inside the container!"
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 27
error "$ADD_ERR -e \"VM_NET_DEV=NAME\" to specify another interface name." && exit 26
fi
BASE_IP="${VM_NET_IP%.*}."
if [ "${VM_NET_IP/$BASE_IP/}" -lt "3" ]; then
error "Invalid VM_NET_IP, must end in a higher number than .3" && exit 27
fi
if [ -z "$MTU" ]; then
MTU=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
fi
if [ "$MTU" -gt "1500" ]; then
info "MTU size is too large: $MTU, ignoring..." && MTU="0"
fi
if [[ "${ADAPTER,,}" != "virtio-net-pci" ]]; then
if [[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]]; then
warn "MTU size is $MTU, but cannot be set for $ADAPTER adapters!" && MTU="0"
fi
fi
if [ -z "$VM_NET_MAC" ]; then
local file="$STORAGE/dsm.mac"
[ -s "$file" ] && VM_NET_MAC=$(<"$file")
VM_NET_MAC="${VM_NET_MAC//[![:print:]]/}"
if [ -z "$VM_NET_MAC" ]; then
# Generate MAC address based on Docker container ID in hostname
VM_NET_MAC=$(echo "$HOST" | md5sum | sed 's/^\(..\)\(..\)\(..\)\(..\)\(..\).*$/02:11:32:\3:\4:\5/')
echo "${VM_NET_MAC^^}" > "$file"
fi
fi
VM_NET_MAC="${VM_NET_MAC^^}"
VM_NET_MAC="${VM_NET_MAC//-/:}"
if [[ ${#VM_NET_MAC} == 12 ]]; then
m="$VM_NET_MAC"
VM_NET_MAC="${m:0:2}:${m:2:2}:${m:4:2}:${m:6:2}:${m:8:2}:${m:10:2}"
fi
if [[ ${#VM_NET_MAC} != 17 ]]; then
error "Invalid mac address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
error "Invalid MAC address: '$VM_NET_MAC', should be 12 or 17 digits long!" && exit 28
fi
GATEWAY=$(ip r | grep default | awk '{print $3}')
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
echo "$IP" > /run/qemu.ip
GATEWAY=$(ip route list dev "$VM_NET_DEV" | awk ' /^default/ {print $3}' | head -n 1)
IP=$(ip address show dev "$VM_NET_DEV" | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/ | head -n 1)
IP6=""
# shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
IP6=$(ip -6 addr show dev "$VM_NET_DEV" scope global up)
[ -n "$IP6" ] && IP6=$(echo "$IP6" | sed -e's/^.*inet6 \([^ ]*\)\/.*$/\1/;t;d' | head -n 1)
fi
[ -f "/run/.containerenv" ] && PODMAN="Y" || PODMAN="N"
echo "$IP" > /run/shm/qemu.ip
return 0
}
@ -227,41 +444,89 @@ getInfo() {
# Configure Network
# ######################################
fKill "server.sh"
if [ ! -c /dev/vhost-net ]; then
if mknod /dev/vhost-net c 10 238; then
chmod 660 /dev/vhost-net
fi
if [[ "$NETWORK" == [Nn]* ]]; then
NET_OPTS=""
return 0
fi
getInfo
html "Initializing network..."
if [[ "$DEBUG" == [Yy1]* ]]; then
info "Container IP is $IP with gateway $GATEWAY on interface $VM_NET_DEV" && echo
mtu=$(cat "/sys/class/net/$VM_NET_DEV/mtu")
line="Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC MTU: $mtu"
[[ "$MTU" != "0" ]] && [[ "$MTU" != "$mtu" ]] && line+=" ($MTU)"
info "$line"
if [ -f /etc/resolv.conf ]; then
nameservers=$(grep '^nameserver*' /etc/resolv.conf | head -c -1 | sed 's/nameserver //g;' | sed -z 's/\n/, /g')
[ -n "$nameservers" ] && info "Nameservers: $nameservers"
fi
echo
fi
if [[ "$IP" == "172.17."* ]]; then
warn "your container IP starts with 172.17.* which will cause conflicts when you install the Container Manager package inside DSM!"
fi
if [[ -d "/sys/class/net/$VM_NET_TAP" ]]; then
info "Lingering interface will be removed..."
ip link delete "$VM_NET_TAP" || true
fi
if [[ "$DHCP" == [Yy1]* ]]; then
if [[ "$GATEWAY" == "172."* ]]; then
if [[ "$DEBUG" != [Yy1]* ]]; then
error "You can only enable DHCP while the container is on a macvlan network!" && exit 26
fi
checkOS
if [[ "$IP" == "172."* ]]; then
warn "container IP starts with 172.* which is often a sign that you are not on a macvlan network (required for DHCP)!"
fi
# Configuration for DHCP IP
configureDHCP
# Configure for macvtap interface
configureDHCP || exit 20
# Display IP on port 80 and 5000
/run/server.sh 5000 /run/ip.sh &
MSG="Booting DSM instance..."
html "$MSG"
else
# Configuration for static IP
configureNAT
if [[ "$IP" != "172."* ]] && [[ "$IP" != "10.8"* ]] && [[ "$IP" != "10.9"* ]]; then
checkOS
fi
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
if [[ "${NETWORK,,}" != "user"* ]]; then
# Configure for tap interface
if ! configureNAT; then
closeBridge
NETWORK="user"
msg="falling back to user-mode networking!"
if [[ "$PODMAN" != [Yy1]* ]]; then
msg="an error occured, $msg"
else
msg="podman detected, $msg"
fi
warn "$msg"
[ -z "$USER_PORTS" ] && info "Notice: port mapping will not work without \"USER_PORTS\" now."
fi
fi
if [[ "${NETWORK,,}" == "user"* ]]; then
# Configure for user-mode networking (slirp)
configureUser || exit 24
fi
fi
NET_OPTS="$NET_OPTS -device virtio-net-pci,romfile=,netdev=hostnet0,mac=$VM_NET_MAC,id=net0"
NET_OPTS+=" -device $ADAPTER,id=net0,netdev=hostnet0,romfile=,mac=$VM_NET_MAC"
[[ "$MTU" != "0" ]] && [[ "$MTU" != "1500" ]] && NET_OPTS+=",host_mtu=$MTU"
return 0

View File

@ -10,18 +10,17 @@ API_HOST="127.0.0.1:2210"
QEMU_TERM=""
QEMU_PORT=7100
QEMU_TIMEOUT=50
QEMU_PID="/run/qemu.pid"
QEMU_LOG="/run/qemu.log"
QEMU_END="/run/qemu.end"
QEMU_DIR="/run/shm"
QEMU_PID="$QEMU_DIR/qemu.pid"
QEMU_LOG="$QEMU_DIR/qemu.log"
QEMU_OUT="$QEMU_DIR/qemu.out"
QEMU_END="$QEMU_DIR/qemu.end"
if [[ "$KVM" == [Nn]* ]]; then
API_TIMEOUT=$(( API_TIMEOUT*2 ))
QEMU_TIMEOUT=$(( QEMU_TIMEOUT*2 ))
fi
rm -f "$QEMU_PID"
rm -f "$QEMU_LOG"
rm -f "$QEMU_END"
touch "$QEMU_LOG"
_trap() {
@ -36,16 +35,18 @@ finish() {
local pid
local reason=$1
if [ -f "$QEMU_PID" ]; then
touch "$QEMU_END"
pid="$(cat "$QEMU_PID")"
if [ -s "$QEMU_PID" ]; then
pid=$(<"$QEMU_PID")
echo && error "Forcefully terminating QEMU process, reason: $reason..."
{ kill -15 "$pid" || true; } 2>/dev/null
while isAlive "$pid"; do
sleep 1
# Workaround for zombie pid
[ ! -f "$QEMU_PID" ] && break
[ ! -s "$QEMU_PID" ] && break
done
fi
@ -62,15 +63,25 @@ finish() {
terminal() {
local msg=$1
local dev=""
if [[ "${msg,,}" != "char"* || "$msg" != *"serial0)" ]]; then
echo "$msg"
if [ -s "$QEMU_OUT" ]; then
local msg
msg=$(<"$QEMU_OUT")
if [ -n "$msg" ]; then
if [[ "${msg,,}" != "char"* || "$msg" != *"serial0)" ]]; then
echo "$msg"
fi
dev="${msg#*/dev/p}"
dev="/dev/p${dev%% *}"
fi
fi
local dev="${msg#*/dev/p}"
dev="/dev/p${dev%% *}"
if [ ! -c "$dev" ]; then
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000')
dev="${dev#*serial0}"
@ -90,7 +101,6 @@ terminal() {
_graceful_shutdown() {
local cnt=0
local code=$?
local pid url response
@ -104,12 +114,12 @@ _graceful_shutdown() {
touch "$QEMU_END"
echo && info "Received $1 signal, sending shutdown command..."
if [ ! -f "$QEMU_PID" ]; then
if [ ! -s "$QEMU_PID" ]; then
echo && error "QEMU PID file does not exist?"
finish "$code" && return "$code"
fi
pid="$(cat "$QEMU_PID")"
pid=$(<"$QEMU_PID")
if ! isAlive "$pid"; then
echo && error "QEMU process does not exist?"
@ -136,6 +146,8 @@ _graceful_shutdown() {
fi
local cnt=0
while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do
! isAlive "$pid" && break
@ -146,7 +158,7 @@ _graceful_shutdown() {
[[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt/$QEMU_TIMEOUT)"
# Workaround for zombie pid
[ ! -f "$QEMU_PID" ] && break
[ ! -s "$QEMU_PID" ] && break
done
@ -159,11 +171,12 @@ _graceful_shutdown() {
MON_OPTS="\
-pidfile $QEMU_PID \
-name $PROCESS,process=$PROCESS,debug-threads=on \
-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay"
if [[ "$CONSOLE" != [Yy]* ]]; then
MON_OPTS="$MON_OPTS -daemonize -D $QEMU_LOG"
MON_OPTS+=" -daemonize -D $QEMU_LOG"
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT

View File

@ -1,21 +1,27 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: ${DHCP:='N'}
: "${DHCP:="N"}"
: "${NETWORK:="Y"}"
[[ "$NETWORK" == [Nn]* ]] && exit 0
info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "$1" "\E[0m\n" >&2; }
error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; }
file="/run/dsm.url"
address="/run/qemu.ip"
shutdown="/run/qemu.end"
file="/run/shm/dsm.url"
info="/run/shm/msg.html"
page="/run/shm/index.html"
address="/run/shm/qemu.ip"
shutdown="/run/shm/qemu.end"
template="/var/www/index.html"
url="http://127.0.0.1:2210/read?command=10"
resp_err="Guest returned an invalid response:"
curl_err="Failed to connect to guest: curl error"
jq_err="Failed to parse response from guest: jq error"
while [ ! -f "$file" ]
while [ ! -s "$file" ]
do
# Check if not shutting down
@ -24,7 +30,7 @@ do
sleep 3
[ -f "$shutdown" ] && exit 1
[ -f "$file" ] && break
[ -s "$file" ] && break
# Retrieve network info from guest VM
{ json=$(curl -m 20 -sk "$url"); rc=$?; } || :
@ -61,15 +67,28 @@ done
[ -f "$shutdown" ] && exit 1
location=$(cat "$file")
location=$(<"$file")
if [[ "$location" != "20.20"* ]]; then
msg="http://$location"
title="<title>Virtual DSM</title>"
body="The location of DSM is <a href='http://$location'>http://$location</a>"
script="<script>setTimeout(function(){ window.location.assign('http://$location'); }, 3000);</script>"
HTML=$(<"$template")
HTML="${HTML/\[1\]/$title}"
HTML="${HTML/\[2\]/$script}"
HTML="${HTML/\[3\]/$body}"
HTML="${HTML/\[4\]/}"
HTML="${HTML/\[5\]/}"
echo "$HTML" > "$page"
echo "$body" > "$info"
else
ip="$(cat "$address")"
ip=$(<"$address")
port="${location##*:}"
if [[ "$ip" == "172."* ]]; then
@ -85,3 +104,5 @@ info "-----------------------------------------------------------"
info " You can now login to DSM at $msg"
info "-----------------------------------------------------------"
echo "" >&2
exit 0

View File

@ -3,64 +3,147 @@ set -Eeuo pipefail
# Docker environment variables
: ${KVM:='Y'}
: ${HOST_CPU:=''}
: ${CPU_MODEL:='host'}
: ${CPU_FEATURES:='+ssse3,+sse4.1,+sse4.2'}
: "${KVM:="Y"}"
: "${HOST_CPU:=""}"
: "${CPU_FLAGS:=""}"
: "${CPU_MODEL:=""}"
: "${DEF_MODEL:="qemu64"}"
[ "$ARCH" != "amd64" ] && KVM="N"
CLOCKSOURCE="tsc"
[[ "${ARCH,,}" == "arm64" ]] && CLOCKSOURCE="arch_sys_counter"
CLOCK="/sys/devices/system/clocksource/clocksource0/current_clocksource"
if [ ! -f "$CLOCK" ]; then
warn "file \"$CLOCK\" cannot not found?"
else
result=$(<"$CLOCK")
result="${result//[![:print:]]/}"
case "${result,,}" in
"${CLOCKSOURCE,,}" ) ;;
"kvm-clock" ) info "Nested KVM virtualization detected.." ;;
"hyperv_clocksource_tsc_page" ) info "Nested Hyper-V virtualization detected.." ;;
"hpet" ) warn "unsupported clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
*) warn "unexpected clock source detected: '$result'. Please set host clock source to '$CLOCKSOURCE'." ;;
esac
fi
if [[ "${ARCH,,}" != "amd64" ]]; then
KVM="N"
warn "your CPU architecture is ${ARCH^^} and cannot provide KVM acceleration for x64 instructions, this will cause a major loss of performance."
fi
if [[ "$KVM" != [Nn]* ]]; then
KVM_ERR=""
if [ ! -e /dev/kvm ]; then
KVM_ERR="(device file missing)"
KVM_ERR="(/dev/kvm is missing)"
else
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
KVM_ERR="(no write access)"
KVM_ERR="(/dev/kvm is unwriteable)"
else
if ! grep -q -e vmx -e svm /proc/cpuinfo; then
KVM_ERR="(vmx/svm disabled)"
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
if ! grep -qw "vmx\|svm" <<< "$flags"; then
KVM_ERR="(not enabled in BIOS)"
fi
fi
fi
if [ -n "$KVM_ERR" ]; then
KVM="N"
error "KVM acceleration not detected $KVM_ERR, this will cause a major loss of performance."
error "See the FAQ on how to enable it, or continue without KVM by setting KVM=N (not recommended)."
[[ "$DEBUG" != [Yy1]* ]] && exit 88
if [[ "$OSTYPE" =~ ^darwin ]]; then
warn "you are using macOS which has no KVM support, this will cause a major loss of performance."
else
kernel=$(uname -a)
case "${kernel,,}" in
*"microsoft"* )
error "Please bind '/dev/kvm' as a volume in the optional container settings when using Docker Desktop." ;;
*"synology"* )
error "Please make sure that Synology VMM (Virtual Machine Manager) is installed and that '/dev/kvm' is binded to this container." ;;
*)
error "KVM acceleration is not available $KVM_ERR, this will cause a major loss of performance."
error "See the FAQ for possible causes, or continue without it by adding KVM: \"N\" (not recommended)." ;;
esac
[[ "$DEBUG" != [Yy1]* ]] && exit 88
fi
fi
fi
if [[ "$KVM" != [Nn]* ]]; then
KVM_OPTS=",accel=kvm -enable-kvm"
CPU_FEATURES="kvm=on,l3-cache=on,+hypervisor"
KVM_OPTS=",accel=kvm -enable-kvm -global kvm-pit.lost_tick_policy=discard"
if ! grep -qw "sse4_2" <<< "$flags"; then
info "Your CPU does not have the SSE4 instruction set that Virtual DSM requires, it will be emulated..."
[ -z "$CPU_MODEL" ] && CPU_MODEL="$DEF_MODEL"
CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2"
fi
if [ -z "$CPU_MODEL" ]; then
CPU_MODEL="host"
CPU_FEATURES+=",migratable=no"
fi
if grep -qw "svm" <<< "$flags"; then
# AMD processor
if grep -qw "tsc_scale" <<< "$flags"; then
CPU_FEATURES+=",+invtsc"
fi
else
# Intel processor
vmx=$(sed -ne '/^vmx flags/s/^.*: //p' /proc/cpuinfo)
if grep -qw "tsc_scaling" <<< "$vmx"; then
CPU_FEATURES+=",+invtsc"
fi
if ! grep -qE '^flags.* (sse4_2)' /proc/cpuinfo; then
error "Your host CPU does not have the SSE4.2 instruction set that Virtual DSM requires to boot."
error "Disable KVM by setting KVM=N to emulate a compatible CPU, at the cost of performance."
[[ "$DEBUG" != [Yy1]* ]] && exit 89
fi
else
KVM_OPTS=""
CPU_FEATURES="l3-cache=on,+hypervisor"
if [[ "$CPU_MODEL" == "host"* ]]; then
if [[ "$ARCH" == "amd64" ]]; then
KVM_OPTS=" -accel tcg,thread=multi"
fi
if [ -z "$CPU_MODEL" ]; then
if [[ "$ARCH" == "amd64" ]]; then
CPU_MODEL="max,$CPU_FEATURES"
CPU_MODEL="max"
CPU_FEATURES+=",migratable=no"
else
CPU_MODEL="qemu64,$CPU_FEATURES"
CPU_MODEL="$DEF_MODEL"
fi
fi
CPU_FEATURES+=",+ssse3,+sse4.1,+sse4.2"
fi
if [ -z "$CPU_FLAGS" ]; then
if [ -z "$CPU_FEATURES" ]; then
CPU_FLAGS="$CPU_MODEL"
else
CPU_FLAGS="$CPU_MODEL,$CPU_FEATURES"
fi
else
if [ -z "$CPU_FEATURES" ]; then
CPU_FLAGS="$CPU_MODEL,$CPU_FLAGS"
else
CPU_FLAGS="$CPU_MODEL,$CPU_FEATURES,$CPU_FLAGS"
fi
fi
if [ -z "$HOST_CPU" ]; then
HOST_CPU=$(lscpu | grep 'Model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
[[ "${CPU,,}" != "unknown" ]] && HOST_CPU="$CPU"
fi
if [ -n "$HOST_CPU" ]; then
@ -68,9 +151,9 @@ if [ -n "$HOST_CPU" ]; then
else
HOST_CPU="QEMU, Virtual CPU,"
if [ "$ARCH" == "amd64" ]; then
HOST_CPU="$HOST_CPU X86_64"
HOST_CPU+=" X86_64"
else
HOST_CPU="$HOST_CPU $ARCH"
HOST_CPU+=" $ARCH"
fi
fi

38
src/progress.sh Normal file
View File

@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -Eeuo pipefail
escape () {
local s
s=${1//&/\&amp;}
s=${s//</\&lt;}
s=${s//>/\&gt;}
s=${s//'"'/\&quot;}
printf -- %s "$s"
return 0
}
file="$1"
total="$2"
body=$(escape "$3")
info="/run/shm/msg.html"
if [[ "$body" == *"..." ]]; then
body="<p class=\"loading\">${body/.../}</p>"
fi
while true
do
if [ -s "$file" ]; then
bytes=$(du -sb "$file" | cut -f1)
if (( bytes > 1000 )); then
if [ -z "$total" ] || [[ "$total" == "0" ]]; then
size=$(numfmt --to=iec --suffix=B "$bytes" | sed -r 's/([A-Z])/ \1/')
else
size="$(echo "$bytes" "$total" | awk '{printf "%.1f", $1 * 100 / $2}')"
size="$size%"
fi
echo "${body//(\[P\])/($size)}"> "$info"
fi
fi
sleep 1 & wait $!
done

View File

@ -1,88 +1,129 @@
#!/usr/bin/env bash
set -Eeuo pipefail
info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "$1" "\E[0m\n"; }
error () { printf "%b%s%b" "\E[1;31m " "ERROR: $1" "\E[0m\n" >&2; }
trap 'error "Status $? while: $BASH_COMMAND (line $LINENO/$BASH_LINENO)"' ERR
[ ! -f "/run/entry.sh" ] && error "Script must run inside Docker container!" && exit 11
[ "$(id -u)" -ne "0" ] && error "Script must be executed with root privileges." && exit 12
echo " Starting $APP for Docker v$(</run/version)..."
echo " For support visit $SUPPORT"
# Docker environment variables
: ${TZ:=''} # System local timezone
: ${DEBUG:='N'} # Disable debugging mode
: ${COUNTRY:=''} # Country code for mirror
: ${CONSOLE:='N'} # Disable console mode
: ${ALLOCATE:='Y'} # Preallocate diskspace
: ${ARGUMENTS:=''} # Extra QEMU parameters
: ${CPU_CORES:='1'} # Amount of CPU cores
: ${RAM_SIZE:='1G'} # Maximum RAM amount
: ${DISK_SIZE:='16G'} # Initial data disk size
: "${TZ:=""}" # System local timezone
: "${DEBUG:="N"}" # Disable debugging mode
: "${COUNTRY:=""}" # Country code for mirror
: "${CONSOLE:="N"}" # Disable console mode
: "${ALLOCATE:=""}" # Preallocate diskspace
: "${ARGUMENTS:=""}" # Extra QEMU parameters
: "${CPU_CORES:="2"}" # Amount of CPU cores
: "${RAM_SIZE:="2G"}" # Maximum RAM amount
: "${RAM_CHECK:="Y"}" # Check available RAM
: "${DISK_SIZE:="16G"}" # Initial data disk size
: "${STORAGE:="/storage"}" # Storage folder location
# Helper variables
KERNEL=$(uname -r | cut -b 1)
MINOR=$(uname -r | cut -d '.' -f2)
PROCESS="${APP,,}"
PROCESS="${PROCESS// /-}"
INFO="/run/shm/msg.html"
PAGE="/run/shm/index.html"
TEMPLATE="/var/www/index.html"
FOOTER1="$APP for Docker v$(</run/version)"
FOOTER2="<a href='$SUPPORT'>$SUPPORT</a>"
CPU=$(cpu)
SYS=$(uname -r)
HOST=$(hostname -s)
KERNEL=$(echo "$SYS" | cut -b 1)
MINOR=$(echo "$SYS" | cut -d '.' -f2)
ARCH=$(dpkg --print-architecture)
VERS=$(qemu-system-x86_64 --version | head -n 1 | cut -d '(' -f 1)
CORES=$(grep -c '^processor' /proc/cpuinfo)
if ! grep -qi "socket(s)" <<< "$(lscpu)"; then
SOCKETS=1
else
SOCKETS=$(lscpu | grep -m 1 -i 'socket(s)' | awk '{print $(2)}')
fi
[ -n "${CPU_CORES//[0-9 ]}" ] && error "Invalid amount of CPU_CORES: $CPU_CORES" && exit 15
# Check system
if [ ! -d "/dev/shm" ]; then
error "Directory /dev/shm not found!" && exit 14
else
[ ! -d "/run/shm" ] && ln -s /dev/shm /run/shm
fi
# Check folder
STORAGE="/storage"
[ ! -d "$STORAGE" ] && error "Storage folder ($STORAGE) not found!" && exit 13
if [[ "${COMMIT:-}" == [Yy1]* ]]; then
STORAGE="/local"
mkdir -p "$STORAGE"
fi
if [ ! -d "$STORAGE" ]; then
error "Storage folder ($STORAGE) not found!" && exit 13
fi
# Check filesystem
FS=$(stat -f -c %T "$STORAGE")
if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then
DISK_IO="threads"
DISK_CACHE="writeback"
fi
# Read memory
RAM_SPARE=500000000
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
RAM_SIZE="${RAM_SIZE// /}"
[ -z "$RAM_SIZE" ] && error "RAM_SIZE not specified!" && exit 16
if [ -z "${RAM_SIZE//[0-9. ]}" ]; then
[ "${RAM_SIZE%%.*}" -lt "130" ] && RAM_SIZE="${RAM_SIZE}G" || RAM_SIZE="${RAM_SIZE}M"
fi
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
! numfmt --from=iec "$RAM_SIZE" &>/dev/null && error "Invalid RAM_SIZE: $RAM_SIZE" && exit 16
RAM_WANTED=$(numfmt --from=iec "$RAM_SIZE")
[ "$RAM_WANTED" -lt "136314880 " ] && error "RAM_SIZE is too low: $RAM_SIZE" && exit 16
# Print system info
SYS="${SYS/-generic/}"
FS="${FS/UNKNOWN //}"
FS="${FS/ext2\/ext3/ext4}"
FS=$(echo "$FS" | sed 's/[)(]//g')
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
SPACE_GB=$(formatBytes "$SPACE" "down")
AVAIL_MEM=$(formatBytes "$RAM_AVAIL" "down")
TOTAL_MEM=$(formatBytes "$RAM_TOTAL" "up")
echo " CPU: ${CPU} | RAM: ${AVAIL_MEM/ GB/}/$TOTAL_MEM | DISK: $SPACE_GB (${FS}) | KERNEL: ${SYS}..."
echo
# Check available memory
if [[ "$RAM_CHECK" != [Nn]* ]] && (( (RAM_WANTED + RAM_SPARE) > RAM_AVAIL )); then
AVAIL_MEM=$(formatBytes "$RAM_AVAIL")
msg="Your configured RAM_SIZE of ${RAM_SIZE/G/ GB} is too high for the $AVAIL_MEM of memory available, please set a lower value."
[[ "${FS,,}" != "zfs" ]] && error "$msg" && exit 17
info "$msg"
fi
# Cleanup files
rm -f /run/dsm.url
rm -f /run/qemu.ip
rm -f /run/qemu.log
rm -f /run/qemu.pid
rm -f /run/qemu.end
rm -f /run/shm/qemu.*
rm -f /run/shm/dsm.url
# Cleanup dirs
rm -rf /tmp/dsm
rm -f /tmp/server.*
rm -rf "$STORAGE/tmp"
# Helper functions
isAlive() {
local pid=$1
if kill -0 "$pid" 2>/dev/null; then
return 0
fi
return 1
}
pKill() {
local pid=$1
{ kill -15 "$pid" || true; } 2>/dev/null
while isAlive "$pid"; do
sleep 0.1
done
return 0
}
fKill() {
local name=$1
{ pkill -f "$name" || true; } 2>/dev/null
while pgrep -f -l "$name" >/dev/null; do
sleep 0.1
done
return 0
}
getCountry() {
local url=$1
local query=$2
@ -128,10 +169,8 @@ addPackage() {
return 0
fi
info "Installing $desc..."
export DEBCONF_NOWARNINGS="yes"
export DEBIAN_FRONTEND="noninteractive"
MSG="Installing $desc..."
info "$MSG" && html "$MSG"
[ -z "$COUNTRY" ] && setCountry
@ -139,10 +178,26 @@ addPackage() {
sed -i 's/deb.debian.org/mirrors.ustc.edu.cn/g' /etc/apt/sources.list.d/debian.sources
fi
apt-get -qq update
apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
DEBIAN_FRONTEND=noninteractive apt-get -qq update
DEBIAN_FRONTEND=noninteractive apt-get -qq --no-install-recommends -y install "$pkg" > /dev/null
return 0
}
# shellcheck disable=SC2143
if [ -f /proc/net/if_inet6 ] && [ -n "$(ifconfig -a | grep inet6)" ]; then
sed -i "s/listen 5000 default_server;/listen [::]:5000 default_server ipv6only=off;/g" /etc/nginx/sites-enabled/web.conf
else
sed -i "s/listen [::]:5000 default_server ipv6only=off;/listen 5000 default_server;/g" /etc/nginx/sites-enabled/web.conf
fi
# Start webserver
cp -r /var/www/* /run/shm
html "Starting $APP for Docker..."
nginx -e stderr
return 0

View File

@ -3,11 +3,26 @@ set -Eeuo pipefail
# Docker environment variables
: ${HOST_MAC:=''}
: ${HOST_DEBUG:=''}
: ${HOST_SERIAL:=''}
: ${HOST_MODEL:=''}
: ${GUEST_SERIAL:=''}
: "${HOST_MAC:=""}"
: "${HOST_DEBUG:=""}"
: "${HOST_SERIAL:=""}"
: "${HOST_MODEL:=""}"
: "${GUEST_SERIAL:=""}"
if [ -n "$HOST_MAC" ]; then
HOST_MAC="${HOST_MAC//-/:}"
if [[ ${#HOST_MAC} == 12 ]]; then
m="$HOST_MAC"
HOST_MAC="${m:0:2}:${m:2:2}:${m:4:2}:${m:6:2}:${m:8:2}:${m:10:2}"
fi
if [[ ${#HOST_MAC} != 17 ]]; then
error "Invalid HOST_MAC address: '$HOST_MAC', should be 12 or 17 digits long!" && exit 28
fi
fi
HOST_ARGS=()
HOST_ARGS+=("-cpu=$CPU_CORES")
@ -52,7 +67,7 @@ else
SERIAL_OPTS="-serial mon:stdio"
fi
SERIAL_OPTS="$SERIAL_OPTS \
SERIAL_OPTS+=" \
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
-chardev socket,id=charchannel0,host=127.0.0.1,port=12345,reconnect=10 \
-device virtserialport,bus=virtio-serial0.0,nr=1,chardev=charchannel0,id=channel0,name=vchannel"

View File

@ -1,66 +0,0 @@
#!/usr/bin/env bash
set -eu
TMP_FILE=$(mktemp -q /tmp/server.XXXXXX)
stop() {
trap - SIGINT EXIT
{ pkill -f socat || true; } 2>/dev/null
[ -f "$TMP_FILE" ] && rm -f "$TMP_FILE"
}
trap 'stop' EXIT SIGINT SIGTERM SIGHUP
html()
{
local h="<!DOCTYPE html><HTML><HEAD><TITLE>VirtualDSM</TITLE>"
h="$h<STYLE>body { color: white; background-color: #125bdb; font-family: Verdana,"
h="$h Arial,sans-serif; } a, a:hover, a:active, a:visited { color: white; }</STYLE></HEAD>"
h="$h<BODY><BR><BR><H1><CENTER>$1</CENTER></H1></BODY></HTML>"
echo "$h"
}
if [[ "$2" != "/"* ]]; then
BODY="$2"
if [[ "$BODY" == "install" ]]; then
BODY="Please wait while Virtual DSM is being installed..."
BODY="$BODY<script>setTimeout(() => { document.location.reload(); }, 9999);</script>"
fi
HTML=$(html "$BODY")
printf '%b' "HTTP/1.1 200 OK\nContent-Length: ${#HTML}\nConnection: close\n\n$HTML" > "$TMP_FILE"
socat TCP4-LISTEN:80,reuseaddr,fork,crlf SYSTEM:"cat $TMP_FILE" 2> /dev/null &
socat TCP4-LISTEN:"${1:-5000}",reuseaddr,fork,crlf SYSTEM:"cat $TMP_FILE" 2> /dev/null & wait $!
exit
fi
if [[ "$2" != "/run/ip.sh" ]]; then
cp "$2" "$TMP_FILE"
else
BODY="The location of DSM is <a href='http://\$LOCATION'>http://\$LOCATION</a><script>"
BODY="$BODY setTimeout(function(){ window.location.assign('http://\$LOCATION'); }, 3000);</script>"
WAIT="Please wait while discovering IP...<script>setTimeout(() => { document.location.reload(); }, 4999);</script>"
HTML=$(html "xxx")
{ echo "#!/bin/bash"
echo "[ -f \"/run/dsm.url\" ] && LOCATION=\$(cat \"/run/dsm.url\")"
echo "HTML=\"$HTML\"; [ -z \"\$LOCATION\" ] && BODY=\"$WAIT\" || BODY=\"$BODY\"; HTML=\${HTML/xxx/\$BODY}"
echo "printf '%b' \"HTTP/1.1 200 OK\\nContent-Length: \${#HTML}\\nConnection: close\\n\\n\$HTML\""
} > "$TMP_FILE"
fi
chmod +x "$TMP_FILE"
socat TCP4-LISTEN:80,reuseaddr,fork,crlf SYSTEM:"$TMP_FILE" 2> /dev/null &
socat TCP4-LISTEN:"${1:-5000}",reuseaddr,fork,crlf SYSTEM:"$TMP_FILE" 2> /dev/null & wait $!

175
src/utils.sh Normal file
View File

@ -0,0 +1,175 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Helper functions
info () { printf "%b%s%b" "\E[1;34m \E[1;36m" "${1:-}" "\E[0m\n"; }
error () { printf "%b%s%b" "\E[1;31m " "ERROR: ${1:-}" "\E[0m\n" >&2; }
warn () { printf "%b%s%b" "\E[1;31m " "Warning: ${1:-}" "\E[0m\n" >&2; }
formatBytes() {
local result
result=$(numfmt --to=iec --suffix=B "$1" | sed -r 's/([A-Z])/ \1/' | sed 's/ B/ bytes/g;')
local unit="${result//[0-9. ]}"
result="${result//[a-zA-Z ]/}"
if [[ "${2:-}" == "up" ]]; then
if [[ "$result" == *"."* ]]; then
result="${result%%.*}"
result=$((result+1))
fi
else
if [[ "${2:-}" == "down" ]]; then
result="${result%%.*}"
fi
fi
echo "$result $unit"
return 0
}
isAlive() {
local pid="$1"
if kill -0 "$pid" 2>/dev/null; then
return 0
fi
return 1
}
pKill() {
local pid="$1"
{ kill -15 "$pid" || true; } 2>/dev/null
while isAlive "$pid"; do
sleep 0.2
done
return 0
}
fWait() {
local name="$1"
while pgrep -f -l "$name" >/dev/null; do
sleep 0.2
done
return 0
}
fKill() {
local name="$1"
{ pkill -f "$name" || true; } 2>/dev/null
fWait "$name"
return 0
}
escape () {
local s
s=${1//&/\&amp;}
s=${s//</\&lt;}
s=${s//>/\&gt;}
s=${s//'"'/\&quot;}
printf -- %s "$s"
return 0
}
html() {
local title
local body
local script
local footer
title=$(escape "$APP")
title="<title>$title</title>"
footer=$(escape "$FOOTER1")
body=$(escape "$1")
if [[ "$body" == *"..." ]]; then
body="<p class=\"loading\">${body/.../}</p>"
fi
[ -n "${2:-}" ] && script="$2" || script=""
local HTML
HTML=$(<"$TEMPLATE")
HTML="${HTML/\[1\]/$title}"
HTML="${HTML/\[2\]/$script}"
HTML="${HTML/\[3\]/$body}"
HTML="${HTML/\[4\]/$footer}"
HTML="${HTML/\[5\]/$FOOTER2}"
echo "$HTML" > "$PAGE"
echo "$body" > "$INFO"
return 0
}
cpu() {
local ret
local cpu=""
ret=$(lscpu)
if grep -qi "model name" <<< "$ret"; then
cpu=$(echo "$ret" | grep -m 1 -i 'model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
fi
if [ -z "${cpu// /}" ] && grep -qi "model:" <<< "$ret"; then
cpu=$(echo "$ret" | grep -m 1 -i 'model:' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
fi
cpu="${cpu// CPU/}"
cpu="${cpu// 4 Core/}"
cpu="${cpu// 6 Core/}"
cpu="${cpu// 8 Core/}"
cpu="${cpu// 10 Core/}"
cpu="${cpu// 12 Core/}"
cpu="${cpu// 16 Core/}"
cpu="${cpu// 32 Core/}"
cpu="${cpu// 48 Core/}"
cpu="${cpu// 64 Core/}"
cpu="${cpu// 96 Core/}"
cpu="${cpu// 128 Core/}"
cpu="${cpu//7th Gen /}"
cpu="${cpu//8th Gen /}"
cpu="${cpu//9th Gen /}"
cpu="${cpu//10th Gen /}"
cpu="${cpu//11th Gen /}"
cpu="${cpu//12th Gen /}"
cpu="${cpu//13th Gen /}"
cpu="${cpu//14th Gen /}"
cpu="${cpu//15th Gen /}"
cpu="${cpu// Processor/}"
cpu="${cpu// Quad core/}"
cpu="${cpu// Dual core/}"
cpu="${cpu// Octa core/}"
cpu="${cpu// Core TM/ Core}"
cpu="${cpu// with Radeon Graphics/}"
cpu="${cpu// with Radeon Vega Graphics/}"
cpu="${cpu// with Radeon Vega Mobile Gfx/}"
[ -z "${cpu// /}" ] && cpu="Unknown"
echo "$cpu"
return 0
}
hasDisk() {
[ -b "/disk" ] && return 0
[ -b "/disk1" ] && return 0
[ -b "/dev/disk1" ] && return 0
[ -b "${DEVICE:-}" ] && return 0
[ -z "${DISK_NAME:-}" ] && DISK_NAME="data"
[ -s "$STORAGE/$DISK_NAME.img" ] && return 0
[ -s "$STORAGE/$DISK_NAME.qcow2" ] && return 0
return 1
}
return 0

30
web/conf/nginx.conf Normal file
View File

@ -0,0 +1,30 @@
server {
listen 5000 default_server;
autoindex on;
tcp_nodelay on;
server_tokens off;
absolute_redirect off;
error_log /dev/null;
access_log /dev/null;
include /etc/nginx/mime.types;
gzip on;
gzip_vary on;
gzip_proxied any;
gzip_comp_level 5;
gzip_min_length 500;
gzip_disable "msie6";
gzip_types text/css text/javascript text/xml text/plain text/x-component application/javascript application/json application/xml application/rss+xml font/truetype font/opentype application/vnd.ms-fontobject image/svg+xml;
add_header Cache-Control "no-cache";
location / {
root /run/shm;
index index.html;
}
}

167
web/css/style.css Normal file
View File

@ -0,0 +1,167 @@
body {
color: white;
background-color: #125bdb;
font-smoothing: antialiased;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
font-family: Verdana, Geneva, sans-serif;
}
#info {
text-shadow: 1px 1px 1px rgba(0, 0, 0, 0.25);
}
#content {
text-align: center;
padding: 20px;
margin-top: 50px;
}
footer {
width: 98%;
position: fixed;
bottom: 0px;
height: 40px;
text-align: center;
color: #0c8aeb;
text-shadow: 0 0 1px #0c8aeb;
}
#empty {
height: 40px;
/* Same height as footer */
}
a,
a:hover,
a:active,
a:visited {
color: white;
}
footer a:link,
footer a:visited,
footer a:active {
color: #0c8aeb;
}
footer a:hover {
color: #73e6ff;
}
.loading:after {
content: " .";
animation: dots 1s steps(5, end) infinite;
}
@keyframes dots {
0%,
20% {
color: rgba(0, 0, 0, 0);
text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0);
}
40% {
color: white;
text-shadow: 0.25em 0 0 rgba(0, 0, 0, 0), 0.5em 0 0 rgba(0, 0, 0, 0);
}
60% {
text-shadow: 0.25em 0 0 white, 0.5em 0 0 rgba(0, 0, 0, 0);
}
80%,
100% {
text-shadow: 0.25em 0 0 white, 0.5em 0 0 white;
}
}
.spinner_LWk7 {
animation: spinner_GWy6 1.2s linear infinite, spinner_BNNO 1.2s linear infinite
}
.spinner_yOMU {
animation: spinner_GWy6 1.2s linear infinite, spinner_pVqn 1.2s linear infinite;
animation-delay: .15s
}
.spinner_KS4S {
animation: spinner_GWy6 1.2s linear infinite, spinner_6uKB 1.2s linear infinite;
animation-delay: .3s
}
.spinner_zVee {
animation: spinner_GWy6 1.2s linear infinite, spinner_Qw4x 1.2s linear infinite;
animation-delay: .45s
}
@keyframes spinner_GWy6 {
0%,
50% {
width: 9px;
height: 9px
}
10% {
width: 11px;
height: 11px
}
}
@keyframes spinner_BNNO {
0%,
50% {
x: 1.5px;
y: 1.5px
}
10% {
x: .5px;
y: .5px
}
}
@keyframes spinner_pVqn {
0%,
50% {
x: 13.5px;
y: 1.5px
}
10% {
x: 12.5px;
y: .5px
}
}
@keyframes spinner_6uKB {
0%,
50% {
x: 13.5px;
y: 13.5px
}
10% {
x: 12.5px;
y: 12.5px
}
}
@keyframes spinner_Qw4x {
0%,
50% {
x: 1.5px;
y: 13.5px
}
10% {
x: .5px;
y: 12.5px
}
}

1
web/img/favicon.svg Normal file
View File

@ -0,0 +1 @@
<svg id="Capa_1" enable-background="new 0 0 511.962 511.962" height="512" viewBox="0 0 511.962 511.962" width="512" xmlns="http://www.w3.org/2000/svg"><g><path d="m489.965 120.063c0-5.77-3.31-11.028-8.512-13.524l-218.984-105.063c-4.102-1.967-8.875-1.967-12.977 0l-218.985 105.063c-5.202 2.496-8.511 7.755-8.511 13.524l-.003 271.834c0 5.77 3.31 11.028 8.512 13.524l218.989 105.064c2.051.983 4.27 1.476 6.488 1.476 2.219 0 4.438-.492 6.488-1.476l218.989-105.064c5.202-2.496 8.512-7.755 8.512-13.524z" fill="#4e6ba6"/><path d="m489.965 120.063c0-5.77-3.31-11.028-8.512-13.524l-218.984-105.063c-2.051-.984-4.269-1.476-6.488-1.476v511.962c2.219 0 4.438-.492 6.488-1.476l218.989-105.064c5.202-2.496 8.512-7.755 8.512-13.524z" fill="#28487a"/><path d="m425.812 160.441c0-2.27-.519-4.457-1.457-6.432l-336.701-.095c-.967 1.999-1.504 4.22-1.504 6.526l-.002 191.081c0 5.769 3.31 11.028 8.512 13.524l154.833 74.285c2.051.983 4.27 1.476 6.488 1.476 2.219 0 4.438-.492 6.488-1.476l154.834-74.285c5.202-2.496 8.512-7.755 8.512-13.524z" fill="#8dc2eb"/><path d="m424.354 154.009h-168.373v286.798c2.219 0 4.438-.492 6.488-1.476l154.834-74.285c5.202-2.496 8.512-7.755 8.512-13.524l-.003-191.081c0-2.27-.52-4.458-1.458-6.432z" fill="#5e9ff6"/><path d="m417.3 146.916-154.831-74.284c-4.102-1.967-8.875-1.967-12.977 0l-154.831 74.284c-3.122 1.498-5.555 3.996-7.007 6.998l168.328 80.812 168.374-80.717c-1.448-3.044-3.9-5.579-7.056-7.093z" fill="#ecf9fd"/><path d="m417.3 146.916-154.831-74.284c-2.051-.983-4.27-1.476-6.488-1.476v163.569l168.374-80.717c-1.447-3.043-3.899-5.578-7.055-7.092z" fill="#d9f3fc"/></g></svg>

After

Width:  |  Height:  |  Size: 1.6 KiB

34
web/index.html Normal file
View File

@ -0,0 +1,34 @@
<!DOCTYPE html>
<html>
<head>
[1]
<meta http-equiv="Cache-Control" content="no-cache" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<link rel="stylesheet" type="text/css" href="css/style.css" />
<link rel="icon" href="img/favicon.svg" type="image/x-icon">
[2]
</head>
<body>
<div id="page">
<div id="content">
<svg id="spinner" width="64" height="64" viewBox="0 0 24 24" xmlns="http://www.w3.org/2000/svg">
<rect class="spinner_LWk7" fill="#0c8aeb" x="1.5" y="1.5" rx="1" width="9" height="9"/>
<rect class="spinner_yOMU" fill="#0c8aeb" x="13.5" y="1.5" rx="1" width="9" height="9"/>
<rect class="spinner_KS4S" fill="#0c8aeb" x="13.5" y="13.5" rx="1" width="9" height="9"/>
<rect class="spinner_zVee" fill="#0c8aeb" x="1.5" y="13.5" rx="1" width="9" height="9"/>
</svg>
<h1 id="info">[3]</h1>
</div>
<div id="empty">
</div>
<footer id="footer">
[4]<br />
[5]
</footer>
</div>
<script type="text/javascript" src="js/script.js"></script>
</body>
</html>

130
web/js/script.js Normal file
View File

@ -0,0 +1,130 @@
var request;
var interval = 1000;
function getInfo() {
var url = "msg.html";
try {
if (window.XMLHttpRequest) {
request = new XMLHttpRequest();
} else {
throw "XMLHttpRequest not available!";
}
request.onreadystatechange = processInfo;
request.open("GET", url, true);
request.send();
} catch (e) {
var err = "Error: " + e.message;
console.log(err);
setError(err);
}
}
function processInfo() {
try {
if (request.readyState != 4) {
return true;
}
var msg = request.responseText;
if (msg == null || msg.length == 0) {
setInfo("Booting DSM instance", true);
schedule();
return false;
}
var notFound = (request.status == 404);
if (request.status == 200) {
if (msg.toLowerCase().indexOf("<html>") !== -1) {
notFound = true;
} else {
if (msg.toLowerCase().indexOf("href=") !== -1) {
var div = document.createElement("div");
div.innerHTML = msg;
var url = div.querySelector("a").href;
setTimeout(() => {
window.location.assign(url);
}, 3000);
setInfo(msg);
return true;
} else {
setInfo(msg);
schedule();
return true;
}
}
}
if (notFound) {
setInfo("Connecting to web portal", true);
reload();
return true;
}
setError("Error: Received statuscode " + request.status);
schedule();
return false;
} catch (e) {
var err = "Error: " + e.message;
console.log(err);
setError(err);
return false;
}
}
function setInfo(msg, loading, error) {
try {
if (msg == null || msg.length == 0) {
return false;
}
var el = document.getElementById("spinner");
error = !!error;
if (!error) {
el.style.visibility = 'visible';
} else {
el.style.visibility = 'hidden';
}
loading = !!loading;
if (loading) {
msg = "<p class=\"loading\">" + msg + "</p>";
}
el = document.getElementById("info");
if (el.innerHTML != msg) {
el.innerHTML = msg;
}
return true;
} catch (e) {
console.log("Error: " + e.message);
return false;
}
}
function setError(text) {
return setInfo(text, false, true);
}
function schedule() {
setTimeout(getInfo, interval);
}
function reload() {
setTimeout(() => {
document.location.reload();
}, 3000);
}
schedule();