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".text = ''
79 ${builtins.readFile "${cfg.package}/etc/vmware/config"}
80 ${cfg.extraConfig}
81 '';
82
83 environment.etc."vmware/bootstrap".source = "${cfg.package}/etc/vmware/bootstrap";
84 environment.etc."vmware/icu".source = "${cfg.package}/etc/vmware/icu";
85 environment.etc."vmware-installer".source = "${cfg.package}/etc/vmware-installer";
86
87 # SUID wrappers
88
89 security.wrappers = {
90 vmware-vmx = {
91 setuid = true;
92 owner = "root";
93 group = "root";
94 source = "${cfg.package}/lib/vmware/bin/.vmware-vmx-wrapped";
95 };
96 };
97
98 # Services
99
100 systemd.services."vmware-wrappers" = {
101 description = "Create VMVare Wrappers";
102 wantedBy = [ "multi-user.target" ];
103 before = [
104 "vmware-authdlauncher.service"
105 "vmware-networks-configuration.service"
106 "vmware-networks.service"
107 "vmware-usbarbitrator.service"
108 ];
109 after = [ "systemd-sysusers.service" ];
110 serviceConfig.Type = "oneshot";
111 serviceConfig.RemainAfterExit = true;
112 script = ''
113 mkdir -p "${parentWrapperDir}"
114 chmod 755 "${parentWrapperDir}"
115 # We want to place the tmpdirs for the wrappers to the parent dir.
116 wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX)
117 chmod a+rx "$wrapperDir"
118 ${lib.concatStringsSep "\n" (vmwareWrappers)}
119 if [ -L ${wrapperDir} ]; then
120 # Atomically replace the symlink
121 # See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/
122 old=$(readlink -f ${wrapperDir})
123 if [ -e "${wrapperDir}-tmp" ]; then
124 rm --force --recursive "${wrapperDir}-tmp"
125 fi
126 ln --symbolic --force --no-dereference "$wrapperDir" "${wrapperDir}-tmp"
127 mv --no-target-directory "${wrapperDir}-tmp" "${wrapperDir}"
128 rm --force --recursive "$old"
129 else
130 # For initial setup
131 ln --symbolic "$wrapperDir" "${wrapperDir}"
132 fi
133 '';
134 };
135
136 systemd.services."vmware-authdlauncher" = {
137 description = "VMware Authentication Daemon";
138 serviceConfig = {
139 Type = "forking";
140 ExecStart = [ "${cfg.package}/bin/vmware-authdlauncher" ];
141 };
142 wantedBy = [ "multi-user.target" ];
143 };
144
145 systemd.services."vmware-networks-configuration" = {
146 description = "VMware Networks Configuration Generation";
147 unitConfig.ConditionPathExists = "!/etc/vmware/networking";
148 serviceConfig = {
149 UMask = "0077";
150 ExecStart = [
151 "${cfg.package}/bin/vmware-networks --postinstall vmware-player,0,1"
152 ];
153 Type = "oneshot";
154 RemainAfterExit = "yes";
155 };
156 wantedBy = [ "multi-user.target" ];
157 };
158
159 systemd.services."vmware-networks" = {
160 description = "VMware Networks";
161 after = [ "vmware-networks-configuration.service" ];
162 requires = [ "vmware-networks-configuration.service" ];
163 serviceConfig = {
164 Type = "forking";
165 ExecCondition = [ "${pkgs.kmod}/bin/modprobe vmnet" ];
166 ExecStart = [ "${cfg.package}/bin/vmware-networks --start" ];
167 ExecStop = [ "${cfg.package}/bin/vmware-networks --stop" ];
168 };
169 wantedBy = [ "multi-user.target" ];
170 };
171
172 systemd.services."vmware-usbarbitrator" = {
173 description = "VMware USB Arbitrator";
174 serviceConfig = {
175 ExecStart = [ "${cfg.package}/bin/vmware-usbarbitrator -f" ];
176 };
177 wantedBy = [ "multi-user.target" ];
178 };
179 };
180}