{ config, pkgs, lib, ... }:
let
cfg = config.virtualisation.vmware.host;
wrapperDir = "/run/vmware/bin"; # Perfectly fits as /usr/local/bin
parentWrapperDir = dirOf wrapperDir;
vmwareWrappers = # Needed as hardcoded paths workaround
let mkVmwareSymlink =
program:
''
ln -s "${config.security.wrapperDir}/${program}" $wrapperDir/${program}
'';
in
[
(mkVmwareSymlink "pkexec")
(mkVmwareSymlink "mount")
(mkVmwareSymlink "umount")
];
in
{
options = with lib; {
virtualisation.vmware.host = {
enable = mkEnableOption "VMware" // {
description = ''
This enables VMware host virtualisation for running VMs.
vmware-vmx will cause kcompactd0 due to
Transparent Hugepages feature in kernel.
Apply [ "transparent_hugepage=never" ] in
option to disable them.
If that didn't work disable TRANSPARENT_HUGEPAGE,
COMPACTION configs and recompile kernel.
'';
};
package = mkOption {
type = types.package;
default = pkgs.vmware-workstation;
defaultText = literalExpression "pkgs.vmware-workstation";
description = "VMware host virtualisation package to use";
};
extraPackages = mkOption {
type = with types; listOf package;
default = with pkgs; [ ];
description = "Extra packages to be used with VMware host.";
example = "with pkgs; [ ntfs3g ]";
};
extraConfig = mkOption {
type = types.lines;
default = "";
description = "Add extra config to /etc/vmware/config";
example = ''
# Allow unsupported device's OpenGL and Vulkan acceleration for guest vGPU
mks.gl.allowUnsupportedDrivers = "TRUE"
mks.vk.allowUnsupportedDevices = "TRUE"
'';
};
};
};
config = lib.mkIf cfg.enable {
boot.extraModulePackages = [ config.boot.kernelPackages.vmware ];
boot.extraModprobeConfig = "alias char-major-10-229 fuse";
boot.kernelModules = [ "vmw_pvscsi" "vmw_vmci" "vmmon" "vmnet" "fuse" ];
environment.systemPackages = [ cfg.package ] ++ cfg.extraPackages;
services.printing.drivers = [ cfg.package ];
environment.etc."vmware/config".text = ''
${builtins.readFile "${cfg.package}/etc/vmware/config"}
${cfg.extraConfig}
'';
environment.etc."vmware/bootstrap".source = "${cfg.package}/etc/vmware/bootstrap";
environment.etc."vmware/icu".source = "${cfg.package}/etc/vmware/icu";
environment.etc."vmware-installer".source = "${cfg.package}/etc/vmware-installer";
# SUID wrappers
security.wrappers = {
vmware-vmx = {
setuid = true;
owner = "root";
group = "root";
source = "${cfg.package}/lib/vmware/bin/.vmware-vmx-wrapped";
};
};
###### wrappers activation script
system.activationScripts.vmwareWrappers =
lib.stringAfter [ "specialfs" "users" ]
''
mkdir -p "${parentWrapperDir}"
chmod 755 "${parentWrapperDir}"
# We want to place the tmpdirs for the wrappers to the parent dir.
wrapperDir=$(mktemp --directory --tmpdir="${parentWrapperDir}" wrappers.XXXXXXXXXX)
chmod a+rx "$wrapperDir"
${lib.concatStringsSep "\n" (vmwareWrappers)}
if [ -L ${wrapperDir} ]; then
# Atomically replace the symlink
# See https://axialcorps.com/2013/07/03/atomically-replacing-files-and-directories/
old=$(readlink -f ${wrapperDir})
if [ -e "${wrapperDir}-tmp" ]; then
rm --force --recursive "${wrapperDir}-tmp"
fi
ln --symbolic --force --no-dereference "$wrapperDir" "${wrapperDir}-tmp"
mv --no-target-directory "${wrapperDir}-tmp" "${wrapperDir}"
rm --force --recursive "$old"
else
# For initial setup
ln --symbolic "$wrapperDir" "${wrapperDir}"
fi
'';
# Services
systemd.services."vmware-authdlauncher" = {
description = "VMware Authentification Daemon";
serviceConfig = {
Type = "forking";
ExecStart = [ "${cfg.package}/bin/vmware-authdlauncher" ];
};
wantedBy = [ "multi-user.target" ];
};
systemd.services."vmware-networks-configuration" = {
description = "VMware Networks Configuration Generation";
unitConfig.ConditionPathExists = "!/etc/vmware/networking";
serviceConfig = {
UMask = "0077";
ExecStart = [
"${cfg.package}/bin/vmware-networks --postinstall vmware-player,0,1"
];
Type = "oneshot";
RemainAfterExit = "yes";
};
wantedBy = [ "multi-user.target" ];
};
systemd.services."vmware-networks" = {
description = "VMware Networks";
after = [ "vmware-networks-configuration.service" ];
requires = [ "vmware-networks-configuration.service" ];
serviceConfig = {
Type = "forking";
ExecCondition = [ "${pkgs.kmod}/bin/modprobe vmnet" ];
ExecStart = [ "${cfg.package}/bin/vmware-networks --start" ];
ExecStop = [ "${cfg.package}/bin/vmware-networks --stop" ];
};
wantedBy = [ "multi-user.target" ];
};
systemd.services."vmware-usbarbitrator" = {
description = "VMware USB Arbitrator";
serviceConfig = {
ExecStart = [ "${cfg.package}/bin/vmware-usbarbitrator -f" ];
};
wantedBy = [ "multi-user.target" ];
};
};
}