Unverified Commit 401d16ac authored by Thiago Kenji Okada's avatar Thiago Kenji Okada Committed by GitHub
Browse files

Merge pull request #247256 from lucasew/xrdp-audio

xrdp: add support for audio
parents 73f76f73 cf26222c
Loading
Loading
Loading
Loading
+112 −69
Original line number Diff line number Diff line
@@ -4,14 +4,17 @@ with lib;

let
  cfg = config.services.xrdp;

  confDir = pkgs.runCommand "xrdp.conf" { preferLocalBuild = true; } ''
    mkdir $out
    mkdir -p $out

    cp ${cfg.package}/etc/xrdp/{km-*,xrdp,sesman,xrdp_keyboard}.ini $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,16 +105,40 @@ 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

      hardware.pulseaudio.extraModules = [ cfg.audio.package ];
    })

    (mkIf cfg.enable {

      networking.firewall.allowedTCPPorts = mkIf cfg.openFirewall [ cfg.port ];

@@ -116,6 +152,8 @@ in

      fonts.enableDefaultPackages = mkDefault true;

      environment.etc."xrdp".source = "${confDir}/*";

      systemd = {
        services.xrdp = {
          wantedBy = [ "multi-user.target" ];
@@ -132,7 +170,7 @@ in
            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 \
              ${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}
@@ -141,14 +179,14 @@ in
            fi
            if [ ! -s /run/xrdp/rsakeys.ini ]; then
              mkdir -p /run/xrdp
            ${cfg.package}/bin/xrdp-keygen xrdp /run/xrdp/rsakeys.ini
              ${pkgs.xrdp}/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";
            ExecStart = "${pkgs.xrdp}/bin/xrdp --nodaemon --port ${toString cfg.port} --config ${confDir}/xrdp.ini";
          };
        };

@@ -158,7 +196,7 @@ in
          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";
            ExecStart = "${pkgs.xrdp}/bin/xrdp-sesman --nodaemon --config ${confDir}/sesman.ini";
            ExecStop  = "${pkgs.coreutils}/bin/kill -INT $MAINPID";
          };
        };
@@ -172,7 +210,12 @@ in
      };
      users.groups.xrdp = {};

    security.pam.services.xrdp-sesman = { allowNullPassword = true; startSession = true; };
      security.pam.services.xrdp-sesman = {
        allowNullPassword = true;
        startSession = true;
      };

    })
  ];

}
+1 −0
Original line number Diff line number Diff line
@@ -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 −0
Original line number Diff line number Diff line
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")
  '';
})
+55 −5
Original line number Diff line number Diff line
{ 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;
    };
  };
+64 −0
Original line number Diff line number Diff line
{ 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 ];
  };
}
Loading