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