1{
2 config,
3 pkgs,
4 lib,
5 ...
6}:
7
8let
9 cfg = config.virtualisation.vmware.host;
10 wrapperDir = "/run/vmware/bin"; # Perfectly fits as /usr/local/bin
11 parentWrapperDir = dirOf wrapperDir;
12 vmwareWrappers = # Needed as hardcoded paths workaround
13 let
14 mkVmwareSymlink = program: ''
15 ln -s "${config.security.wrapperDir}/${program}" $wrapperDir/${program}
16 '';
17 in
18 [
19 (mkVmwareSymlink "pkexec")
20 (mkVmwareSymlink "mount")
21 (mkVmwareSymlink "umount")
22 ];
23in
24{
25 options = with lib; {
26 virtualisation.vmware.host = {
27 enable = mkEnableOption "VMware" // {
28 description = ''
29 This enables VMware host virtualisation for running VMs.
30
31 ::: {.important}
32 `vmware-vmx` will cause kcompactd0 due to
33 `Transparent Hugepages` feature in kernel.
34 Apply `[ "transparent_hugepage=never" ]` in
35 option {option}`boot.kernelParams` to disable them.
36 :::
37
38 ::: {.note}
39 If that didn't work disable `TRANSPARENT_HUGEPAGE`,
40 `COMPACTION` configs and recompile kernel.
41 :::
42 '';
43 };
44 package = mkPackageOption pkgs "vmware-workstation" { };
45 extraPackages = mkOption {
46 type = with types; listOf package;
47 default = with pkgs; [ ];
48 description = "Extra packages to be used with VMware host.";
49 example = "with pkgs; [ ntfs3g ]";
50 };
51 extraConfig = mkOption {
52 type = types.lines;
53 default = "";
54 description = "Add extra config to /etc/vmware/config";
55 example = ''
56 # Allow unsupported device's OpenGL and Vulkan acceleration for guest vGPU
57 mks.gl.allowUnsupportedDrivers = "TRUE"
58 mks.vk.allowUnsupportedDevices = "TRUE"
59 '';
60 };
61 };
62 };
63
64 config = lib.mkIf cfg.enable {
65 boot.extraModulePackages = [ config.boot.kernelPackages.vmware ];
66 boot.extraModprobeConfig = "alias char-major-10-229 fuse";
67 boot.kernelModules = [
68 "vmw_pvscsi"
69 "vmw_vmci"
70 "vmmon"
71 "vmnet"
72 "fuse"
73 ];
74
75 environment.systemPackages = [ cfg.package ] ++ cfg.extraPackages;
76 services.printing.drivers = [ cfg.package ];
77
78 environment.etc."vmware/config".source =
79 let
80 packageConfig = "${cfg.package}/etc/vmware/config";
81 in
82 if cfg.extraConfig == "" then
83 packageConfig
84 else
85 pkgs.runCommandLocal "etc-vmware-config"
86 {
87 inherit packageConfig;
88 inherit (cfg) extraConfig;
89 }
90 ''
91 (
92 cat "$packageConfig"
93 printf "\n"
94 echo "$extraConfig"
95 ) >"$out"
96 '';
97
98 environment.etc."vmware/bootstrap".source = "${cfg.package}/etc/vmware/bootstrap";
99 environment.etc."vmware/icu".source = "${cfg.package}/etc/vmware/icu";
100 environment.etc."vmware-installer".source = "${cfg.package}/etc/vmware-installer";
101
102 # SUID wrappers
103
104 security.wrappers = {
105 vmware-vmx = {
106 setuid = true;
107 owner = "root";
108 group = "root";
109 source = "${cfg.package}/lib/vmware/bin/.vmware-vmx-wrapped";
110 };
111 };
112
113 # Services
114
115 systemd.services."vmware-wrappers" = {
116 description = "Create VMVare Wrappers";
117 wantedBy = [ "multi-user.target" ];
118 before = [
119 "vmware-authdlauncher.service"
120 "vmware-networks-configuration.service"
121 "vmware-networks.service"
122 "vmware-usbarbitrator.service"
123 ];
124 after = [ "systemd-sysusers.service" ];
125 serviceConfig.Type = "oneshot";
126 serviceConfig.RemainAfterExit = true;
127 script = ''
128 mkdir -p "${parentWrapperDir}"
129 chmod 755 "${parentWrapperDir}"
130 # We want to place the tmpdirs for the wrappers to the parent dir.
131 wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX)
132 chmod a+rx "$wrapperDir"
133 ${lib.concatStringsSep "\n" (vmwareWrappers)}
134 if [ -L ${wrapperDir} ]; then
135 # Atomically replace the symlink
136 # See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/
137 old=$(readlink -f ${wrapperDir})
138 if [ -e "${wrapperDir}-tmp" ]; then
139 rm --force --recursive "${wrapperDir}-tmp"
140 fi
141 ln --symbolic --force --no-dereference "$wrapperDir" "${wrapperDir}-tmp"
142 mv --no-target-directory "${wrapperDir}-tmp" "${wrapperDir}"
143 rm --force --recursive "$old"
144 else
145 # For initial setup
146 ln --symbolic "$wrapperDir" "${wrapperDir}"
147 fi
148 '';
149 };
150
151 systemd.services."vmware-authdlauncher" = {
152 description = "VMware Authentication Daemon";
153 serviceConfig = {
154 Type = "forking";
155 ExecStart = [ "${cfg.package}/bin/vmware-authdlauncher" ];
156 };
157 wantedBy = [ "multi-user.target" ];
158 };
159
160 systemd.services."vmware-networks-configuration" = {
161 description = "VMware Networks Configuration Generation";
162 unitConfig.ConditionPathExists = "!/etc/vmware/networking";
163 serviceConfig = {
164 UMask = "0077";
165 ExecStart = [
166 "${cfg.package}/bin/vmware-networks --postinstall vmware-player,0,1"
167 ];
168 Type = "oneshot";
169 RemainAfterExit = "yes";
170 };
171 wantedBy = [ "multi-user.target" ];
172 };
173
174 systemd.services."vmware-networks" = {
175 description = "VMware Networks";
176 after = [ "vmware-networks-configuration.service" ];
177 requires = [ "vmware-networks-configuration.service" ];
178 serviceConfig = {
179 Type = "forking";
180 ExecCondition = [ "${pkgs.kmod}/bin/modprobe vmnet" ];
181 ExecStart = [ "${cfg.package}/bin/vmware-networks --start" ];
182 ExecStop = [ "${cfg.package}/bin/vmware-networks --stop" ];
183 };
184 wantedBy = [ "multi-user.target" ];
185 };
186
187 systemd.services."vmware-usbarbitrator" = {
188 description = "VMware USB Arbitrator";
189 serviceConfig = {
190 ExecStart = [ "${cfg.package}/bin/vmware-usbarbitrator -f" ];
191 };
192 wantedBy = [ "multi-user.target" ];
193 };
194 };
195}