Compare commits

...

243 Commits
v4.43 ... v7.12

Author SHA1 Message Date
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
Kroese
c28dbb6b9b feat: Add FUSE warning (#542)
* feat: Add FUSE warning
2024-01-05 13:24:54 +01:00
Kroese
dcc26b67a6 fix: Diskspace check (#541)
* fix: Diskspace check
2024-01-05 02:27:15 +01:00
Kroese
2627d320f3 fix: Remove VNC support (#540)
* fix: Remove VNC support
2024-01-05 00:02:49 +01:00
Kroese
fd4bc28835 fix: Console mode (#538) 2024-01-04 23:24:34 +01:00
Kroese
63e4d588a2 docs: Readme (#535) 2024-01-04 05:33:30 +01:00
Kroese
feb1680909 build: Update location (#534) 2024-01-04 05:31:34 +01:00
IP2Location
9d21489b8e Added api.ip2location.io (#533) 2024-01-04 04:11:20 +01:00
Kroese
e70ed1900f fix: Port forwarding (#530) 2024-01-03 14:02:16 +01:00
Kroese
d65b5a089a fix: KVM check (#529) 2024-01-03 13:33:38 +01:00
Kroese
84643647cc fix: Compose (#527) 2023-12-31 15:11:20 +01:00
Kroese
57f487db6d build: Remove agent
* build: Remove agent
2023-12-31 01:56:03 +01:00
Kroese
0862cad2ce fix: Port forwarding (#525)
* fix: Port forwarding
2023-12-30 00:01:12 +01:00
Kroese
5ae4f59315 fix: QEMU monitor (#524) 2023-12-29 19:05:51 +01:00
Kroese
3fc3005ba5 fix: Processor (#523)
* fix: Processor
2023-12-29 18:27:51 +01:00
Kroese
20f48edd00 fix: Serial port (#522)
* fix: Serial port
2023-12-29 18:15:39 +01:00
Kroese
b854aad830 fix: Use single IO thread (#521) 2023-12-29 17:34:23 +01:00
Kroese
6cbe03f656 fix: Port forwarding (#520) 2023-12-29 17:20:18 +01:00
Kroese
63cac9a75e fix: Display config (#519)
* fix: Display config
2023-12-29 16:56:49 +01:00
Kroese
08616f1057 build: Update Dockerfile (#518) 2023-12-29 00:24:36 +01:00
Kroese
e6193b1020 fix: Port forwarding (#517) 2023-12-28 21:30:28 +01:00
Kroese
f28b9903f3 fix: iptables for NAT (#516) 2023-12-28 21:20:38 +01:00
Kroese
7bf2d119ea feat: Validate mac address (#515) 2023-12-28 20:54:33 +01:00
Kroese
527bded1b2 feat: Detect default interface
* feat: Detect default interface
2023-12-28 20:25:04 +01:00
Kroese
1208c53ebb feat: Check network interface (#513) 2023-12-28 18:26:56 +01:00
Kroese
973efa2d27 feat: Show daemon log
* feat: Show daemon log
2023-12-28 17:58:07 +01:00
Kroese
d09588b915 fix: Refactor
* fix: Refactor
2023-12-28 16:08:12 +01:00
Kroese
19aa313753 fix: Shellcheck (#509) 2023-12-28 08:35:51 +01:00
Kroese
9db12cd25f fix: Device detection
* fix: Device detection
2023-12-28 08:31:32 +01:00
Kroese
69e785e6ee fix: Shutdown message
* fix: Shutdown message
2023-12-28 05:26:53 +01:00
Kroese
159fce6839 fix: Detect device
* fix: Detect device
2023-12-28 05:04:40 +01:00
Kroese
08e4084458 feat: Daemonize QEMU
* feat: Daemonize QEMU
2023-12-28 03:42:06 +01:00
Kroese
06f210846c fix: KVM flag (#504) 2023-12-27 16:28:24 +01:00
Kroese
74629e4b55 fix: Installation (#501) 2023-12-27 04:07:45 +01:00
Kroese
6e8af6e52f fix: Host CPU (#500) 2023-12-27 03:23:39 +01:00
Kroese
38611a7af2 fix: Host CPU (#499) 2023-12-27 03:18:03 +01:00
Kroese
f089acc01a fix: CPU features (#496) 2023-12-25 05:58:14 +01:00
Kroese
5a7ecb48d6 fix: Error messages (#495) 2023-12-25 05:03:00 +01:00
Kroese
5b3880aa5e fix: Error messages (#494) 2023-12-25 04:48:18 +01:00
Kroese
4653aafbee docs: Readme (#493) 2023-12-25 04:21:50 +01:00
Kroese
281f2992ff fix: Close file descriptors
* fix: Close file descriptors
2023-12-25 04:04:01 +01:00
Kroese
4bdcf8bfe1 fix: Skip mknod errors (#491) 2023-12-24 14:39:37 +01:00
Kroese
62acaa95bf fix: Check attribute (#490) 2023-12-24 14:27:29 +01:00
Kroese
369bff339d fix: Update Dockerfile (#489) 2023-12-24 02:54:57 +01:00
Kroese
5332d387f4 fix: Set file attribute (#488)
* fix: Set file attribute
2023-12-24 02:41:44 +01:00
Kroese
f0d08ef263 fix: Remove flag (#487)
* fix: Remove flag
2023-12-23 22:58:45 +01:00
Kroese
e4334f9499 fix: Ignore mknod errors (#486) 2023-12-23 22:28:25 +01:00
Kroese
627ec56262 feat: LINUX_IMMUTABLE flag (#485)
* feat: LINUX_IMMUTABLE flag
2023-12-23 21:26:23 +01:00
Kroese
251cf8121e feat: Add LINUX_IMMUTABLE flag (#484)
To change directory attributes on COW filesystems
2023-12-23 20:35:06 +01:00
Kroese
87fad1b0e9 feat: Set directory attributes (#483)
* feat: Set directory attributes
2023-12-23 20:01:27 +01:00
Kroese
698516ac8c feat: Detect country from timezone (#482) 2023-12-23 18:46:14 +01:00
databreach
dae5d75674 feat: Improve support for unprivileged hosts (including LXC) (#479)
* * Add fakeroot to extract the dsm system without elevated permissions
* Remove obsolete docker variable "DEV" used to exclude extraction of device nodes

* feat: Detect unprivileged container

* fix: Use fakeroot for mke2fs

---------

Co-authored-by: Kroese <kroese@users.noreply.github.com>
2023-12-23 18:04:43 +01:00
Kroese
95facffa9b fix: Use specified LAN adaptor (#481)
* fix: Use specified LAN adaptor
2023-12-23 15:23:36 +01:00
Kroese
682e0a9952 feat: Check filesystem (#477)
* feat: Check filesystem
2023-12-22 15:00:22 +01:00
Kroese
9a97dfdc70 fix: Check filesystem (#476) 2023-12-22 06:15:33 +01:00
Kroese
2f383699f9 feat: Firewall info
feat: Firewall info
2023-12-22 05:20:44 +01:00
Kroese
8137a137b3 fix: Disable device nodes 2023-12-22 05:10:22 +01:00
Kroese
1339d51796 feat: Firewall info 2023-12-22 04:47:58 +01:00
Kroese
dce447c974 fix: Quit message 2023-12-22 04:24:48 +01:00
Kroese
ef5b650991 fix: Shutdown loop (#474) 2023-12-20 12:09:25 +01:00
Kroese
fd19c7b4f3 feat: Log creation info (#473) 2023-12-20 03:34:26 +01:00
Kroese
727297642c fix: Exit counter (#472) 2023-12-20 02:00:07 +01:00
Kroese
cd457801e7 fix: Set IP in bridge mode
* fix: Set IP in bridge mode
2023-12-20 01:52:55 +01:00
Kroese
392e7afdfe fix: Allocate after conversion
fix: Allocate after conversion
2023-12-19 08:54:43 +01:00
Kroese
b425e34907 fix: Allocate after conversion 2023-12-19 08:54:08 +01:00
Kroese
7c2e2fc7b1 feat: Enable IO threading
feat: Enable IO threading
2023-12-19 06:16:28 +01:00
Kroese
9bac0c94a8 feat: Detect COW on btrfs 2023-12-19 06:11:50 +01:00
Kroese
631568681e feat: Detect COW on BTRFS 2023-12-19 06:01:50 +01:00
Kroese
6599861dbb feat: Detect COW on BTRFS 2023-12-19 05:46:49 +01:00
Kroese
3bcd831531 fix: Display timeout 2023-12-19 04:30:01 +01:00
Kroese
fafd4a4fca feat: Enable IO threading 2023-12-19 03:59:19 +01:00
Kroese
5b69178f08 docs: Allocation 2023-12-19 03:33:41 +01:00
Kroese
479a30369d feat: Enable IO threading 2023-12-19 02:34:48 +01:00
Kroese
ba522a4acb feat: Increase clustersize for qcow2
* feat: Increase clustersize for qcow2
2023-12-18 17:34:40 +01:00
Kroese
3ab6d2c418 docs: Allocation 2023-12-18 16:48:01 +01:00
Kroese
b71b2245cb fix: Check diskspace
* fix: Check diskspace
2023-12-18 16:44:26 +01:00
Kroese
bbea0eb429 docs: Readme 2023-12-18 12:39:11 +01:00
Kroese
c28c5cfbaa docs: Allocation 2023-12-18 12:36:03 +01:00
Kroese
be3fd30cb2 feat: Honor allocation flag for qcow2
* feat: Honor allocation flag for qcow2
2023-12-18 12:18:28 +01:00
Kroese
e3942da906 feat: Set NOCOW flag for btrfs
feat: Set NOCOW flag for btrfs
2023-12-17 11:42:08 +01:00
Kroese
d66be1a228 fix: Do not set flags on resize 2023-12-17 11:25:42 +01:00
Kroese
72085d3711 fix: Set qcow2 flags 2023-12-17 11:17:26 +01:00
Kroese
e7cdbb1db5 feat: Optimize qcow2 flags 2023-12-17 10:51:30 +01:00
Kroese
7a592e0cea docs: DHCP mode 2023-12-17 10:08:55 +01:00
Kroese
107a4b87d5 fix: Quotes 2023-12-17 10:04:00 +01:00
Kroese
8b0ec3bef7 fix: Remove healthcheck exception 2023-12-17 09:57:50 +01:00
Kroese
aaded40a4f fix: Cleanup server files 2023-12-17 09:54:07 +01:00
Kroese
7f77bb88ab fix: Grammar 2023-12-17 09:50:07 +01:00
Kroese
95c3b2caad fix: Default folder 2023-12-17 09:39:19 +01:00
Kroese
150c450bf6 docs: Default folder 2023-12-17 09:37:36 +01:00
Kroese
932c23afba fix: Check SSE4.2 2023-12-16 15:57:30 +01:00
Kroese
2e0107e46f fix: Bash shebang
* fix: Bash shebang
2023-12-16 07:42:57 +01:00
Kroese
d22a3a4c7d feat: Support more CPU models
feat: Support more CPU models
2023-12-16 06:44:58 +01:00
Kroese
f93f870626 feat: CPU features 2023-12-16 06:31:43 +01:00
Kroese
a2e55c5dda fix: Configuration variables 2023-12-16 05:11:20 +01:00
Kroese
32748509ea fix: CPU configuration 2023-12-16 05:08:34 +01:00
Kroese
970a662170 fix: CPU configuration 2023-12-16 05:04:17 +01:00
Kroese
8925323a6e feat: KVM flag 2023-12-16 04:55:40 +01:00
Kroese
10915a601c feat: Display emulated CPU 2023-12-16 04:44:15 +01:00
Kroese
2cc1af19b1 fix: Configure CPU 2023-12-16 04:38:15 +01:00
Kroese
6670ca4fe1 fix: Configuration 2023-12-16 04:36:56 +01:00
Kroese
d3f77c848c feat: Emulate SSE4.2 on ARM64 2023-12-16 04:36:00 +01:00
Kroese
3f2ca67051 feat: Emulate SSE4.2 on ARM64 2023-12-16 04:22:21 +01:00
Kroese
3812101366 feat: Add KVM flag 2023-12-16 04:02:11 +01:00
Kroese
db72acfc4f feat: Install DSM 7.2 on ARM64 2023-12-16 03:12:55 +01:00
Kroese
1b3d760f5f fix: Set CPU model 2023-12-16 03:07:08 +01:00
Kroese
539f5de6d9 fix: Add qemu-user package on arm64 2023-12-16 02:45:16 +01:00
Kroese
fe2d072056 fix: Check for SSE4.2 support 2023-12-16 02:05:54 +01:00
Kroese
469ee67942 fix: Error codes 2023-12-16 01:42:02 +01:00
Kroese
95991d8f5d fix: Error codes 2023-12-16 01:36:33 +01:00
Kroese
7e12585429 fix: Error codes 2023-12-16 01:30:34 +01:00
Kroese
c335078aac feat: Display emulated CPU 2023-12-16 01:00:42 +01:00
Kroese
f1fbbb5623 feat: Configure CPU model 2023-12-16 00:35:08 +01:00
Kroese
b6502e0a38 fix: Simplify healthcheck
fix: Simplify healthcheck
2023-12-15 09:17:53 +01:00
Kroese
2fab3e5897 fix: Simplify healthcheck 2023-12-15 09:01:19 +01:00
Kroese
a4ea89d6e7 fix: Extended error message 2023-12-15 08:49:37 +01:00
Kroese
c451f253fa build: Release token (#454) 2023-12-14 03:45:32 +01:00
Kroese
03121b6c6d build: Renovate (#452) 2023-12-11 00:32:18 +01:00
Kroese
007d20c315 fix: Message variables
* fix: Message variables
2023-12-10 16:54:02 +01:00
Kroese
26d6fa9fcc feat: Improve shutdown
* feat: Improve shutdown
2023-12-10 15:58:37 +01:00
Kroese
b9f3e52ba4 feat: Improve shutdown (#448)
* feat: Improve shutdown
2023-12-10 09:22:35 +01:00
38 changed files with 2292 additions and 1030 deletions

View File

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

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

@@ -0,0 +1,62 @@
name: "\U0001F6A8 Technical issue"
description: When you're experiencing problems using the container
body:
- type: checkboxes
attributes:
label: Is there no existing issue for this?
description: Please search to see if no solution was already provided before.
options:
- label: I have searched the existing issues
required: true
- type: input
id: cpu
attributes:
label: Machine specifications
description: The processor and RAM amount in your machine.
placeholder: e.g. Intel N5105 / 16 GB
validations:
required: true
- type: input
id: os
attributes:
label: Operating system
description: The distribution and kernel version (as shown by `uname -a`).
placeholder: e.g. Ubuntu 24.04 / Kernel 6.8.0-22-generic
validations:
required: true
- type: input
id: docker
attributes:
label: Docker version
description: The version of the Docker engine (as shown by `docker -v`).
placeholder: e.g. 26.0.1
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).
validations:
required: true
- type: textarea
id: log
attributes:
label: Docker log
description: The logfile of the container (as shown by `docker logs dsm`).
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

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

@@ -0,0 +1,44 @@
name: "\U0001F680 Feature request"
description: Suggest an idea for improving the container
title: "[Feature]: "
labels: ["enhancement"]
body:
- type: checkboxes
attributes:
label: Is there no existing feature request for this?
description: Please search to see if the feature was not already requested before.
options:
- label: I have searched the existing feature requests
required: true
- 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.

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

@@ -0,0 +1,64 @@
name: "\U0001F41E Bug report"
description: Create a report to help us improve the container
title: "[Bug]: "
labels: ["bug"]
body:
- type: checkboxes
attributes:
label: Is there no existing bug report for this?
description: Please search to see if the bug was not already reported before.
options:
- label: I have searched the existing bug reports
required: true
- type: input
id: cpu
attributes:
label: Machine specifications
description: The processor and RAM amount in your machine.
placeholder: e.g. Intel N5105 / 16 GB
validations:
required: true
- type: input
id: os
attributes:
label: Operating system
description: The distribution and kernel version (as shown by `uname -a`).
placeholder: e.g. Ubuntu 24.04 / Kernel 6.8.0-22-generic
validations:
required: true
- type: input
id: docker
attributes:
label: Docker version
description: The version of the Docker engine (as shown by `docker -v`).
placeholder: e.g. 26.0.1
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).
validations:
required: true
- type: textarea
id: log
attributes:
label: Docker log
description: The logfile of the container (as shown by `docker logs dsm`).
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

@@ -1,6 +1,4 @@
{
"extends": [
"config:base",
":disableDependencyDashboard"
]
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended", ":disableDependencyDashboard"]
}

View File

@@ -8,6 +8,10 @@ on:
paths-ignore:
- '**/*.md'
- '**/*.yml'
- '**/*.js'
- '**/*.css'
- '**/*.html'
- 'web/**'
- '.gitignore'
- '.dockerignore'
- '.github/**'
@@ -74,21 +78,19 @@ jobs:
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 }}
build-args: |
VCS_REF=${GITHUB_SHA::8}
VERSION_ARG=${{ steps.meta.outputs.version }}
-
name: Create a release
uses: action-pack/github-release@v2
env:
GITHUB_TOKEN: ${{ secrets.REPO_ACCESS_TOKEN }}
with:
tag: "v${{ steps.meta.outputs.version }}"
title: "v${{ steps.meta.outputs.version }}"
token: ${{ secrets.REPO_ACCESS_TOKEN }}
-
name: Increment version variable
uses: action-pack/bump@v2
@@ -101,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 SC2028 -e SC2153 -e SC2004
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
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,18 +1,23 @@
FROM qemux/qemu-host as builder
FROM qemux/qemu-host:2.04 as builder
# FROM golang as builder
# WORKDIR /
# RUN git clone https://github.com/qemu-tools/qemu-host.git
# RUN git clone https://github.com/qemus/qemu-host.git
# WORKDIR /qemu-host/src
# RUN go mod download
# RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o /qemu-host.bin .
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 && \
RUN set -eu && extra="" && \
if [ "$TARGETPLATFORM" != "linux/amd64" ]; then extra="qemu-user"; fi && \
apt-get update && \
apt-get --no-install-recommends -y install \
jq \
tini \
@@ -21,39 +26,38 @@ RUN apt-get update && apt-get -y upgrade && \
wget \
fdisk \
unzip \
socat \
nginx \
procps \
xz-utils \
iptables \
iproute2 \
apt-utils \
dnsmasq \
fakeroot \
net-tools \
qemu-utils \
ca-certificates \
netcat-openbsd \
qemu-system-x86 \
&& apt-get clean \
&& rm -rf /var/lib/apt/lists/* /tmp/* /var/tmp/*
"$extra" && \
apt-get clean && \
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/nginx.conf /etc/nginx/sites-enabled/web.conf
VOLUME /storage
EXPOSE 22
EXPOSE 80
EXPOSE 139
EXPOSE 445
EXPOSE 5000
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
HEALTHCHECK --interval=60s --start-period=45s --retries=2 CMD /run/check.sh
ENTRYPOINT ["/usr/bin/tini", "-s", "/run/entry.sh"]

View File

@@ -1,140 +0,0 @@
#!/usr/bin/env bash
set -u
VERSION="9"
HEADER="VirtualDSM Agent"
# Functions
error () { echo -e "\E[1;31m ERROR: $1\E[0m" ; }
info () { echo -e "\E[1;34m\E[1;36m $1\E[0m" ; }
finish() {
echo " $HEADER: Shutting down.."
exit
}
function checkNMI {
local nmi
nmi=$(cat /proc/interrupts | grep NMI | sed 's/[^1-9]*//g')
if [ "$nmi" != "" ]; then
info "Received shutdown request through NMI.."
/usr/syno/sbin/synoshutdown -s > /dev/null
finish
fi
}
function downloadUpdate {
TMP="/tmp/agent.sh"
rm -f "${TMP}"
# Auto update the agent
URL="https://raw.githubusercontent.com/vdsm/virtual-dsm/master/agent/agent.sh"
remote_size=$(curl -sIk -m 4 "${URL}" | grep -i "content-length:" | tr -d " \t" | cut -d ':' -f 2)
remote_size=${remote_size//$'\r'}
[[ "$remote_size" == "" || "$remote_size" == "0" ]] && return
remote_size=$(($remote_size+0))
((remote_size<100)) && return
SCRIPT=$(readlink -f "${BASH_SOURCE[0]}")
local_size=$(stat -c%s "$SCRIPT")
local_size=$(($local_size+0))
[[ remote_size -eq local_size ]] && return
if ! curl -sfk -m 10 -o "${TMP}" "${URL}"; then
error "$HEADER: curl error ($?)" && return
fi
if [ ! -f "${TMP}" ]; then
error "$HEADER: update error, file not found.." && return
fi
line=$(head -1 "${TMP}")
if [[ "$line" != "#!/usr/bin/env bash" ]]; then
error "$HEADER: update error, invalid header: $line" && return
fi
if cmp --silent -- "${TMP}" "${SCRIPT}"; then
error "$HEADER: update file is already equal? (${local_size} / ${remote_size})" && return
fi
mv -f "${TMP}" "${SCRIPT}"
chmod 755 "${SCRIPT}"
info "$HEADER: succesfully installed update..."
}
function installPackages {
for filename in /usr/local/packages/*.spk; do
if [ -f "$filename" ]; then
BASE=$(basename "$filename" .spk)
BASE="${BASE%%-*}"
[[ $BASE == "ActiveInsight" ]] && continue
info "Installing package ${BASE}.."
/usr/syno/bin/synopkg install "$filename" > /dev/null
/usr/syno/bin/synopkg start "$BASE" > /dev/null &
rm "$filename"
fi
done
}
trap finish SIGINT SIGTERM
ts=$(date +%s%N)
echo ""
echo " Started $HEADER v$VERSION..."
checkNMI
# Install packages
first_run=false
for filename in /usr/local/packages/*.spk; do
if [ -f "$filename" ]; then
first_run=true
fi
done
if [ "$first_run" = true ]; then
installPackages
else
downloadUpdate
fi
# Wait for NMI interrupt as a shutdown signal
while true; do
checkNMI
sleep 2 & wait $!
done

View File

@@ -1,87 +0,0 @@
#!/bin/bash
PIDFILE="/var/run/agent.pid"
SCRIPT="/usr/local/bin/agent.sh"
error () { echo -e "\E[1;31m ERROR: $1\E[0m" ; }
info () { echo -e "\E[1;34m\E[1;36m $1\E[0m" ; }
status() {
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")"; then
echo 'Service running'
return 1
fi
return 0
}
start() {
if [ -f "$PIDFILE" ] && kill -0 "$(cat "$PIDFILE")"; then
echo 'Service already running'
return 1
fi
echo 'Starting agent service...'
chmod 666 /dev/ttyS0
if [ ! -f "$SCRIPT" ]; then
URL="https://raw.githubusercontent.com/vdsm/virtual-dsm/master/agent/agent.sh"
if ! curl -sfk -m 10 -o "${SCRIPT}" "${URL}"; then
error 'Failed to download agent script.' > /dev/ttyS0
rm -f "${SCRIPT}"
return 1
else
info 'Agent script was missing?' > /dev/ttyS0
fi
chmod 755 "${SCRIPT}"
fi
echo "-" > /var/lock/subsys/agent.sh
"$SCRIPT" &> /dev/ttyS0 & echo $! > "$PIDFILE"
return 0
}
stop() {
if [ ! -f "$PIDFILE" ] || ! kill -0 "$(cat "$PIDFILE")"; then
echo 'Service not running'
return 1
fi
echo 'Stopping agent service...'
chmod 666 /dev/ttyS0
info 'Stopping agent service...' > /dev/ttyS0
kill -15 "$(cat "$PIDFILE")" && rm -f "$PIDFILE"
rm -f /var/lock/subsys/agent.sh
echo 'Service stopped'
return 0
}
case "$1" in
start)
start
;;
stop)
stop
;;
status)
status
;;
restart)
stop
start
;;
*)
echo "Usage: $0 {start|stop|restart}"
exit 1
esac

15
compose.yml Normal file
View File

@@ -0,0 +1,15 @@
services:
dsm:
container_name: dsm
image: vdsm/virtual-dsm:latest
environment:
DISK_SIZE: "16G"
devices:
- /dev/kvm
cap_add:
- NET_ADMIN
ports:
- 5000:5000
volumes:
- /var/dsm:/storage
stop_grace_period: 2m

View File

@@ -1,23 +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
- /dev/net/tun
- /dev/vhost-net
device_cgroup_rules:
- 'c *:* rwm'
cap_add:
- NET_ADMIN
ports:
- 5000:5000
volumes:
- /opt/dsm:/storage
restart: on-failure
stop_grace_period: 1m

58
kubernetes.yml Normal file
View File

@@ -0,0 +1,58 @@
apiVersion: v1
kind: PersistentVolumeClaim
metadata:
name: dsm-pvc
spec:
accessModes:
- ReadWriteOnce
resources:
requests:
storage: 16Gi
---
apiVersion: v1
kind: Pod
metadata:
name: dsm
labels:
name: dsm
spec:
terminationGracePeriodSeconds: 120 # the Kubernetes default is 30 seconds and it may be not enough
containers:
- name: dsm
image: vdsm/virtual-dsm
ports:
- containerPort: 5000
protocol: TCP
resources:
limits:
devices.kubevirt.io/kvm: 1
securityContext:
privileged: true
env:
- name: RAM_SIZE
value: 1G
- name: CPU_CORES
value: "1"
- name: DISK_SIZE
value: "16G" # Kubernetes uses Gi, but DSM uses GB
volumeMounts:
- mountPath: /storage
name: storage
volumes:
- name: storage
persistentVolumeClaim:
claimName: dsm-pvc
---
apiVersion: v1
kind: Service
metadata:
name: dsm
spec:
type: NodePort
selector:
name: dsm
ports:
- name: tcp-5000
port: 5000
targetPort: 5000
nodePort: 31150

279
readme.md
View File

@@ -1,6 +1,6 @@
<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">
@@ -11,25 +11,23 @@
</div></h1>
Virtual DSM in a docker container.
Virtual DSM in a Docker container.
## Features
- Multiple disks
- KVM acceleration
- GPU passthrough
- Upgrades supported
## 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:
@@ -39,172 +37,219 @@ services:
ports:
- 5000:5000
volumes:
- /opt/dsm:/storage
restart: on-failure
stop_grace_period: 1m
- /var/dsm:/storage
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 --name dsm -p 5000:5000 --device=/dev/kvm --cap-add NET_ADMIN --stop-timeout 120 vdsm/virtual-dsm
```
Via Kubernetes:
```shell
kubectl apply -f kubernetes.yml
```
## FAQ
* ### How do I change the size of the virtual disk?
* ### How do I use it?
To expand the default size of 16 GB, locate the `DISK_SIZE` setting in your compose file and modify it to your preferred capacity:
Very simple! These are the steps:
```yaml
environment:
DISK_SIZE: "128G"
```
- Start the container and connect to [port 5000](http://localhost:5000) using your web browser.
This can also be used to resize the existing disk to a larger capacity without any data loss.
- Wait until DSM is ready, choose an username and password, and you will be taken to the desktop.
* ### How do I change the location of the virtual disk?
Enjoy your brand new machine, and don't forget to star this repo!
To change the virtual disk's location from the default Docker volume, include the following bind mount in your compose file:
* ### How do I change the storage location?
```yaml
volumes:
- /home/user/data:/storage
```
To change the storage location, include the following bind mount in your compose file:
Replace the example path `/home/user/data` with the desired storage folder.
```yaml
volumes:
- /var/dsm:/storage
```
* ### How do I add multiple disks?
Replace the example path `/var/dsm` with the desired storage folder.
To create additional disks, modify your compose file like this:
* ### How do I change the size of the disk?
```yaml
environment:
DISK2_SIZE: "32G"
DISK3_SIZE: "64G"
volumes:
- /home/example:/storage2
- /mnt/data/example:/storage3
```
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 create a growable disk?
```yaml
environment:
DISK_SIZE: "128G"
```
By default, the entire capacity of the disk is reserved in advance. To create a growable disk that only allocates space that is actually used, add the following environment variable:
This can also be used to resize the existing disk to a larger capacity without any data loss.
```yaml
environment:
DISK_FMT: "qcow2"
```
* ### How do I create a growable disk?
This can also be used to convert any existing disks to the ```qcow2``` format.
By default, the entire capacity of the disk is reserved in advance.
* ### How do I increase the amount of CPU or RAM?
To create a growable disk that only allocates space that is actually used, add the following environment variable:
By default, a single core and 1 GB of RAM are allocated to the container. To increase this, add the following environment variables:
```yaml
environment:
DISK_FMT: "qcow2"
```
```yaml
environment:
RAM_SIZE: "4G"
CPU_CORES: "4"
```
Please note that this may reduce the write performance of the disk.
* ### How do I verify if my system supports KVM?
* ### How do I add multiple disks?
To verify if your system supports KVM, run the following commands:
To create additional disks, modify your compose file like this:
```bash
sudo apt install cpu-checker
sudo kvm-ok
```
```yaml
environment:
DISK2_SIZE: "32G"
DISK3_SIZE: "64G"
volumes:
- /home/example:/storage2
- /mnt/data/example:/storage3
```
If you receive an error from `kvm-ok` indicating that KVM acceleration can't be used, check your BIOS settings.
* ### How do I pass-through a disk?
* ### How do I assign an individual IP address to the container?
It is possible to pass-through disk devices directly by adding them to your compose file in this way:
By default, the container uses bridge networking, which shares the IP address with the host.
```yaml
devices:
- /dev/disk/by-uuid/12345-12345-12345-12345-12345:/disk2
- /dev/disk/by-uuid/45678-45678-45678-45678-45678:/disk3
```
If you want to assign an individual IP address to the container, you can create a macvlan network as follows:
Make sure to bind the disk via its UUID (obtainable via `lsblk -o name,uuid`) instead of its name (`/dev/sdc`), to prevent ever binding the wrong disk when the drive letters happen to change.
```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
```
Please note that the device needs to be totally empty (without any partition table) otherwise DSM does not always format it into a volume.
Be sure to modify these values to match your local subnet.
Do NOT use this feature with the goal of sharing files from the host, they will all be lost without warning when DSM creates the volume.
Once you have created the network, change your compose file to look as follows:
* ### How do I increase the amount of CPU or RAM?
```yaml
services:
dsm:
container_name: dsm
..<snip>..
networks:
vdsm:
ipv4_address: 192.168.0.100
By default, a single CPU core and 1 GB of RAM are allocated to the container.
networks:
vdsm:
external: true
```
If there arises a need to increase this, add 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?
To verify if your system supports KVM, run the following commands:
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.
```bash
sudo apt install cpu-checker
sudo kvm-ok
```
To enable this feature, add the following lines to your compose file:
If you receive an error from `kvm-ok` indicating that KVM acceleration can't be used, check the virtualization settings in the BIOS.
```yaml
environment:
DHCP: "Y"
devices:
- /dev/vhost-net
device_cgroup_rules:
- 'c *:* rwm'
```
* ### How do I assign an individual IP address to the container?
Please note that even if you don't need DHCP, it's still recommended to enable this feature as it prevents NAT issues and increases performance by using a `macvtap` interface.
By default, the container uses bridge networking, which shares the IP address with the host.
* ### How do I passthrough the GPU?
If you want to assign an individual IP address to the container, you can create a macvlan network as follows:
To passthrough your Intel GPU, add the following lines to your compose file:
```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
environment:
GPU: "Y"
devices:
- /dev/dri
```
Be sure to modify these values to match your local subnet.
This can be used to enable the facial recognition function in Synology Photos for example.
Once you have created the network, change your compose file to look as follows:
* ### How do I install a specific version of vDSM?
```yaml
services:
dsm:
container_name: dsm
..<snip>..
networks:
vdsm:
ipv4_address: 192.168.0.100
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:
networks:
vdsm:
external: true
```
```yaml
environment:
URL: "https://global.synologydownload.com/download/DSM/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
```
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.
With this method, it is even possible to switch between different versions while keeping all your file data intact.
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.
* ### What are the differences compared to the standard DSM?
* ### How can DSM acquire an IP address from my router?
There are only two minor differences: the Virtual Machine Manager package is not available, and Surveillance Station will not include any free licenses.
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.
* ### Is this project legal?
To enable this mode, add the following lines to your compose file:
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.
```yaml
environment:
DHCP: "Y"
devices:
- /dev/vhost-net
device_cgroup_rules:
- 'c *:* rwm'
```
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.
Please note that even if you don't need DHCP, it's still recommended to enable this mode, as it prevents NAT issues and increases performance by using a `macvtap` interface. You can just set a static IP from the DSM control panel afterwards.
* ### 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
```
This can be used to enable the facial recognition function in Synology Photos for example.
* ### 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 its download URL 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 between different versions while keeping all your file data intact.
If you don't have internet access, it's also possible to skip the download by setting URL to:
```yaml
environment:
URL: "DSM_VirtualDSM_42218.pat"
```
after placing a file called `DSM_VirtualDSM_42218.pat` in your `/storage` folder.
* ### 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.
* ### 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 would 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
@@ -216,5 +261,5 @@ Only run this container on Synology hardware, any other use is not permitted by
[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

View File

@@ -1,46 +1,32 @@
#!/usr/bin/env bash
set -Eeuo pipefail
[ ! -f "/run/qemu.pid" ] && echo "QEMU not running yet.." && exit 0
[ -f "/run/qemu.count" ] && echo "QEMU is shutting down.." && exit 1
: "${NETWORK:="Y"}"
file="/run/dsm.url"
[ -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" != [Yy1]* ]] && echo "Networking is disabled.." && exit 0
if [ ! -f "$file" ]; then
file="/run/shm/dsm.url"
address="/run/shm/qemu.ip"
# Retrieve IP from guest VM for Docker healthcheck
[ ! -s "$file" ] && echo "DSM has not enabled networking yet.." && exit 1
{ json=$(curl -m 30 -sk http://127.0.0.1:2210/read?command=10); rc=$?; } || :
(( rc != 0 )) && echo "Failed to connect to guest: curl error $rc" && exit 1
{ result=$(echo "$json" | jq -r '.status'); rc=$?; } || :
(( rc != 0 )) && echo "Failed to parse response from guest: jq error $rc ( $json )" && exit 1
[[ "$result" == "null" ]] && echo "Guest returned invalid response: $json" && exit 1
if [[ "$result" != "success" ]] ; then
{ msg=$(echo "$json" | jq -r '.message'); rc=$?; } || :
echo "Guest replied $result: $msg" && exit 1
fi
{ port=$(echo "$json" | jq -r '.data.data.dsm_setting.data.http_port'); rc=$?; } || :
(( rc != 0 )) && echo "Failed to parse response from guest: jq error $rc ( $json )" && exit 1
[[ "$port" == "null" ]] && echo "Guest has not set a portnumber yet.." && exit 1
[ -z "$port" ] && echo "Guest has not set a portnumber yet.." && exit 1
{ ip=$(echo "$json" | jq -r '.data.data.ip.data[] | select((.name=="eth0") and has("ip")).ip'); rc=$?; } || :
(( rc != 0 )) && echo "Failed to parse response from guest: jq error $rc ( $json )" && exit 1
[[ "$ip" == "null" ]] && echo "Guest returned invalid response: $json" && exit 1
[ -z "$ip" ] && echo "Guest has not received an IP yet.." && exit 1
echo "$ip:$port" > $file
fi
location=$(cat "$file")
location=$(<"$file")
if ! curl -m 20 -ILfSs "http://$location/" > /dev/null; then
echo "Failed to reach http://$location"
exit 1
if [[ "$location" == "20.20"* ]]; then
ip="20.20.20.1"
port="${location##*:}"
echo "Failed to reach DSM at port $port"
else
echo "Failed to reach DSM at http://$location"
ip=$(<"$address")
fi
echo "You might need to whitelist IP $ip in the DSM firewall." && exit 1
fi
echo "Healthcheck OK"

View File

@@ -1,40 +1,29 @@
#!/bin/bash
#!/usr/bin/env bash
set -Eeuo pipefail
KVM_ERR=""
KVM_OPTS=""
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_FLAGS -smp $CPU_CORES,sockets=1,dies=1,cores=$CPU_CORES,threads=1"
MAC_OPTS="-machine type=q35,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"
if [ -e /dev/kvm ] && sh -c 'echo -n > /dev/kvm' &> /dev/null; then
if ! grep -q -e vmx -e svm /proc/cpuinfo; then
KVM_ERR="(vmx/svm disabled)"
fi
else
[ -e /dev/kvm ] && KVM_ERR="(no write access)" || KVM_ERR="(device file missing)"
fi
if [ -n "$KVM_ERR" ]; then
if [ "$ARCH" == "amd64" ]; then
error "KVM acceleration not detected $KVM_ERR, see the FAQ about this."
[[ "$DEBUG" != [Yy1]* ]] && exit 88
fi
else
KVM_OPTS=",accel=kvm -enable-kvm -cpu host"
fi
DEF_OPTS="-nographic -nodefaults -boot strict=on -display none"
RAM_OPTS=$(echo "-m $RAM_SIZE" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
CPU_OPTS="-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}"
EXTRA_OPTS="-device virtio-balloon-pci,id=balloon0,bus=pcie.0,addr=0x4"
EXTRA_OPTS="$EXTRA_OPTS -object rng-random,id=objrng0,filename=/dev/urandom"
EXTRA_OPTS="$EXTRA_OPTS -device virtio-rng-pci,rng=objrng0,id=rng0,bus=pcie.0,addr=0x1c"
if [[ "$GPU" == [Yy1]* ]] && [[ "$ARCH" == "amd64" ]]; then
DEF_OPTS="-nodefaults -boot strict=on -display egl-headless,rendernode=/dev/dri/renderD128"
DEF_OPTS="$DEF_OPTS -device virtio-vga,id=video0,max_outputs=1,bus=pcie.0,addr=0x1"
fi
ARGS="$DEF_OPTS $CPU_OPTS $RAM_OPTS $MAC_OPTS $MON_OPTS $SERIAL_OPTS $NET_OPTS $DISK_OPTS $EXTRA_OPTS $ARGUMENTS"
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
RAM_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
AVAIL_GB=$(( (RAM_AVAIL + 1073741823)/1073741824 ))
if (( (RAM_WANTED + 500000000) > RAM_AVAIL )); then
error "Your configured RAM_SIZE of $WANTED_GB GB is higher than the $AVAIL_GB GB of memory available, please set a lower value."
exit 17
fi
if (( (RAM_WANTED + 1450000000) > RAM_AVAIL )); then
warn "your configured RAM_SIZE of $WANTED_GB GB is much too close to the $AVAIL_GB GB of memory available, please set a lower value."
fi
return 0

View File

@@ -3,23 +3,25 @@ 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_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_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
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
[ ! -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
DISK_OPTS="\
-device virtio-scsi-pci,id=hw-synoboot,bus=pcie.0,addr=0xa \
-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,bus=pcie.0,addr=0xb \
-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"
@@ -34,7 +36,7 @@ fmt2ext() {
echo "img"
;;
*)
error "Unrecognized disk format: $DISK_FMT" && exit 88
error "Unrecognized disk format: $DISK_FMT" && exit 78
;;
esac
}
@@ -50,7 +52,7 @@ ext2fmt() {
echo "raw"
;;
*)
error "Unrecognized file extension: .$DISK_EXT" && exit 88
error "Unrecognized file extension: .$DISK_EXT" && exit 78
;;
esac
}
@@ -59,8 +61,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)
@@ -70,79 +72,29 @@ getSize() {
qemu-img info "$DISK_FILE" -f "$DISK_FMT" | grep '^virtual size: ' | sed 's/.*(\(.*\) bytes)/\1/'
;;
*)
error "Unrecognized disk format: $DISK_FMT" && exit 88
error "Unrecognized disk format: $DISK_FMT" && exit 78
;;
esac
}
resizeDisk() {
local DISK_FILE=$1
local CUR_SIZE=$2
local DATA_SIZE=$3
local DISK_SPACE=$4
local DISK_DESC=$5
local DISK_FMT=$6
local GB REQ FAIL SPACE SPACE_GB
isCow() {
local FS=$1
GB=$(( (CUR_SIZE + 1073741823)/1073741824 ))
info "Resizing $DISK_DESC from ${GB}G to $DISK_SPACE .."
FAIL="Could not resize $DISK_FMT file of $DISK_DESC ($DISK_FILE) from ${GB}G to $DISK_SPACE .."
if [[ "${FS,,}" == "btrfs" ]]; then
return 0
fi
REQ=$((DATA_SIZE-CUR_SIZE))
(( REQ < 1 )) && error "Shrinking disks is not supported!" && exit 84
case "${DISK_FMT,,}" in
raw)
if [[ "$ALLOCATE" == [Nn]* ]]; then
# Resize file by changing its length
if ! truncate -s "$DISK_SPACE" "$DISK_FILE"; then
error "$FAIL" && exit 85
fi
else
# Check free diskspace
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
if (( REQ > SPACE )); then
error "Not enough free space to resize $DISK_DESC to $DISK_SPACE in $DIR, it has only $SPACE_GB GB available.."
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting DISK_FMT to \"qcow2\"." && exit 84
fi
# Resize file by allocating more space
if ! fallocate -l "$DISK_SPACE" "$DISK_FILE"; then
if ! truncate -s "$DISK_SPACE" "$DISK_FILE"; then
error "$FAIL" && exit 85
fi
fi
fi
;;
qcow2)
if ! qemu-img resize -f "$DISK_FMT" "$DISK_FILE" "$DISK_SPACE" ; then
error "$FAIL" && exit 85
fi
;;
esac
return 1
}
convertDisk() {
local CONV_FLAGS="-p"
local SOURCE_FILE=$1
local SOURCE_FMT=$2
local DST_FILE=$3
local DST_FMT=$4
supportsDirect() {
local FS=$1
case "$DST_FMT" in
qcow2)
CONV_FLAGS="$CONV_FLAGS -c"
;;
esac
if [[ "${FS,,}" == "ecryptfs" ]] || [[ "${FS,,}" == "tmpfs" ]]; then
return 1
fi
# shellcheck disable=SC2086
qemu-img convert $CONV_FLAGS -f "$SOURCE_FMT" -O "$DST_FMT" -- "$SOURCE_FILE" "$DST_FILE"
return 0
}
createDisk() {
@@ -150,131 +102,387 @@ createDisk() {
local DISK_SPACE=$2
local DISK_DESC=$3
local DISK_FMT=$4
local GB FAIL SPACE SPACE_GB
local FS=$5
local DATA_SIZE DIR SPACE FA
FAIL="Could not create a $DISK_SPACE $DISK_FMT file for $DISK_DESC ($DISK_FILE)"
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
rm -f "$DISK_FILE"
if [[ "$ALLOCATE" != [Nn]* ]]; then
# Check free diskspace
DIR=$(dirname "$DISK_FILE")
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..."
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting ALLOCATE=N." && exit 76
fi
fi
html "Creating a $DISK_DESC image..."
info "Creating a $DISK_SPACE $DISK_TYPE $DISK_DESC image in $DISK_FMT format..."
local FAIL="Could not create a $DISK_TYPE $DISK_FMT $DISK_DESC image of $DISK_SPACE ($DISK_FILE)"
case "${DISK_FMT,,}" in
raw)
if isCow "$FS"; then
if ! touch "$DISK_FILE"; then
error "$FAIL" && exit 77
fi
{ chattr +C "$DISK_FILE"; } || :
fi
if [[ "$ALLOCATE" == [Nn]* ]]; then
# Create an empty file
if ! truncate -s "$DISK_SPACE" "$DISK_FILE"; then
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
rm -f "$DISK_FILE"
error "$FAIL" && exit 87
error "$FAIL" && exit 77
fi
else
# Check free diskspace
SPACE=$(df --output=avail -B 1 "$DIR" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
if (( DATA_SIZE > SPACE )); then
error "Not enough free space to create a $DISK_DESC of $DISK_SPACE in $DIR, it has only $SPACE_GB GB available.."
error "Please specify a smaller ${DISK_DESC^^}_SIZE or disable preallocation by setting DISK_FMT to \"qcow2\"." && exit 86
fi
# Create an empty file
if ! fallocate -l "$DISK_SPACE" "$DISK_FILE"; then
if ! truncate -s "$DISK_SPACE" "$DISK_FILE"; then
if ! fallocate -l "$DATA_SIZE" "$DISK_FILE"; then
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
rm -f "$DISK_FILE"
error "$FAIL" && exit 87
error "$FAIL" && exit 77
fi
fi
fi
;;
qcow2)
if ! qemu-img create -f "$DISK_FMT" -- "$DISK_FILE" "$DISK_SPACE" ; then
local DISK_PARAM="$DISK_ALLOC"
isCow "$FS" && DISK_PARAM="$DISK_PARAM,nocow=on"
[ -n "$DISK_FLAGS" ] && DISK_PARAM="$DISK_PARAM,$DISK_FLAGS"
if ! qemu-img create -f "$DISK_FMT" -o "$DISK_PARAM" -- "$DISK_FILE" "$DATA_SIZE" ; then
rm -f "$DISK_FILE"
error "$FAIL" && exit 89
error "$FAIL" && exit 70
fi
;;
esac
if isCow "$FS"; then
FA=$(lsattr "$DISK_FILE")
if [[ "$FA" != *"C"* ]]; then
error "Failed to disable COW for $DISK_DESC image $DISK_FILE on ${FS^^} filesystem (returned $FA)"
fi
fi
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 FS DIR CUR_SIZE DATA_SIZE DISK_FILE
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
CUR_SIZE=$(getSize "$DISK_FILE")
DATA_SIZE=$(numfmt --from=iec "$DISK_SPACE")
local REQ=$((DATA_SIZE-CUR_SIZE))
(( REQ < 1 )) && error "Shrinking disks is not supported yet, please increase ${DISK_DESC^^}_SIZE." && exit 71
if [[ "$ALLOCATE" != [Nn]* ]]; then
# Check free diskspace
DIR=$(dirname "$DISK_FILE")
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.."
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 ))
MSG="Resizing $DISK_DESC from ${GB}G to $DISK_SPACE..."
info "$MSG" && html "$MSG"
local FAIL="Could not resize the $DISK_TYPE $DISK_FMT $DISK_DESC image from ${GB}G to $DISK_SPACE ($DISK_FILE)"
case "${DISK_FMT,,}" in
raw)
if [[ "$ALLOCATE" == [Nn]* ]]; then
# Resize file by changing its length
if ! truncate -s "$DATA_SIZE" "$DISK_FILE"; then
error "$FAIL" && exit 75
fi
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
fi
fi
fi
;;
qcow2)
if ! qemu-img resize -f "$DISK_FMT" "--$DISK_ALLOC" "$DISK_FILE" "$DATA_SIZE" ; then
error "$FAIL" && exit 72
fi
;;
esac
return 0
}
convertDisk() {
local SOURCE_FILE=$1
local SOURCE_FMT=$2
local DST_FILE=$3
local DST_FMT=$4
local DISK_BASE=$5
local DISK_DESC=$6
local FS=$7
[ -f "$DST_FILE" ] && error "Conversion failed, destination file $DST_FILE already exists?" && exit 79
[ ! -f "$SOURCE_FILE" ] && error "Conversion failed, source file $SOURCE_FILE does not exists?" && exit 79
local TMP_FILE="$DISK_BASE.tmp"
rm -f "$TMP_FILE"
if [[ "$ALLOCATE" != [Nn]* ]]; then
local DIR CUR_SIZE SPACE
# Check free diskspace
DIR=$(dirname "$TMP_FILE")
CUR_SIZE=$(getSize "$SOURCE_FILE")
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..."
error "Please free up some disk space or disable preallocation by setting ALLOCATE=N." && exit 76
fi
fi
html "Converting $DISK_DESC to $DST_FMT..."
info "Converting $DISK_DESC to $DST_FMT, please wait until completed..."
local CONV_FLAGS="-p"
local DISK_PARAM="$DISK_ALLOC"
isCow "$FS" && DISK_PARAM="$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"
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
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"
fi
fi
fi
rm -f "$SOURCE_FILE"
mv "$TMP_FILE" "$DST_FILE"
if isCow "$FS"; then
FA=$(lsattr "$DST_FILE")
if [[ "$FA" != *"C"* ]]; then
error "Failed to disable COW for $DISK_DESC image $DST_FILE on ${FS^^} filesystem (returned $FA)"
fi
fi
html "Conversion of $DISK_DESC completed..."
info "Conversion of $DISK_DESC to $DST_FMT completed succesfully!"
return 0
}
checkFS () {
local FS=$1
local DISK_FILE=$2
local DISK_DESC=$3
local DIR FA
DISK_FILE="$DISK_BASE.$DISK_EXT"
DIR=$(dirname "$DISK_FILE")
[ ! -d "$DIR" ] && return 0
FS=$(stat -f -c %T "$DIR")
if [[ "$FS" == "overlay"* ]]; then
if [[ "${FS,,}" == "overlay"* ]]; then
info "Warning: the filesystem of $DIR is OverlayFS, this usually means it was binded to an invalid path!"
fi
if [[ "${FS,,}" == "fuse"* ]]; then
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")
if [[ "$FA" != *"C"* ]]; then
info "Warning: COW (copy on write) is not disabled for $DISK_DESC image file $DISK_FILE, this is recommended on ${FS^^} filesystems!"
fi
fi
fi
return 0
}
createDevice () {
local DISK_ID=$1
local DISK_FILE=$2
local DISK_INDEX=$3
local DISK_ADDRESS=$4
local DISK_FMT=$5
local DISK_IO=$6
local DISK_CACHE=$7
local result="-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"
result="$result \
-device virtio-scsi-pci,id=hw-$DISK_ID,iothread=io2,bus=pcie.0,addr=$DISK_ADDRESS \
-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"
echo "$result"
return 0
}
addDisk () {
local DISK_BASE=$1
local DISK_EXT=$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_ID="userdata$DISK_INDEX"
local DISK_FILE="$DISK_BASE.$DISK_EXT"
local DIR DATA_SIZE FS PREV_FMT PREV_EXT CUR_SIZE OPTS
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')
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")
if (( DATA_SIZE < 6442450944 )); then
error "Please increase ${DISK_DESC^^}_SIZE to at least 6 GB." && exit 83
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
fi
if ! [ -f "$DISK_FILE" ] ; then
local PREV_EXT PREV_FMT PREV_FILE TMP_FILE
FS=$(stat -f -c %T "$DIR")
checkFS "$FS" "$DISK_FILE" "$DISK_DESC" || exit $?
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")"
PREV_FILE="$DISK_BASE.$PREV_EXT"
PREV_EXT=$(fmt2ext "$PREV_FMT")
if [ -f "$PREV_FILE" ] ; then
info "Detected that ${DISK_DESC^^}_FMT changed from \"$PREV_FMT\" to \"$DISK_FMT\"."
info "Starting conversion of $DISK_DESC to this new format, please wait until completed..."
TMP_FILE="$DISK_BASE.tmp"
rm -f "$TMP_FILE"
if ! convertDisk "$PREV_FILE" "$PREV_FMT" "$TMP_FILE" "$DISK_FMT" ; then
rm -f "$TMP_FILE"
error "Failed to convert $DISK_DESC to $DISK_FMT format." && exit 89
fi
mv "$TMP_FILE" "$DISK_FILE"
rm -f "$PREV_FILE"
info "Conversion of $DISK_DESC completed succesfully!"
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" -gt "$CUR_SIZE" ]; then
resizeDisk "$DISK_FILE" "$CUR_SIZE" "$DATA_SIZE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" || exit $?
if (( DATA_SIZE > CUR_SIZE )); then
resizeDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
fi
else
createDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" || exit $?
createDisk "$DISK_FILE" "$DISK_SPACE" "$DISK_DESC" "$DISK_FMT" "$FS" || exit $?
fi
DISK_OPTS="$DISK_OPTS \
-device virtio-scsi-pci,id=hw-$DISK_ID,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"
OPTS=$(createDevice "$DISK_ID" "$DISK_FILE" "$DISK_INDEX" "$DISK_ADDRESS" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE")
DISK_OPTS="$DISK_OPTS $OPTS"
return 0
}
DISK_EXT="$(fmt2ext "$DISK_FMT")" || exit $?
addDevice () {
local DISK_DEV=$1
local DISK_DESC=$2
local DISK_INDEX=$3
local DISK_ADDRESS=$4
local DISK_ID="userdata$DISK_INDEX"
[ -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 OPTS
OPTS=$(createDevice "$DISK_ID" "$DISK_DEV" "$DISK_INDEX" "$DISK_ADDRESS" "raw" "$DISK_IO" "$DISK_CACHE")
DISK_OPTS="$DISK_OPTS $OPTS"
return 0
}
html "Initializing disks..."
DISK_EXT=$(fmt2ext "$DISK_FMT")
if [ -z "$ALLOCATE" ]; then
if [[ "${DISK_FMT,,}" == "raw" ]]; then
ALLOCATE="Y"
else
ALLOCATE="N"
fi
fi
if [[ "$ALLOCATE" == [Nn]* ]]; then
DISK_TYPE="growable"
DISK_ALLOC="preallocation=off"
else
DISK_TYPE="preallocated"
DISK_ALLOC="preallocation=falloc"
fi
DISK1_FILE="$STORAGE/data"
if [[ ! -f "$DISK1_FILE.img" ]] && [[ -f "$STORAGE/data${DISK_SIZE}.img" ]]; then
@@ -309,52 +517,50 @@ if [ ! -f "$DISK3_FILE.img" ]; then
fi
DISK4_FILE="/storage4/data4"
DISK5_FILE="/storage5/data5"
DISK6_FILE="/storage6/data6"
: ${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" "device" "3" "0xc" || exit $?
else
addDisk "$DISK1_FILE" "$DISK_EXT" "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,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" "device2" "4" "0xd" || exit $?
else
addDisk "$DISK2_FILE" "$DISK_EXT" "disk2" "$DISK2_SIZE" "4" "0xd" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi
return 0
}
if [ -n "$DEVICE3" ]; then
addDevice "$DEVICE3" "device3" "5" "0xe" || exit $?
else
addDisk "$DISK3_FILE" "$DISK_EXT" "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:=''}
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 $?
if [ -n "$DEVICE4" ]; then
addDevice "$DEVICE4" "device4" "6" "0xf" || exit $?
else
addDisk "$DISK4_FILE" "$DISK_EXT" "disk4" "$DISK4_SIZE" "6" "0xf" "$DISK_FMT" "$DISK_IO" "$DISK_CACHE" || exit $?
fi
html "Initialized disks successfully..."
return 0

38
src/display.sh Normal file
View File

@@ -0,0 +1,38 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Docker environment variables
: "${GPU:="N"}" # GPU passthrough
: "${VGA:="virtio"}" # VGA adaptor
: "${DISPLAY:="none"}" # Display type
if [[ "$GPU" != [Yy1]* ]] || [[ "$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"
DISPLAY_OPTS="$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
fi
fi
if [ ! -c /dev/dri/renderD128 ]; then
if mknod /dev/dri/renderD128 c 226 128; then
chmod 666 /dev/dri/renderD128
fi
fi
addPackage "xserver-xorg-video-intel" "Intel GPU drivers"
addPackage "qemu-system-modules-opengl" "OpenGL module"
return 0

View File

@@ -1,33 +1,36 @@
#!/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
. reset.sh # Initialize system
. install.sh # Run installation
. disk.sh # Initialize disks
. display.sh # Initialize graphics
. network.sh # Initialize network
. gpu.sh # Initialize graphics
. proc.sh # Initialize processor
. serial.sh # Initialize serialport
. power.sh # Configure shutdown
. config.sh # Configure arguments
trap - ERR
info "Booting ${APP}..."
[[ "$DEBUG" == [Yy1]* ]] && echo "Arguments: $ARGS" && echo
if [[ "$CONSOLE" == [Yy]* ]]; then
exec qemu-system-x86_64 -pidfile "$QEMU_PID" ${ARGS:+ $ARGS}
exit $?
exec qemu-system-x86_64 ${ARGS:+ $ARGS}
fi
set -m
(
[[ "$DEBUG" == [Yy1]* ]] && info "$VERS" && set -x
qemu-system-x86_64 ${ARGS:+ $ARGS} & echo $! > "$QEMU_PID"
{ set +x; } 2>/dev/null
)
set +m
{ qemu-system-x86_64 ${ARGS:+ $ARGS} >"$QEMU_OUT" 2>"$QEMU_LOG"; rc=$?; } || :
(( rc != 0 )) && error "$(<"$QEMU_LOG")" && exit 15
tail --pid "$(cat "$QEMU_PID")" --follow /dev/null & wait $!
terminal
tail -fn +0 "$QEMU_LOG" 2>/dev/null &
cat "$QEMU_TERM" 2>/dev/null & wait $! || :
sleep 1 & wait $!
finish 0

View File

@@ -1,24 +0,0 @@
#!/bin/bash
set -Eeuo pipefail
if [[ "$GPU" != [Yy1]* ]] || [[ "$ARCH" != "amd64" ]]; then
return 0
fi
[ ! -d /dev/dri ] && mkdir -m 755 /dev/dri
if [ ! -c /dev/dri/card0 ]; then
mknod /dev/dri/card0 c 226 0
fi
if [ ! -c /dev/dri/renderD128 ]; then
mknod /dev/dri/renderD128 c 226 128
fi
chmod 666 /dev/dri/card0
chmod 666 /dev/dri/renderD128
addPackage "xserver-xorg-video-intel" "Intel GPU drivers"
addPackage "qemu-system-modules-opengl" "OpenGL module"
return 0

View File

@@ -1,24 +1,37 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: ${URL:=''} # URL of the PAT file to be downloaded.
: ${DEV:='Y'} # Controls whether device nodes are created.
: "${URL:=""}" # URL of the PAT file to be downloaded.
if [ -f "$STORAGE"/dsm.ver ]; then
BASE=$(cat "$STORAGE/dsm.ver")
if [ -f "$STORAGE/dsm.ver" ]; then
BASE=$(<"$STORAGE/dsm.ver")
[ -z "$BASE" ] && BASE="DSM_VirtualDSM_69057"
else
# Fallback for old installs
BASE="DSM_VirtualDSM_42962"
fi
[ -n "$URL" ] && BASE=$(basename "$URL" .pat)
if [ -n "$URL" ]; 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"* ]]; then
if [ -s "$STORAGE/$BASE.pat" ]; then
URL="file://$STORAGE/$BASE.pat"
else
error "File $STORAGE/$BASE.pat does not exist!" && exit 65
fi
fi
fi
if [[ -f "$STORAGE/$BASE.boot.img" ]] && [[ -f "$STORAGE/$BASE.system.img" ]]; then
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"
@@ -29,65 +42,69 @@ DL_GLOBAL="https://global.synologydownload.com/download/DSM"
if [ -z "$DL" ]; then
[ -z "$COUNTRY" ] && setCountry
[ -z "$COUNTRY" ] && info "Warning: could not detect country to select mirror!"
[[ "${COUNTRY^^}" == "CN" ]] && DL="$DL_CHINA" || DL="$DL_GLOBAL"
fi
if [ -z "$URL" ]; then
if [ "$ARCH" == "amd64" ]; then
URL="$DL/release/7.2.1/69057-1/DSM_VirtualDSM_69057.pat"
else
URL="$DL/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
fi
fi
[ -z "$URL" ] && URL="$DL/release/7.2.1/69057-1/DSM_VirtualDSM_69057.pat"
BASE=$(basename "$URL" .pat)
BASE=$(basename "${URL%%\?*}" .pat)
: "${BASE//+/ }"; printf -v BASE '%b' "${_//%/\\x}"
BASE=$(echo "$BASE" | sed -e 's/[^A-Za-z0-9._-]/_/g')
if [[ "$URL" != "file://$STORAGE/$BASE.pat" ]]; then
rm -f "$STORAGE"/"$BASE".pat
rm -f "$STORAGE/$BASE.pat"
fi
rm -f "$STORAGE"/"$BASE".agent
rm -f "$STORAGE"/"$BASE".boot.img
rm -f "$STORAGE"/"$BASE".system.img
rm -f "$STORAGE/$BASE.agent"
rm -f "$STORAGE/$BASE.boot.img"
rm -f "$STORAGE/$BASE.system.img"
[[ "$DEBUG" == [Yy1]* ]] && set -x
# Check filesystem
MIN_ROOT=471859200
MIN_SPACE=6442450944
FS=$(stat -f -c %T "$STORAGE")
if [[ "$FS" == "overlay"* ]]; then
if [[ "${FS,,}" == "overlay"* ]]; then
info "Warning: the filesystem of $STORAGE is OverlayFS, this usually means it was binded to an invalid path!"
fi
if [[ "$FS" != "fat"* && "$FS" != "vfat"* && "$FS" != "exfat"* && \
"$FS" != "ntfs"* && "$FS" != "fuse"* && "$FS" != "msdos"* ]]; then
if [[ "${FS,,}" == "fuse"* ]]; then
info "Warning: the filesystem of $STORAGE is FUSE, this extra layer will negatively affect performance!"
fi
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)
if (( MIN_SPACE > SPACE )); then
TMP="$STORAGE/tmp"
info "Warning: the $FS filesystem of $STORAGE does not support UNIX permissions.."
SPACE_MB=$(( (SPACE + 1048575)/1048576 ))
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
fi
fi
rm -rf "$TMP" && mkdir -p "$TMP"
# Check free diskspace
ROOT_SPACE=536870912
SPACE=$(df --output=avail -B 1 / | tail -n 1)
(( MIN_ROOT > SPACE )) && error "Not enough free space in container root, need at least 450 MB available." && exit 96
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=$(df --output=avail -B 1 "$TMP" | tail -n 1)
MIN_SPACE=8589934592
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 6 GB." && exit 95
if [[ "$TMP" != "$STORAGE/tmp" ]]; then
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 6 GB." && exit 94
fi
(( 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
# Check if output is to interactive TTY
if [ -t 1 ]; then
@@ -98,30 +115,66 @@ fi
# Download the required files from the Synology website
ROOT="Y"
RD="$TMP/rd.gz"
RDC="$STORAGE/dsm.rd"
if [ ! -f "$RDC" ]; then
if [ ! -s "$RDC" ] && [[ "$URL" == "file://"* ]] && [[ "${URL,,}" == *"_42218.pat" ]]; then
info "Install: Downloading installer..."
rm -f "$RD"
rm -f "$RDC"
RD="$TMP/rd.gz"
tar --extract --file="${URL:7}" --directory="$(dirname "$RD")"/. "$(basename "$RD")"
cp "$RD" "$RDC"
fi
if [ ! -s "$RDC" ]; then
rm -f "$RD"
rm -f "$RDC"
MSG="Downloading installer..."
PRG="Downloading installer ([P])..."
info "Install: $MSG" && html "$MSG"
SIZE=5394188
POS="65627648-71021835"
VERIFY="b4215a4b213ff5154db0488f92c87864"
LOC="$DL/release/7.0.1/42218/DSM_VirtualDSM_42218.pat"
[[ "${URL,,}" == *"_42218.pat" ]] && LOC="$URL"
{ curl -r "$POS" -sfk -S -o "$RD" "$LOC"; rc=$?; } || :
(( rc != 0 )) && error "Failed to download $LOC, reason: $rc" && exit 60
/run/progress.sh "$RD" "$SIZE" "$PRG" &
{ curl -r "$POS" -sfk --connect-timeout 10 -S -o "$RD" "$LOC"; rc=$?; } || :
SUM=$(md5sum "$RD" | cut -f 1 -d " ")
fKill "progress.sh"
(( rc == 4 )) && error "Failed to download $LOC , network failure!" && exit 60
if (( rc != 0 )); then
if (( rc != 22 )) && (( rc != 56 )); then
error "Failed to download $LOC, reason: $rc" && exit 60
fi
SUM="skip"
else
SUM=$(md5sum "$RD" | cut -f 1 -d " ")
fi
if [ "$SUM" != "$VERIFY" ]; then
PAT="/install.pat"
rm "$RD"
SIZE=379637760
rm -f "$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
html "$MSG"
/run/progress.sh "$PAT" "$SIZE" "$PRG" &
{ wget "$LOC" -O "$PAT" -q --no-check-certificate --timeout=10 --show-progress "$PROGRESS"; rc=$?; } || :
fKill "progress.sh"
(( rc == 4 )) && error "Failed to download $LOC , network failure!" && exit 60
(( rc != 0 )) && error "Failed to download $LOC , reason: $rc" && exit 60
tar --extract --file="$PAT" --directory="$(dirname "$RD")"/. "$(basename "$RD")"
rm "$PAT"
@@ -135,18 +188,17 @@ 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
(( rc != 1 )) && error "Failed to unxz $RDC on $FS, reason $rc" && exit 91
if [[ "$DEV" == [Nn]* ]]; then
# Exclude dev/ from cpio extract
{ (cd "$TMP" && cpio -it < "$TMP/rd" | grep -Ev 'dev/' | while read -r entry; do cpio -idm "$entry" < "$TMP/rd" 2>/dev/null; done); rc=$?; } || :
else
{ (cd "$TMP" && cpio -idm <"$TMP/rd" 2>/dev/null); rc=$?; } || :
{ (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 on $FS, reason $rc" && exit 92
fi
(( rc != 0 )) && error "Failed to extract $RDC, reason $rc" && exit 92
mkdir -p /run/extract
rm -rf /run/extract && 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 \
@@ -172,7 +224,11 @@ fi
rm -rf "$TMP" && mkdir -p "$TMP"
info "Install: Downloading $(basename "$URL")..."
info "Install: Downloading $BASE.pat..."
MSG="Downloading DSM..."
PRG="Downloading DSM ([P])..."
html "$MSG"
PAT="/$BASE.pat"
rm -f "$PAT"
@@ -183,12 +239,21 @@ if [[ "$URL" == "file://"* ]]; then
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,,}" == *"_69057.pat" ]] && SIZE=363837333
[[ "${URL,,}" == *"_42218.pat" ]] && SIZE=379637760
/run/progress.sh "$PAT" "$SIZE" "$PRG" &
{ wget "$URL" -O "$PAT" -q --no-check-certificate --timeout=10 --show-progress "$PROGRESS"; rc=$?; } || :
fKill "progress.sh"
(( rc == 4 )) && error "Failed to download $URL , network failure!" && exit 69
(( rc != 0 )) && error "Failed to download $URL , reason: $rc" && exit 69
fi
[ ! -f "$PAT" ] && error "Failed to download $URL" && exit 69
[ ! -s "$PAT" ] && error "Failed to download $URL" && exit 69
SIZE=$(stat -c%s "$PAT")
@@ -196,17 +261,15 @@ if ((SIZE<250000000)); then
error "The specified PAT file is probably an update pack as it's too small." && exit 62
fi
MSG="Extracting downloaded image..."
info "Install: $MSG" && html "$MSG"
if { tar tf "$PAT"; } >/dev/null 2>&1; then
info "Install: Extracting downloaded image..."
tar xpf "$PAT" -C "$TMP/."
else
[ "$ARCH" != "amd64" ] && addPackage "qemu-user" "QEMU"
info "Install: Extracting downloaded image..."
export LD_LIBRARY_PATH="/run/extract"
if [ "$ARCH" == "amd64" ]; then
@@ -221,39 +284,47 @@ else
fi
HDA="$TMP/hda1"
IDB="$TMP/indexdb"
PKG="$TMP/packages"
HDP="$TMP/synohdpack_img"
rm -rf /run/extract
[ ! -f "$HDA.tgz" ] && error "The PAT file contains no OS image." && exit 64
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"
SYSTEM="$TMP/sys.img"
SYSTEM_SIZE=4954537983
SYSTEM="$STORAGE/$BASE.system.img"
rm -f "$SYSTEM"
# Check free diskspace
SPACE=$(df --output=avail -B 1 "$TMP" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
(( SYSTEM_SIZE > SPACE )) && error "Not enough free space to create a 4 GB system disk, have only $SPACE_GB GB available." && exit 87
SYSTEM_SIZE=4954537983
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
SPACE_MB=$(( (SPACE + 1048575)/1048576 ))
if ! fallocate -l "$SYSTEM_SIZE" "$SYSTEM"; then
if ! truncate -s "$SYSTEM_SIZE" "$SYSTEM"; then
rm -f "$SYSTEM" && error "Could not allocate a file for the system disk." && exit 88
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
fi
if ! touch "$SYSTEM"; then
error "Could not create file $SYSTEM for the system disk." && exit 98
fi
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."
fi
fi
# Check if file exists
[ ! -f "$SYSTEM" ] && error "System disk does not exist ($SYSTEM)" && exit 89
# Check the filesize
SIZE=$(stat -c%s "$SYSTEM")
[[ SIZE -ne SYSTEM_SIZE ]] && rm -f "$SYSTEM" && error "System disk has the wrong size: $SIZE" && exit 90
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
fi
fi
PART="$TMP/partition.fdisk"
@@ -269,50 +340,67 @@ PART="$TMP/partition.fdisk"
sfdisk -q "$SYSTEM" < "$PART"
info "Install: Extracting system partition..."
MOUNT="$TMP/system"
rm -rf "$MOUNT" && mkdir -p "$MOUNT"
mv "$HDA.tgz" "$HDA.txz"
MSG="Extracting system partition..."
info "Install: $MSG" && html "$MSG"
if [[ "$DEV" == [Nn]* ]]; then
# Exclude dev/ from tar extract
tar xpfJ "$HDA.txz" --absolute-names --exclude="dev" -C "$MOUNT/"
else
tar xpfJ "$HDA.txz" --absolute-names -C "$MOUNT/"
fi
HDA="$TMP/hda1"
IDB="$TMP/indexdb"
PKG="$TMP/packages"
HDP="$TMP/synohdpack_img"
[ ! -s "$HDA.tgz" ] && error "The PAT file contains no OS image." && exit 64
mv "$HDA.tgz" "$HDA.txz"
[ -d "$PKG" ] && mv "$PKG/" "$MOUNT/.SynoUpgradePackages/"
rm -f "$MOUNT/.SynoUpgradePackages/ActiveInsight-"*
[ -f "$HDP.txz" ] && tar xpfJ "$HDP.txz" --absolute-names -C "$MOUNT/"
[ -f "$IDB.txz" ] && tar xpfJ "$IDB.txz" --absolute-names -C "$MOUNT/usr/syno/synoman/indexdb/"
[ -s "$HDP.txz" ] && tar xpfJ "$HDP.txz" --absolute-names -C "$MOUNT/"
info "Install: Installing system partition..."
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
MSG="Installing system partition..."
mke2fs -q -t ext4 -b 4096 -d "$MOUNT/" -L "$LABEL" -F -E "offset=$OFFSET" "$SYSTEM" "$NUMBLOCKS"
if [[ "$ROOT" != [Nn]* ]]; then
tar xpfJ "$HDA.txz" --absolute-names --skip-old-files -C "$MOUNT/"
info "Install: $MSG" && html "$MSG"
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: $MSG' '\E[0m\n';\
mke2fs -q -t ext4 -b 4096 -d $MOUNT/ -L $LABEL -F -E offset=$OFFSET $SYSTEM $NUMBLOCKS"
fi
rm -rf "$MOUNT"
echo "$BASE" > "$STORAGE"/dsm.ver
echo "$BASE" > "$STORAGE/dsm.ver"
if [[ "$URL" == "file://$STORAGE/$BASE.pat" ]]; then
rm -f "$PAT"
else
mv -f "$PAT" "$STORAGE"/"$BASE".pat
mv -f "$PAT" "$STORAGE/$BASE.pat"
fi
mv -f "$BOOT" "$STORAGE"/"$BASE".boot.img
mv -f "$SYSTEM" "$STORAGE"/"$BASE".system.img
mv -f "$BOOT" "$STORAGE/$BASE.boot.img"
rm -rf "$TMP"
{ set +x; } 2>/dev/null
[[ "$DEBUG" == [Yy1]* ]] && echo
html "Installation finished successfully..."
return 0

View File

@@ -3,17 +3,21 @@ set -Eeuo pipefail
# Docker environment variables
: ${DHCP:='N'}
: ${MAC:='02:11:32:AA:BB:CC'}
: "${MAC:=""}"
: "${DHCP:="N"}"
: "${NETWORK:="Y"}"
: "${HOST_PORTS:=""}"
: ${VM_NET_TAP:='dsm'}
: ${VM_NET_DEV:='eth0'}
: ${VM_NET_MAC:="$MAC"}
: ${VM_NET_HOST:='VirtualDSM'}
: "${VM_NET_DEV:=""}"
: "${VM_NET_TAP:="dsm"}"
: "${VM_NET_MAC:="$MAC"}"
: "${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:"
# ######################################
# Functions
@@ -21,17 +25,23 @@ set -Eeuo pipefail
configureDHCP() {
# Create a macvtap network for the VM guest
# 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
{ ip link add link "$VM_NET_DEV" name "$VM_NET_TAP" address "$VM_NET_MAC" type macvtap mode bridge ; rc=$?; } || :
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 config: --cap-add NET_ADMIN" && exit 16
error "Cannot create macvtap interface. Please make sure that the network type is 'macvlan' and not 'ipvlan',"
error "that your kernel is recent (>4) and supports it, and that the container has the NET_ADMIN capability set." && exit 16
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
@@ -53,15 +63,13 @@ configureDHCP() {
{ exec 30>>"$TAP_PATH"; rc=$?; } 2>/dev/null || :
if (( rc != 0 )); then
error "Cannot create TAP interface ($rc). Please add the following docker settings to your "
error "container: --device-cgroup-rule='c $MAJOR:* rwm' --device=/dev/vhost-net" && exit 21
error "Cannot create TAP interface ($rc). $ADD_ERR --device-cgroup-rule='c *:* rwm'" && exit 21
fi
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
if (( rc != 0 )); then
error "VHOST can not be found ($rc). Please add the following "
error "docker setting to your container: --device=/dev/vhost-net" && exit 22
error "VHOST can not be found ($rc). $ADD_ERR --device=/dev/vhost-net" && exit 22
fi
NET_OPTS="-netdev tap,id=hostnet0,vhost=on,vhostfd=40,fd=30"
@@ -69,7 +77,7 @@ configureDHCP() {
return 0
}
configureDNS () {
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"
@@ -80,79 +88,136 @@ configureDNS () {
# 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=$(echo "$DNSMASQ_OPTS" | sed 's/\t/ /g' | tr -s ' ' | sed 's/^ *//')
# Add DNS entry for container
DNSMASQ_OPTS="$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}
if ! $DNSMASQ ${DNSMASQ_OPTS:+ $DNSMASQ_OPTS}; then
error "Failed to start dnsmasq, reason: $?" && exit 29
fi
{ set +x; } 2>/dev/null
[[ "$DEBUG" == [Yy1]* ]] && echo
return 0
}
configureNAT () {
getPorts() {
local list=$1
[ -z "$list" ] && echo "" && return 0
if [[ "$list" != *","* ]]; then
echo " ! --dport $list"
else
echo " -m multiport ! --dports $list"
fi
return 0
}
configureNAT() {
# Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
if mknod /dev/net/tun c 10 200; then
chmod 666 /dev/net/tun
fi
fi
if [ ! -c /dev/net/tun ]; then
error "TUN device missing. $ADD_ERR --device /dev/net/tun --cap-add NET_ADMIN" && exit 25
fi
# Check port forwarding flag
if [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
{ sysctl -w net.ipv4.ip_forward=1 > /dev/null; rc=$?; } || :
if (( rc != 0 )) || [[ $(< /proc/sys/net/ipv4/ip_forward) -eq 0 ]]; then
error "IP forwarding is disabled. $ADD_ERR --sysctl net.ipv4.ip_forward=1" && exit 24
fi
fi
local tables="The 'ip_tables' kernel module is not loaded. Try this command: sudo modprobe ip_tables iptable_nat"
local tuntap="The 'tun' kernel module is not available. Try this command: 'sudo modprobe tun' or run the container with 'privileged: true'."
# 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 "Capability NET_ADMIN has not been set most likely. Please add the "
error "following docker setting to your container: --cap-add NET_ADMIN" && exit 23
error "Failed to create bridge. $ADD_ERR --cap-add NET_ADMIN" && exit 23
fi
ip address add ${VM_NET_IP%.*}.1/24 broadcast ${VM_NET_IP%.*}.255 dev dockerbridge
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" && exit 31
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
# Add internet connection to the VM
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"
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
exclude=$(getPorts "$HOST_PORTS")
if ! iptables -t nat -A POSTROUTING -o "$VM_NET_DEV" -j MASQUERADE; then
error "$tables" && exit 30
fi
# shellcheck disable=SC2086
iptables -t nat -A PREROUTING -i "$VM_NET_DEV" -d "$IP" -p tcp${exclude} -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"
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
fi
{ set +x; } 2>/dev/null
[[ "$DEBUG" == [Yy1]* ]] && echo
# 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 "Please add the following docker setting to your container: --sysctl net.ipv4.ip_forward=1" && exit 24
fi
iptables -A POSTROUTING -t mangle -p udp --dport bootpc -j CHECKSUM --checksum-fill > /dev/null 2>&1 || true
fi
NET_OPTS="-netdev tap,ifname=$VM_NET_TAP,script=no,downscript=no,id=hostnet0"
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
(( rc == 0 )) && NET_OPTS="$NET_OPTS,vhost=on,vhostfd=40"
if [ -c /dev/vhost-net ]; then
{ exec 40>>/dev/vhost-net; rc=$?; } 2>/dev/null || :
(( rc == 0 )) && NET_OPTS="$NET_OPTS,vhost=on,vhostfd=40"
fi
configureDNS
return 0
}
closeNetwork () {
closeNetwork() {
if [[ "$DHCP" == [Yy1]* ]]; then
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
fi
[[ "$NETWORK" != [Yy1]* ]] && return 0
exec 30<&- || true
exec 40<&- || true
if [[ "$DHCP" == [Yy1]* ]]; then
@@ -161,6 +226,9 @@ closeNetwork () {
else
local pid="/var/run/dnsmasq.pid"
[ -s "$pid" ] && pKill "$(<"$pid")"
ip link set "$VM_NET_TAP" down promisc off || true
ip link delete "$VM_NET_TAP" || true
@@ -168,58 +236,115 @@ closeNetwork () {
ip link delete dockerbridge || true
fi
return 0
}
checkOS() {
local name
local os=""
name=$(uname -a)
[[ "${name,,}" == *"darwin"* ]] && os="MacOS"
[[ "${name,,}" == *"microsoft"* ]] && os="Windows"
if [ -n "$os" ]; then
error "You are using Docker Desktop for $os which does not support macvlan, please revert to bridge networking!"
return 1
fi
return 0
}
getInfo() {
if [ -z "$VM_NET_DEV" ]; then
# Automaticly detect the default network interface
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
fi
if [ -z "$VM_NET_MAC" ]; then
local file="$STORAGE/dsm.mac"
[ -s "$file" ] && VM_NET_MAC=$(<"$file")
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
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/shm/qemu.ip
return 0
}
# ######################################
# Configure Network
# ######################################
{ pkill -f server.sh || true; } 2>/dev/null
# Create the necessary file structure for /dev/net/tun
if [ ! -c /dev/net/tun ]; then
[ ! -d /dev/net ] && mkdir -m 755 /dev/net
mknod /dev/net/tun c 10 200
chmod 666 /dev/net/tun
if [[ "$NETWORK" != [Yy1]* ]]; then
NET_OPTS=""
return 0
fi
[ ! -c /dev/net/tun ] && error "TUN network interface not available..." && exit 85
# Create the necessary file structure for /dev/vhost-net
if [ ! -c /dev/vhost-net ]; then
mknod /dev/vhost-net c 10 238
chmod 660 /dev/vhost-net
fi
update-alternatives --set iptables /usr/sbin/iptables-legacy > /dev/null
update-alternatives --set ip6tables /usr/sbin/ip6tables-legacy > /dev/null
VM_NET_MAC="${VM_NET_MAC//-/:}"
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/)
getInfo
html "Initializing network..."
if [[ "$DEBUG" == [Yy1]* ]]; then
info "Container IP is $IP with gateway $GATEWAY" && echo
info "Host: $HOST IP: $IP Gateway: $GATEWAY Interface: $VM_NET_DEV MAC: $VM_NET_MAC"
[ -f /etc/resolv.conf ] && grep '^nameserver*' /etc/resolv.conf
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 [[ "$DHCP" == [Yy1]* ]]; then
! checkOS && exit 19
if [[ "$GATEWAY" == "172."* ]]; then
if [[ "$DEBUG" == [Yy1]* ]]; then
info "Warning: Are you sure the container is on a macvlan network?"
else
error "You can only enable DHCP while the container is on a macvlan network!" && exit 86
fi
warn "your gateway 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
# Display IP on port 80 and 5000
/run/server.sh 5000 /run/ip.sh &
MSG="Booting DSM instance..."
html "$MSG"
else
if [[ "$GATEWAY" != "172."* ]]; then
! checkOS && exit 19
fi
# Shutdown nginx
nginx -s stop 2> /dev/null
fWait "nginx"
# Configuration for static IP
configureNAT

View File

@@ -3,72 +3,183 @@ set -Eeuo pipefail
# Configure QEMU for graceful shutdown
API_CMD=6
API_TIMEOUT=50
API_HOST="127.0.0.1:2210"
QEMU_TERM=""
QEMU_PORT=7100
QEMU_TIMEOUT=50
QEMU_PID=/run/qemu.pid
QEMU_COUNT=/run/qemu.count
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"
rm -f "$QEMU_PID"
rm -f "$QEMU_COUNT"
if [[ "$KVM" == [Nn]* ]]; then
API_TIMEOUT=$(( API_TIMEOUT*2 ))
QEMU_TIMEOUT=$(( QEMU_TIMEOUT*2 ))
fi
_trap(){
func="$1" ; shift
for sig ; do
trap "$func $sig" "$sig"
touch "$QEMU_LOG"
_trap() {
func="$1" ; shift
for sig ; do
trap "$func $sig" "$sig"
done
}
finish() {
local pid
local reason=$1
touch "$QEMU_END"
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
[ ! -s "$QEMU_PID" ] && break
done
fi
fKill "print.sh"
fKill "host.bin"
closeNetwork
sleep 1
echo && echo " Shutdown completed!"
exit "$reason"
}
terminal() {
local dev=""
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
if [ ! -c "$dev" ]; then
dev=$(echo 'info chardev' | nc -q 1 -w 1 localhost "$QEMU_PORT" | tr -d '\000')
dev="${dev#*serial0}"
dev="${dev#*pty:}"
dev="${dev%%$'\n'*}"
dev="${dev%%$'\r'*}"
fi
if [ ! -c "$dev" ]; then
error "Device '$dev' not found!"
finish 34 && return 34
fi
QEMU_TERM="$dev"
return 0
}
_graceful_shutdown() {
local code=$?
local pid url response
set +e
local cnt response
[ ! -f "$QEMU_PID" ] && exit 130
[ -f "$QEMU_COUNT" ] && return
if [ -f "$QEMU_END" ]; then
echo && info "Received $1 signal while already shutting down..."
return
fi
echo 0 > "$QEMU_COUNT"
echo && info "Received $1 signal, shutting down..."
touch "$QEMU_END"
echo && info "Received $1 signal, sending shutdown command..."
if [ ! -s "$QEMU_PID" ]; then
echo && error "QEMU PID file does not exist?"
finish "$code" && return "$code"
fi
pid=$(<"$QEMU_PID")
if ! isAlive "$pid"; then
echo && error "QEMU process does not exist?"
finish "$code" && return "$code"
fi
# Don't send the powerdown signal because vDSM ignores ACPI signals
# echo 'system_powerdown' | nc -q 1 -w 1 localhost "${QEMU_PORT}" > /dev/null
# Send shutdown command to guest agent via serial port
response=$(curl -sk -m 30 -S http://127.0.0.1:2210/read?command=6 2>&1)
url="http://$API_HOST/read?command=$API_CMD&timeout=$API_TIMEOUT"
response=$(curl -sk -m "$(( API_TIMEOUT+2 ))" -S "$url" 2>&1)
if [[ ! "$response" =~ "\"success\"" ]]; then
if [[ "$response" =~ "\"success\"" ]]; then
echo && error "Failed to send shutdown command ( $response )."
echo && info "Virtual DSM is now ready to shutdown..."
kill -15 "$(cat "$QEMU_PID")"
pkill -f qemu-system-x86_64 || true
else
response="${response#*message\"\: \"}"
[ -z "$response" ] && response="second signal"
echo && error "Forcefully terminating because of: ${response%%\"*}"
{ kill -15 "$pid" || true; } 2>/dev/null
fi
while [ "$(cat $QEMU_COUNT)" -lt "$QEMU_TIMEOUT" ]; do
local cnt=0
# Increase the counter
echo $(($(cat $QEMU_COUNT)+1)) > "$QEMU_COUNT"
while [ "$cnt" -lt "$QEMU_TIMEOUT" ]; do
# Try to connect to qemu
if echo 'info version'| nc -q 1 -w 1 localhost "$QEMU_PORT" >/dev/null 2>&1 ; then
! isAlive "$pid" && break
sleep 1
sleep 1
cnt=$((cnt+1))
cnt="$(cat $QEMU_COUNT)/$QEMU_TIMEOUT"
[[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt)"
[[ "$DEBUG" == [Yy1]* ]] && info "Shutting down, waiting... ($cnt/$QEMU_TIMEOUT)"
fi
# Workaround for zombie pid
[ ! -s "$QEMU_PID" ] && break
done
echo && echo " Quitting..."
echo 'quit' | nc -q 1 -w 1 localhost "$QEMU_PORT" >/dev/null 2>&1 || true
if [ "$cnt" -ge "$QEMU_TIMEOUT" ]; then
echo && error "Shutdown timeout reached, aborting..."
fi
closeNetwork
return
finish "$code" && return "$code"
}
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
MON_OPTS="\
-pidfile $QEMU_PID \
-name $PROCESS,process=$PROCESS,debug-threads=on \
-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay"
MON_OPTS="-monitor telnet:localhost:$QEMU_PORT,server,nowait,nodelay"
if [[ "$CONSOLE" != [Yy]* ]]; then
MON_OPTS="$MON_OPTS -daemonize -D $QEMU_LOG"
_trap _graceful_shutdown SIGTERM SIGHUP SIGINT SIGABRT SIGQUIT
fi
return 0

View File

@@ -1,25 +1,46 @@
#!/usr/bin/env bash
set -Eeuo pipefail
: "${DHCP:="N"}"
: "${NETWORK:="Y"}"
[[ "$NETWORK" != [Yy1]* ]] && 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"
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"
while [ ! -f "$file" ]
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 [ ! -s "$file" ]
do
# Check if not shutting down
[ -f "$shutdown" ] && exit 1
sleep 3
[ -f "$file" ] && continue
# Retrieve IP from guest VM
[ -f "$shutdown" ] && exit 1
[ -s "$file" ] && break
{ json=$(curl -m 30 -sk http://127.0.0.1:2210/read?command=10); rc=$?; } || :
(( rc != 0 )) && error "Failed to connect to guest: curl error $rc" && continue
# Retrieve network info from guest VM
{ json=$(curl -m 20 -sk "$url"); rc=$?; } || :
[ -f "$shutdown" ] && exit 1
(( rc != 0 )) && error "$curl_err $rc" && continue
{ result=$(echo "$json" | jq -r '.status'); rc=$?; } || :
(( rc != 0 )) && error "Failed to parse response from guest: jq error $rc ( $json )" && continue
[[ "$result" == "null" ]] && error "Guest returned invalid response: $json" && continue
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
[[ "$result" == "null" ]] && error "$resp_err $json" && continue
if [[ "$result" != "success" ]] ; then
{ msg=$(echo "$json" | jq -r '.message'); rc=$?; } || :
@@ -27,28 +48,47 @@ do
fi
{ port=$(echo "$json" | jq -r '.data.data.dsm_setting.data.http_port'); rc=$?; } || :
(( rc != 0 )) && error "Failed to parse response from guest: jq error $rc ( $json )" && continue
[[ "$port" == "null" ]] && error "Guest returned invalid response: $json" && continue
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
[[ "$port" == "null" ]] && error "$resp_err $json" && continue
[ -z "$port" ] && continue
{ ip=$(echo "$json" | jq -r '.data.data.ip.data[] | select((.name=="eth0") and has("ip")).ip'); rc=$?; } || :
(( rc != 0 )) && error "Failed to parse response from guest: jq error $rc ( $json )" && continue
[[ "$ip" == "null" ]] && error "Guest returned invalid response: $json" && continue
[ -z "$ip" ] && continue
(( rc != 0 )) && error "$jq_err $rc ( $json )" && continue
[[ "$ip" == "null" ]] && error "$resp_err $json" && continue
if [ -z "$ip" ]; then
[[ "$DHCP" == [Yy1]* ]] && continue
ip="20.20.20.21"
fi
echo "$ip:$port" > $file
done
location=$(cat "$file")
[ -f "$shutdown" ] && exit 1
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=$(ip address show dev eth0 | grep inet | awk '/inet / { print $2 }' | cut -f1 -d/)
ip=$(<"$address")
port="${location##*:}"
if [[ "$ip" == "172."* ]]; then
@@ -64,3 +104,5 @@ info "-----------------------------------------------------------"
info " You can now login to DSM at $msg"
info "-----------------------------------------------------------"
echo "" >&2
exit 0

118
src/proc.sh Normal file
View File

@@ -0,0 +1,118 @@
#!/usr/bin/env bash
set -Eeuo pipefail
# Docker environment variables
: "${KVM:="Y"}"
: "${HOST_CPU:=""}"
: "${CPU_FLAGS:=""}"
: "${CPU_MODEL:=""}"
: "${DEF_MODEL:="qemu64"}"
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)"
else
if ! sh -c 'echo -n > /dev/kvm' &> /dev/null; then
KVM_ERR="(no write access)"
else
flags=$(sed -ne '/^flags/s/^.*: //p' /proc/cpuinfo)
if ! grep -qw "vmx\|svm" <<< "$flags"; then
KVM_ERR="(vmx/svm disabled)"
fi
fi
fi
if [ -n "$KVM_ERR" ]; then
KVM="N"
if [[ "$OSTYPE" =~ ^darwin ]]; then
warn "you are using MacOS which has no KVM support, this will cause a major loss of performance."
else
if grep -qi Microsoft /proc/version; then
warn "you are using Windows 10 which has no KVM support, this will cause a major loss of performance."
else
error "KVM acceleration not available $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
fi
fi
fi
fi
if [[ "$KVM" != [Nn]* ]]; then
CPU_FEATURES="kvm=on,l3-cache=on"
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="$CPU_FEATURES,+ssse3,+sse4.1,+sse4.2"
fi
if [ -z "$CPU_MODEL" ]; then
CPU_MODEL="host"
CPU_FEATURES="$CPU_FEATURES,migratable=no"
fi
else
KVM_OPTS=""
CPU_FEATURES="l3-cache=on"
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_FEATURES,migratable=no"
else
CPU_MODEL="$DEF_MODEL"
fi
fi
CPU_FEATURES="$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 -m 1 '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 [ -n "$HOST_CPU" ]; then
HOST_CPU="${HOST_CPU%%,*},,"
else
HOST_CPU="QEMU, Virtual CPU,"
if [ "$ARCH" == "amd64" ]; then
HOST_CPU="$HOST_CPU X86_64"
else
HOST_CPU="$HOST_CPU $ARCH"
fi
fi
return 0

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=$(printf '%.1f\n' "$((bytes*100*100/total))e-2")
size="$size%"
fi
echo "${body//(\[P\])/($size)}"> "$info"
fi
fi
sleep 1 & wait $!
done

View File

@@ -1,53 +1,190 @@
#!/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; }
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; }
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
: ${GPU:='N'} # Enable GPU passthrough
: ${DEBUG:='N'} # Enable debugging mode
: ${COUNTRY:=''} # Country code for mirror
: ${CONSOLE:='N'} # Start in 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:="1"}" # Amount of CPU cores
: "${RAM_SIZE:="1G"}" # Maximum RAM amount
: "${DISK_SIZE:="16G"}" # Initial data disk size
# Helper variables
KERNEL=$(uname -r | cut -b 1)
MINOR=$(uname -r | cut -d '.' -f2)
PROCESS="${APP,,}"
PROCESS="${PROCESS// /-}"
STORAGE="/storage"
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>"
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)
CPU=$(lscpu | grep -m 1 'Model name' | cut -f 2 -d ":" | awk '{$1=$1}1' | sed 's# @.*##g' | sed s/"(R)"//g | sed 's/[^[:alnum:] ]\+/ /g' | sed 's/ */ /g')
# 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 [ ! -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_AVAIL=$(free -b | grep -m 1 Mem: | awk '{print $7}')
RAM_TOTAL=$(free -b | grep -m 1 Mem: | awk '{print $2}')
RAM_SIZE=$(echo "${RAM_SIZE^^}" | sed 's/MB/M/g;s/GB/G/g;s/TB/T/g')
RAM_WANTED=$(numfmt --from=iec "$RAM_SIZE")
AVAIL_GB=$(( (RAM_AVAIL + 1073741823)/1073741824 ))
TOTAL_GB=$(( (RAM_TOTAL + 1073741823)/1073741824 ))
WANTED_GB=$(( (RAM_WANTED + 1073741823)/1073741824 ))
# Print system info
SYS="${SYS/-generic/}"
FS="${FS/ext2\/ext3/ext4}"
SPACE=$(df --output=avail -B 1 "$STORAGE" | tail -n 1)
SPACE_GB=$(( (SPACE + 1073741823)/1073741824 ))
echo " CPU: ${CPU} | RAM: $AVAIL_GB/$TOTAL_GB GB | DISK: $SPACE_GB GB (${FS}) | HOST: ${SYS}..."
echo
# Check memory
if (( (RAM_WANTED + 500000000) > RAM_AVAIL )); then
error "Your configured RAM_SIZE of $WANTED_GB GB is higher than the $AVAIL_GB GB of memory available, please set a lower value."
exit 17
fi
# Cleanup files
rm -f /run/dsm.url
rm -f /run/qemu.pid
rm -f /run/qemu.count
rm -f /run/shm/qemu.*
rm -f /run/shm/dsm.url
# Cleanup dirs
rm -rf /tmp/dsm
rm -rf "$STORAGE/tmp"
# Helper functions
getCountry () {
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
}
getCountry() {
local url=$1
local query=$2
local rc json result
@@ -66,18 +203,25 @@ getCountry () {
return 0
}
setCountry () {
setCountry() {
[[ "${TZ,,}" == "asia/harbin" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/beijing" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/urumqi" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/kashgar" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/shanghai" ]] && COUNTRY="CN"
[[ "${TZ,,}" == "asia/chongqing" ]] && COUNTRY="CN"
[ -z "$COUNTRY" ] && getCountry "https://api.ipapi.is" ".location.country_code"
[ -z "$COUNTRY" ] && getCountry "https://ifconfig.co/json" ".country_iso"
[ -z "$COUNTRY" ] && getCountry "https://api.ip2location.io" ".country_code"
[ -z "$COUNTRY" ] && getCountry "https://ipinfo.io/json" ".country"
[ -z "$COUNTRY" ] && getCountry "https://api.myip.com" ".cc"
return 0
}
addPackage () {
addPackage() {
local pkg=$1
local desc=$2
@@ -85,10 +229,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
@@ -96,10 +238,15 @@ 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
}
# Start webserver
cp -r /var/www/* /run/shm
html "Starting $APP for Docker..."
nginx -e stderr
return 0

View File

@@ -1,27 +1,27 @@
#!/bin/bash
#!/usr/bin/env bash
set -Eeuo pipefail
# Docker environment variables
: ${HOST_CPU:=''}
: ${HOST_MAC:=''}
: ${HOST_DEBUG:=''}
: ${HOST_SERIAL:=''}
: ${HOST_MODEL:=''}
: ${GUEST_SERIAL:=''}
: "${HOST_MAC:=""}"
: "${HOST_DEBUG:=""}"
: "${HOST_SERIAL:=""}"
: "${HOST_MODEL:=""}"
: "${GUEST_SERIAL:=""}"
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')
fi
if [ -n "$HOST_MAC" ]; then
if [ -n "$HOST_CPU" ]; then
HOST_CPU="$HOST_CPU,,"
else
if [ "$ARCH" == "amd64" ]; then
HOST_CPU="QEMU, Virtual CPU, X86_64"
else
HOST_CPU="QEMU, Virtual CPU, $ARCH"
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=()
@@ -61,11 +61,14 @@ done
# Configure serial ports
SERIAL_OPTS="\
-serial mon:stdio \
if [[ "$CONSOLE" != [Yy]* ]]; then
SERIAL_OPTS="-serial pty"
else
SERIAL_OPTS="-serial mon:stdio"
fi
SERIAL_OPTS="$SERIAL_OPTS \
-device virtio-serial-pci,id=virtio-serial0,bus=pcie.0,addr=0x3 \
-chardev pty,id=charserial0 \
-device isa-serial,chardev=charserial0,id=serial0 \
-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 $!

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();

31
web/nginx.conf Normal file
View File

@@ -0,0 +1,31 @@
server {
listen 80;
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;
}
}