mirror of
https://github.com/NixOS/nixpkgs.git
synced 2024-11-18 03:30:45 +00:00
Merge pull request #247256 from lucasew/xrdp-audio
xrdp: add support for audio
This commit is contained in:
commit
401d16acd0
|
@ -4,14 +4,17 @@ with lib;
|
|||
|
||||
let
|
||||
cfg = config.services.xrdp;
|
||||
confDir = pkgs.runCommand "xrdp.conf" { preferLocalBuild = true; } ''
|
||||
mkdir $out
|
||||
|
||||
cp ${cfg.package}/etc/xrdp/{km-*,xrdp,sesman,xrdp_keyboard}.ini $out
|
||||
confDir = pkgs.runCommand "xrdp.conf" { preferLocalBuild = true; } ''
|
||||
mkdir -p $out
|
||||
|
||||
cp -r ${cfg.package}/etc/xrdp/* $out
|
||||
chmod -R +w $out
|
||||
|
||||
cat > $out/startwm.sh <<EOF
|
||||
#!/bin/sh
|
||||
. /etc/profile
|
||||
${lib.optionalString cfg.audio.enable "${cfg.audio.package}/libexec/pulsaudio-xrdp-module/pulseaudio_xrdp_init"}
|
||||
${cfg.defaultWindowManager}
|
||||
EOF
|
||||
chmod +x $out/startwm.sh
|
||||
|
@ -25,13 +28,17 @@ let
|
|||
|
||||
substituteInPlace $out/sesman.ini \
|
||||
--replace LogFile=xrdp-sesman.log LogFile=/dev/null \
|
||||
--replace EnableSyslog=1 EnableSyslog=0
|
||||
--replace EnableSyslog=1 EnableSyslog=0 \
|
||||
--replace startwm.sh $out/startwm.sh \
|
||||
--replace reconnectwm.sh $out/reconnectwm.sh \
|
||||
|
||||
# Ensure that clipboard works for non-ASCII characters
|
||||
sed -i -e '/.*SessionVariables.*/ a\
|
||||
LANG=${config.i18n.defaultLocale}\
|
||||
LOCALE_ARCHIVE=${config.i18n.glibcLocales}/lib/locale/locale-archive
|
||||
' $out/sesman.ini
|
||||
|
||||
${cfg.extraConfDirCommands}
|
||||
'';
|
||||
in
|
||||
{
|
||||
|
@ -44,7 +51,12 @@ in
|
|||
|
||||
enable = mkEnableOption (lib.mdDoc "xrdp, the Remote Desktop Protocol server");
|
||||
|
||||
package = mkPackageOption pkgs "xrdp" { };
|
||||
package = mkPackageOptionMD pkgs "xrdp" { };
|
||||
|
||||
audio = {
|
||||
enable = mkEnableOption (lib.mdDoc "audio support for xrdp sessions. So far it only works with PulseAudio sessions on the server side. No PipeWire support yet");
|
||||
package = mkPackageOptionMD pkgs "pulseaudio-module-xrdp" {};
|
||||
};
|
||||
|
||||
port = mkOption {
|
||||
type = types.port;
|
||||
|
@ -93,86 +105,117 @@ in
|
|||
confDir = mkOption {
|
||||
type = types.path;
|
||||
default = confDir;
|
||||
defaultText = literalMD "generated from configuration";
|
||||
description = lib.mdDoc "The location of the config files for xrdp.";
|
||||
internal = true;
|
||||
description = lib.mdDoc ''
|
||||
Configuration directory of xrdp and sesman.
|
||||
|
||||
Changes to this must be made through extraConfDirCommands.
|
||||
'';
|
||||
readOnly = true;
|
||||
};
|
||||
|
||||
extraConfDirCommands = mkOption {
|
||||
type = types.str;
|
||||
default = "";
|
||||
description = lib.mdDoc ''
|
||||
Extra commands to run on the default confDir derivation.
|
||||
'';
|
||||
example = ''
|
||||
substituteInPlace $out/sesman.ini \
|
||||
--replace LogLevel=INFO LogLevel=DEBUG \
|
||||
--replace LogFile=/dev/null LogFile=/var/log/xrdp.log
|
||||
'';
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
|
||||
###### implementation
|
||||
|
||||
config = mkIf cfg.enable {
|
||||
config = lib.mkMerge [
|
||||
(mkIf cfg.audio.enable {
|
||||
environment.systemPackages = [ cfg.audio.package ]; # needed for autostart
|
||||
|
||||
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
|
||||
hardware.pulseaudio.extraModules = [ cfg.audio.package ];
|
||||
})
|
||||
|
||||
# xrdp can run X11 program even if "services.xserver.enable = false"
|
||||
xdg = {
|
||||
autostart.enable = true;
|
||||
menus.enable = true;
|
||||
mime.enable = true;
|
||||
icons.enable = true;
|
||||
};
|
||||
(mkIf cfg.enable {
|
||||
|
||||
fonts.enableDefaultPackages = mkDefault true;
|
||||
networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];
|
||||
|
||||
systemd = {
|
||||
services.xrdp = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
description = "xrdp daemon";
|
||||
requires = [ "xrdp-sesman.service" ];
|
||||
preStart = ''
|
||||
# prepare directory for unix sockets (the sockets will be owned by loggedinuser:xrdp)
|
||||
mkdir -p /tmp/.xrdp || true
|
||||
chown xrdp:xrdp /tmp/.xrdp
|
||||
chmod 3777 /tmp/.xrdp
|
||||
|
||||
# generate a self-signed certificate
|
||||
if [ ! -s ${cfg.sslCert} -o ! -s ${cfg.sslKey} ]; then
|
||||
mkdir -p $(dirname ${cfg.sslCert}) || true
|
||||
mkdir -p $(dirname ${cfg.sslKey}) || true
|
||||
${pkgs.openssl.bin}/bin/openssl req -x509 -newkey rsa:2048 -sha256 -nodes -days 365 \
|
||||
-subj /C=US/ST=CA/L=Sunnyvale/O=xrdp/CN=www.xrdp.org \
|
||||
-config ${cfg.package}/share/xrdp/openssl.conf \
|
||||
-keyout ${cfg.sslKey} -out ${cfg.sslCert}
|
||||
chown root:xrdp ${cfg.sslKey} ${cfg.sslCert}
|
||||
chmod 440 ${cfg.sslKey} ${cfg.sslCert}
|
||||
fi
|
||||
if [ ! -s /run/xrdp/rsakeys.ini ]; then
|
||||
mkdir -p /run/xrdp
|
||||
${cfg.package}/bin/xrdp-keygen xrdp /run/xrdp/rsakeys.ini
|
||||
fi
|
||||
'';
|
||||
serviceConfig = {
|
||||
User = "xrdp";
|
||||
Group = "xrdp";
|
||||
PermissionsStartOnly = true;
|
||||
ExecStart = "${cfg.package}/bin/xrdp --nodaemon --port ${toString cfg.port} --config ${cfg.confDir}/xrdp.ini";
|
||||
};
|
||||
# xrdp can run X11 program even if "services.xserver.enable = false"
|
||||
xdg = {
|
||||
autostart.enable = true;
|
||||
menus.enable = true;
|
||||
mime.enable = true;
|
||||
icons.enable = true;
|
||||
};
|
||||
|
||||
services.xrdp-sesman = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
description = "xrdp session manager";
|
||||
restartIfChanged = false; # do not restart on "nixos-rebuild switch". like "display-manager", it can have many interactive programs as children
|
||||
serviceConfig = {
|
||||
ExecStart = "${cfg.package}/bin/xrdp-sesman --nodaemon --config ${cfg.confDir}/sesman.ini";
|
||||
ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
|
||||
fonts.enableDefaultPackages = mkDefault true;
|
||||
|
||||
environment.etc."xrdp".source = "${confDir}/*";
|
||||
|
||||
systemd = {
|
||||
services.xrdp = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
description = "xrdp daemon";
|
||||
requires = [ "xrdp-sesman.service" ];
|
||||
preStart = ''
|
||||
# prepare directory for unix sockets (the sockets will be owned by loggedinuser:xrdp)
|
||||
mkdir -p /tmp/.xrdp || true
|
||||
chown xrdp:xrdp /tmp/.xrdp
|
||||
chmod 3777 /tmp/.xrdp
|
||||
|
||||
# generate a self-signed certificate
|
||||
if [ ! -s ${cfg.sslCert} -o ! -s ${cfg.sslKey} ]; then
|
||||
mkdir -p $(dirname ${cfg.sslCert}) || true
|
||||
mkdir -p $(dirname ${cfg.sslKey}) || true
|
||||
${lib.getExe pkgs.openssl} req -x509 -newkey rsa:2048 -sha256 -nodes -days 365 \
|
||||
-subj /C=US/ST=CA/L=Sunnyvale/O=xrdp/CN=www.xrdp.org \
|
||||
-config ${cfg.package}/share/xrdp/openssl.conf \
|
||||
-keyout ${cfg.sslKey} -out ${cfg.sslCert}
|
||||
chown root:xrdp ${cfg.sslKey} ${cfg.sslCert}
|
||||
chmod 440 ${cfg.sslKey} ${cfg.sslCert}
|
||||
fi
|
||||
if [ ! -s /run/xrdp/rsakeys.ini ]; then
|
||||
mkdir -p /run/xrdp
|
||||
${pkgs.xrdp}/bin/xrdp-keygen xrdp /run/xrdp/rsakeys.ini
|
||||
fi
|
||||
'';
|
||||
serviceConfig = {
|
||||
User = "xrdp";
|
||||
Group = "xrdp";
|
||||
PermissionsStartOnly = true;
|
||||
ExecStart = "${pkgs.xrdp}/bin/xrdp --nodaemon --port ${toString cfg.port} --config ${confDir}/xrdp.ini";
|
||||
};
|
||||
};
|
||||
|
||||
services.xrdp-sesman = {
|
||||
wantedBy = [ "multi-user.target" ];
|
||||
after = [ "network.target" ];
|
||||
description = "xrdp session manager";
|
||||
restartIfChanged = false; # do not restart on "nixos-rebuild switch". like "display-manager", it can have many interactive programs as children
|
||||
serviceConfig = {
|
||||
ExecStart = "${pkgs.xrdp}/bin/xrdp-sesman --nodaemon --config ${confDir}/sesman.ini";
|
||||
ExecStop = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
|
||||
};
|
||||
};
|
||||
|
||||
};
|
||||
|
||||
};
|
||||
users.users.xrdp = {
|
||||
description = "xrdp daemon user";
|
||||
isSystemUser = true;
|
||||
group = "xrdp";
|
||||
};
|
||||
users.groups.xrdp = {};
|
||||
|
||||
users.users.xrdp = {
|
||||
description = "xrdp daemon user";
|
||||
isSystemUser = true;
|
||||
group = "xrdp";
|
||||
};
|
||||
users.groups.xrdp = {};
|
||||
security.pam.services.xrdp-sesman = {
|
||||
allowNullPassword = true;
|
||||
startSession = true;
|
||||
};
|
||||
|
||||
security.pam.services.xrdp-sesman = { allowNullPassword = true; startSession = true; };
|
||||
};
|
||||
})
|
||||
];
|
||||
|
||||
}
|
||||
|
|
|
@ -958,6 +958,7 @@ in {
|
|||
xmonad-xdg-autostart = handleTest ./xmonad-xdg-autostart.nix {};
|
||||
xpadneo = handleTest ./xpadneo.nix {};
|
||||
xrdp = handleTest ./xrdp.nix {};
|
||||
xrdp-with-audio-pulseaudio = handleTest ./xrdp-with-audio-pulseaudio.nix {};
|
||||
xscreensaver = handleTest ./xscreensaver.nix {};
|
||||
xss-lock = handleTest ./xss-lock.nix {};
|
||||
xterm = handleTest ./xterm.nix {};
|
||||
|
|
97
nixos/tests/xrdp-with-audio-pulseaudio.nix
Normal file
97
nixos/tests/xrdp-with-audio-pulseaudio.nix
Normal file
|
@ -0,0 +1,97 @@
|
|||
import ./make-test-python.nix ({ pkgs, ...} : {
|
||||
# How to interactively test this module if the audio actually works
|
||||
|
||||
# - nix run .#pulseaudio-module-xrdp.tests.xrdp-with-audio-pulseaudio.driverInteractive
|
||||
# - test_script() # launches the terminal and the tests itself
|
||||
# - server.send_monitor_command("hostfwd_add tcp::3389-:3389") # forward the RDP port to the host
|
||||
# - Connect with the RDP client you like (ex: Remmina)
|
||||
# - Don't forget to enable audio support. In remmina: Advanced -> Audio output mode to Local (default is Off)
|
||||
# - Open a browser or something that plays sound. Ex: chromium
|
||||
|
||||
name = "xrdp-with-audio-pulseaudio";
|
||||
meta = with pkgs.lib.maintainers; {
|
||||
maintainers = [ lucasew ];
|
||||
};
|
||||
|
||||
nodes = {
|
||||
server = { pkgs, ... }: {
|
||||
imports = [ ./common/user-account.nix ];
|
||||
|
||||
environment.etc."xrdp/test.txt".text = "Shouldn't conflict";
|
||||
|
||||
services.xrdp.enable = true;
|
||||
services.xrdp.audio.enable = true;
|
||||
services.xrdp.defaultWindowManager = "${pkgs.xterm}/bin/xterm";
|
||||
|
||||
hardware.pulseaudio = {
|
||||
enable = true;
|
||||
};
|
||||
|
||||
systemd.user.services.pactl-list = {
|
||||
script = ''
|
||||
while [ ! -S /tmp/.xrdp/xrdp_chansrv_audio_in_socket_* ]; do
|
||||
sleep 1
|
||||
done
|
||||
sleep 1
|
||||
${pkgs.pulseaudio}/bin/pactl list
|
||||
echo Source:
|
||||
${pkgs.pulseaudio}/bin/pactl get-default-source | tee /tmp/pulseaudio-source
|
||||
echo Sink:
|
||||
${pkgs.pulseaudio}/bin/pactl get-default-sink | tee /tmp/pulseaudio-sink
|
||||
|
||||
'';
|
||||
wantedBy = [ "default.target" ];
|
||||
};
|
||||
|
||||
networking.firewall.allowedTCPPorts = [ 3389 ];
|
||||
};
|
||||
|
||||
client = { pkgs, ... }: {
|
||||
imports = [ ./common/x11.nix ./common/user-account.nix ];
|
||||
test-support.displayManager.auto.user = "alice";
|
||||
|
||||
environment.systemPackages = [ pkgs.freerdp ];
|
||||
|
||||
services.xrdp.enable = true;
|
||||
services.xrdp.audio.enable = true;
|
||||
services.xrdp.defaultWindowManager = "${pkgs.icewm}/bin/icewm";
|
||||
|
||||
hardware.pulseaudio = {
|
||||
enable = true;
|
||||
};
|
||||
};
|
||||
};
|
||||
|
||||
testScript = { nodes, ... }: let
|
||||
user = nodes.client.config.users.users.alice;
|
||||
in ''
|
||||
start_all()
|
||||
|
||||
client.wait_for_x()
|
||||
client.wait_for_file("${user.home}/.Xauthority")
|
||||
client.succeed("xauth merge ${user.home}/.Xauthority")
|
||||
|
||||
client.sleep(5)
|
||||
|
||||
client.execute("xterm >&2 &")
|
||||
client.sleep(1)
|
||||
|
||||
client.send_chars("xfreerdp /cert-tofu /w:640 /h:480 /v:127.0.0.1 /u:${user.name} /p:${user.password} /sound\n")
|
||||
|
||||
client.sleep(10)
|
||||
|
||||
client.succeed("[ -S /tmp/.xrdp/xrdp_chansrv_audio_in_socket_* ]") # checks if it's a socket
|
||||
client.sleep(5)
|
||||
client.screenshot("localrdp")
|
||||
|
||||
client.execute("xterm >&2 &")
|
||||
client.sleep(1)
|
||||
client.send_chars("xfreerdp /cert-tofu /w:640 /h:480 /v:server /u:${user.name} /p:${user.password} /sound\n")
|
||||
client.sleep(10)
|
||||
|
||||
server.succeed("[ -S /tmp/.xrdp/xrdp_chansrv_audio_in_socket_* ]") # checks if it's a socket
|
||||
server.succeed('[ "$(cat /tmp/pulseaudio-source)" == "xrdp-source" ]')
|
||||
server.succeed('[ "$(cat /tmp/pulseaudio-sink)" == "xrdp-sink" ]')
|
||||
client.screenshot("remoterdp")
|
||||
'';
|
||||
})
|
|
@ -1,4 +1,25 @@
|
|||
{ lib, stdenv, fetchFromGitHub, applyPatches, pkg-config, which, perl, autoconf, automake, libtool, openssl, systemd, pam, fuse, libjpeg, libopus, nasm, xorg }:
|
||||
{ lib
|
||||
, stdenv
|
||||
, applyPatches
|
||||
, fetchFromGitHub
|
||||
, pkg-config
|
||||
, which
|
||||
, perl
|
||||
, autoconf
|
||||
, automake
|
||||
, libtool
|
||||
, openssl
|
||||
, systemd
|
||||
, pam
|
||||
, fuse
|
||||
, libjpeg
|
||||
, libopus
|
||||
, nasm
|
||||
, xorg
|
||||
, lame
|
||||
, pixman
|
||||
, libjpeg_turbo
|
||||
}:
|
||||
|
||||
let
|
||||
version = "0.9.23.1";
|
||||
|
@ -45,7 +66,8 @@ let
|
|||
|
||||
enableParallelBuilding = true;
|
||||
};
|
||||
xrdp = stdenv.mkDerivation rec {
|
||||
|
||||
xrdp = stdenv.mkDerivation {
|
||||
inherit version;
|
||||
pname = "xrdp";
|
||||
|
||||
|
@ -53,10 +75,25 @@ let
|
|||
|
||||
nativeBuildInputs = [ pkg-config autoconf automake which libtool nasm perl ];
|
||||
|
||||
buildInputs = [ openssl systemd pam fuse libjpeg libopus xorg.libX11 xorg.libXfixes xorg.libXrandr ];
|
||||
buildInputs = [
|
||||
fuse
|
||||
lame
|
||||
libjpeg
|
||||
libjpeg_turbo
|
||||
libopus
|
||||
openssl
|
||||
pam
|
||||
pixman
|
||||
systemd
|
||||
xorg.libX11
|
||||
xorg.libXfixes
|
||||
xorg.libXrandr
|
||||
];
|
||||
|
||||
postPatch = ''
|
||||
substituteInPlace sesman/xauth.c --replace "xauth -q" "${xorg.xauth}/bin/xauth -q"
|
||||
|
||||
substituteInPlace configure.ac --replace /usr/include/ ""
|
||||
'';
|
||||
|
||||
preConfigure = ''
|
||||
|
@ -64,7 +101,20 @@ let
|
|||
./bootstrap
|
||||
'';
|
||||
dontDisableStatic = true;
|
||||
configureFlags = [ "--with-systemdsystemunitdir=/var/empty" "--enable-ipv6" "--enable-jpeg" "--enable-fuse" "--enable-rfxcodec" "--enable-opus" "--enable-pam-config=unix" ];
|
||||
configureFlags = [
|
||||
"--with-systemdsystemunitdir=/var/empty"
|
||||
"--enable-fuse"
|
||||
"--enable-ipv6"
|
||||
"--enable-jpeg"
|
||||
"--enable-mp3lame"
|
||||
"--enable-opus"
|
||||
"--enable-pam-config=unix"
|
||||
"--enable-pixman"
|
||||
"--enable-rdpsndaudin"
|
||||
"--enable-rfxcodec"
|
||||
"--enable-tjpeg"
|
||||
"--enable-vsock"
|
||||
];
|
||||
|
||||
installFlags = [ "DESTDIR=$(out)" "prefix=" ];
|
||||
|
||||
|
@ -104,7 +154,7 @@ let
|
|||
description = "An open source RDP server";
|
||||
homepage = "https://github.com/neutrinolabs/xrdp";
|
||||
license = licenses.asl20;
|
||||
maintainers = with maintainers; [ chvp ];
|
||||
maintainers = with maintainers; [ chvp lucasew ];
|
||||
platforms = platforms.linux;
|
||||
};
|
||||
};
|
||||
|
|
|
@ -0,0 +1,64 @@
|
|||
{ stdenv
|
||||
, fetchFromGitHub
|
||||
, lib
|
||||
, nix-update-script
|
||||
, pulseaudio
|
||||
, autoreconfHook
|
||||
, pkg-config
|
||||
, nixosTests
|
||||
}:
|
||||
|
||||
stdenv.mkDerivation rec {
|
||||
pname = "pulseaudio-module-xrdp";
|
||||
version = "0.7";
|
||||
|
||||
src = fetchFromGitHub {
|
||||
owner = "neutrinolabs";
|
||||
repo = pname;
|
||||
rev = "v${version}";
|
||||
hash = "sha256-GT0kBfq6KvuiX30B9JzCiUxgSm9E6IhdJuQKKKprDCE=";
|
||||
};
|
||||
|
||||
preConfigure = ''
|
||||
tar -xvf ${pulseaudio.src}
|
||||
mv pulseaudio-* pulseaudio-src
|
||||
chmod +w -Rv pulseaudio-src
|
||||
cp ${pulseaudio.dev}/include/pulse/config.h pulseaudio-src
|
||||
configureFlags="$configureFlags PULSE_DIR=$(realpath ./pulseaudio-src)"
|
||||
'';
|
||||
|
||||
installPhase = ''
|
||||
runHook preInstall
|
||||
|
||||
mkdir -p $out/lib/pulseaudio/modules $out/libexec/pulsaudio-xrdp-module $out/etc/xdg/autostart
|
||||
install -m 755 src/.libs/*${stdenv.hostPlatform.extensions.sharedLibrary} $out/lib/pulseaudio/modules
|
||||
|
||||
install -m 755 instfiles/load_pa_modules.sh $out/libexec/pulsaudio-xrdp-module/pulseaudio_xrdp_init
|
||||
substituteInPlace $out/libexec/pulsaudio-xrdp-module/pulseaudio_xrdp_init \
|
||||
--replace pactl ${pulseaudio}/bin/pactl
|
||||
|
||||
runHook postInstall
|
||||
'';
|
||||
|
||||
nativeBuildInputs = [
|
||||
autoreconfHook
|
||||
pkg-config
|
||||
pulseaudio.dev
|
||||
];
|
||||
|
||||
passthru = {
|
||||
updateScript = nix-update-script { };
|
||||
tests = {
|
||||
inherit (nixosTests) xrdp-with-audio-pulseaudio;
|
||||
};
|
||||
};
|
||||
|
||||
meta = with lib; {
|
||||
description = "xrdp sink/source pulseaudio modules";
|
||||
homepage = "https://github.com/neutrinolabs/pulseaudio-module-xrdp";
|
||||
license = licenses.lgpl21;
|
||||
maintainers = with maintainers; [ lucasew ];
|
||||
platforms = platforms.linux;
|
||||
sourceProvenance = [ sourceTypes.fromSource ];
|
||||
};
|
||||
}
|
|
@ -31998,6 +31998,8 @@ with pkgs;
|
|||
|
||||
xrdp = callPackage ../applications/networking/remote/xrdp { };
|
||||
|
||||
pulseaudio-module-xrdp = callPackage ../applications/networking/remote/xrdp/pulseaudio-module-xrdp { };
|
||||
|
||||
freerdp = callPackage ../applications/networking/remote/freerdp {
|
||||
inherit (darwin.apple_sdk.frameworks) AudioToolbox AVFoundation Carbon Cocoa CoreMedia;
|
||||
inherit (gst_all_1) gstreamer gst-plugins-base gst-plugins-good;
|
||||
|
|
Loading…
Reference in a new issue