How to build Bareos-FD, which requires a python module

Similarly to Should I use buildPythonApplication or mkDerivation here?, I’m looking to compile a program, and the cmake instructions end up attempting to install a python shared object into the system python’s site-packages directory:

CMake Error at core/src/plugins/filed/python/cmake_install.cmake:92 (file):
  file INSTALL cannot copy file
  "/build/source/build/core/src/plugins/filed/python/python3modules/bareosfd.cpython-312-x86_64-linux-gnu.so"
  to
  "/nix/store/99hl269v1igvjbp1znfk5jcarhzgy822-python3-3.12.8/lib/python3.12/site-packages/bareosfd.cpython-312-x86_64-linux-gnu.so":
  Permission denied.
Call Stack (most recent call first):
  core/src/plugins/filed/cmake_install.cmake:47 (include)
  core/src/plugins/cmake_install.cmake:47 (include)
  core/src/cmake_install.cmake:67 (include)
  core/cmake_install.cmake:76 (include)
  cmake_install.cmake:47 (include)

How we got here

Long story short, I’m looking to have a local “filedaemon” on my nixos server, which will allow Bareos (a backup program) to take snapshots of the disk incrementally.

I’m compiling it from here: GitHub - bareos/bareos at bareos-24

It has CMAKE files, and I’ve been able to work around most of the issues, however, when it gets to the installation of the shared object (shown above), it seems to be unresponsive to any compile flags (I’ve tried plugindir, Python3_SITEARCH) and while I’ve had success minimizing the build with several (client-only, ENABLE_WEBUI=OFF, etc.) I’ve only been ever able to build it fully with ENABLE_PYTHON=OFF.

So I’m trying to figure out if there’s a best-practices pattern for NixOS for when I need to build a package that needs to build python modules and have it overlaid into the execution runtime and/or path of the application. But especially, I’d be very much appreciative of any nudge in the right direction so that I can get this building on my systems. Thanks!

You could try building the python program under /bareos/python-bareos in a separate derivation via buildPythonPackage, and then include one into the other via the install phase, or such.

I’m not sure if the python project depends on the C project or vice versa, or if they are separate packages anyways, in which case it wouldn’t matter.

I took a chance that they were in fact separate, and especially leaned heavily on the fact that I only needed the mariabackup.py file (and imports) present on that particular FD, so here’s what I ended up doing - which was to download the prebuilt python package from PYPI, and use that in the build:

{ config, pkgs, lib, ... }:                                                                                                                                                                                                              

let                                                                                                                                                                                                                                      
  # Define the tl-expected library                                                                                                                                                                                                       
  tlExpected = pkgs.stdenv.mkDerivation {                                                                                                                                                                                                
    pname = "tl-expected";                                                                                                                                                                                                               
    version = "1.1.0";                                                                                                                                                                                                                   
    src = pkgs.fetchFromGitHub {                                                                                                                                                                                                         
      owner = "TartanLlama";                                                                                                                                                                                                             
      repo = "expected";                                                                                                                                                                                                                 
      rev = "v1.1.0";                                                                                                                                                                                                                    
      hash = "sha256-AuRU8VI5l7Th9fJ5jIc/6mPm0Vqbbt6rY8QCCNDOU50=";                                                                                                                                                                      
    };                                                                                                                                                                                                                                   
    dontConfigure = true;                                                                                                                                                                                                                
    dontBuild = true;                                                                                                                                                                                                                    
    installPhase = ''                                                                                                                                                                                                                    
      mkdir -p $out/include                                                                                                                                                                                                              
      cp -r include/tl $out/include/                                                                                                                                                                                                     

      mkdir -p $out/lib/cmake/tl-expected                                                                                                                                                                                                
      cat > $out/lib/cmake/tl-expected/tl-expected-config.cmake <<'EOF'                                                                                                                                                                  
add_library(tl::expected INTERFACE IMPORTED)                                                                                                                                                                                             
get_filename_component(_self_dir "''${CMAKE_CURRENT_LIST_FILE}" PATH)                                                                                                                                                                    
target_include_directories(                                                                                                                                                                                                              
  tl::expected INTERFACE "''${_self_dir}/../../include"                                                                                                                                                                                  
)                                                                                                                                                                                                                                        
EOF                                                                                                                                                                                                                                      
                                                                                                                                                                                                                                         
      cat > "$out/lib/cmake/tl-expected/tl-expected-config-version.cmake" <<'VER'                                                                                                                                                        
set(PACKAGE_VERSION "1.1.0")                                                                                                                                                                                                             
if(PACKAGE_VERSION VERSION_LESS PACKAGE_FIND_VERSION)                                                                                                                                                                                    
  set(PACKAGE_VERSION_COMPATIBLE FALSE)                                                                                                                                                                                                  
else()                                                                                                                                                                                                                                   
  set(PACKAGE_VERSION_COMPATIBLE TRUE)                                                                                                                                                                                                   
  if(PACKAGE_VERSION VERSION_EQUAL PACKAGE_FIND_VERSION)                                                                                                                                                                                 
    set(PACKAGE_VERSION_EXACT TRUE)                                                                                                                                                                                                      
  endif()                                                                                                                                                                                                                                
endif()                                                                                                                                                                                                                                  
VER                                                                                                                                                                                                                                      
    '';                                                                                                                                                                                                                                  
  };                                                                                                                                                                                                                                     
                                                                                                                                                                                                                                         
  # Create a Python environment with the python-bareos package from PyPI                                                                                                                                                                 
  pythonWithBareos = pkgs.python3.withPackages (ps: [                                                                                                                                                                                    
    (ps.buildPythonPackage rec {                                                                                                                                                                                                         
      pname = "python_bareos";                                                                                                                                                                                                           
      version = "24.0.2";                                                                                                                                                                                                                
      src = ps.fetchPypi {                                                                                                                                                                                                               
        inherit pname version;                                                                                                                                                                                                           
        # You'll need to fill in the correct SHA256 hash                                                                                                                                                                                 
        # To get this, you can temporarily set this to lib.fakeSha256,                                                                                                                                                                   
        # run the build once, and copy the correct hash from the error message                                                                                                                                                           
        sha256 = "sha256-Ru9hEdr69IhGV09bL3AtfnqNvmY01fMi53h1Mh4qZ8g=";                                                                                                                                                                  
      };                                                                                                                                                                                                                                 
      doCheck = false;                                                                                                                                                                                                                   
    })                                                                                                                                                                                                                                   
  ]);                                                                                                                                                                                                                                    
                                                                                                                                                                                                                                         
  # Build bareos-fd without Python support in the build                                                                                                                                                                                  
  bareosFd = pkgs.stdenv.mkDerivation rec {                                                                                                                                                                                              
    pname = "bareos-fd";                                                                                                                                                                                                                 
    version = "24.0.2";                                                                                                                                                                                                                  
    src = pkgs.fetchFromGitHub {                                                                                                                                                                                                         
      owner = "bareos";                                                                                                                                                                                                                  
      repo = "bareos";                                                                                                                                                                                                                   
      rev = "Release/${version}";                                                                                                                                                                                                        
      hash = "sha256-QVcUwZHguQUmtfZUpvx/1fJVRSErrawJKsCGlJXDnug=";                                                                                                                                                                      
    };                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                         
    nativeBuildInputs = with pkgs; [                                                                                                                                                                                                     
      cmake                                                                                                                                                                                                                              
      gitMinimal                                                                                                                                                                                                                         
      pkg-config                                                                                                                                                                                                                         
      makeWrapper                                                                                                                                                                                                                        
    ];                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                         
    buildInputs = with pkgs; [                                                                                                                                                                                                           
      boost openssl readline zlib lzo acl libcap cli11                                                                                                                                                                                   
      fmt microsoft-gsl utf8cpp xxHash tlExpected mariadb systemd                                                                                                                                                                        
      python3                                                                                                                                                                                                                            
    ];                                                                                                                                                                                                                                   
                                                                                                                                                                                                                                         
    cmakeFlags = [                                                                                                                                                                                                                       
      "-DENABLE_FD_ONLY=ON"                                                                                                                                                                                                              
      "-DENABLE_PYTHON=OFF"                                                                                                                                                                                                              
      "-DCMAKE_INSTALL_PREFIX=${placeholder "out"}"                                                                                                                                                                                      
      "-DCPM_USE_LOCAL_PACKAGES=ON"                                                                                                                                                                                                      
      "-DCPM_LOCAL_PACKAGES_ONLY=ON"                                                                                                                                                                                                     
      #"-DUSE_SYSTEM_TL_EXPECTED=ON"                                                                                                                                                                                                     
      #"-DCPM_SOURCE_CACHE=${tlExpected}"                                                                                                                                                                                                
      "-Dclient-only=ON"                                                                                                                                                                                                                 
      "-DENABLE_WEBUI=OFF"                                                                                                                                                                                                               
      "-DENABLE_GFAPI_FD=OFF"                                                                                                                                                                                                            
      "-DENABLE_JANSSON=OFF"                                                                                                                                                                                                             
      "-DENABLE_BCONSOLE=OFF"                                                                                                                                                                                                            
      "-DSYSTEMD_UNITDIR=$out/lib/systemd/system"                                                                                                                                                                                        
      "-Dsystemd=ON"                                                                                                                                                                                                                     
    ];                                                                                                                                                                                                                                   

    # Create plugins directory & install the mariabackup.py plugin in there                                                                                                                                                              
    postInstall = ''                                                                                                                                                                                                                     
      mkdir -p $out/lib/bareos/plugins                                                                                                                                                                                                   
                                                                                                                                                                                                                                         
      # copy the python plugin that CMake left in the build tree                                                                                                                                                                         
      cp ../core/src/plugins/filed/python/mariabackup/bareos-fd-mariabackup.py \                                                                                                                                                         
         ../core/src/plugins/filed/python/pyfiles/BareosFdPluginLocalFilesBaseclass.py \                                                                                                                                                 
         ../core/src/plugins/filed/python/pyfiles/BareosFdWrapper.py \                                                                                                                                                                   
         ../core/src/plugins/filed/python/pyfiles/BareosFdPluginBaseclass.py \                                                                                                                                                           
        $out/lib/bareos/plugins/                                                                                                                                                                                                         
                                                                                                                                                                                                                                         
    '';                                                                                                                                                                                                                                  
                                                                                                                                                                                                                                         
    # Wrap executables                                                                                                                                                                                                                   
    postFixup = ''                                                                                                                                                                                                                       
      for prog in $out/bin/* $out/sbin/*; do                                                                                                                                                                                             
        if [ -f "$prog" ] && [ -x "$prog" ]; then                                                                                                                                                                                        
          wrapProgram "$prog" \                                                                                                                                                                                                          
            --prefix PYTHONPATH : "${pythonWithBareos}/${pkgs.python3.sitePackages}" \                                                                                                                                                   
            --prefix PATH : "${pythonWithBareos}/bin:$PATH"                                                                                                                                                                              
        fi                                                                                                                                                                                                                               
      done                                                                                                                                                                                                                               
    '';                                                                                                                                                                                                                                  
  };                                                                                                                                                                                                                                     
in                                                                                                                                                                                                                                       

That has at least allowed the daemon to get up and running, and have connectivity to from the director to the remote fd daemon. Hopefully that helps someone (or even myself!) later.

Here’s the other half of the config under in, since the post was too long:

{                                                                                                                                                                                                                                        
  systemd.tmpfiles.rules = [                                                                                                                                                                                                             
    # Ensure bareos dirs exist                                                                                                                                                                                                           
    "d /var/log/bareos 0750 bareos bareos -"                                                                                                                                                                                             
    "d /var/lib/bareos 0750 bareos bareos -"                                                                                                                                                                                             
  ];                                                                                                                                                                                                                                     

  # Create a non-root user for Bareos                                                                                                                                                                                                    
  users.users.bareos = {                                                                                                                                                                                                                 
    uid = 9016;                                                                                                                                                                                                                          
    isSystemUser = true;                                                                                                                                                                                                                 
    home = "/var/lib/bareos";                                                                                                                                                                                                            
    group = "bareos";                                                                                                                                                                                                                    
    shell = pkgs.bashInteractive;                                                                                                                                                                                                        
    extraGroups = [ "mysql" ];                                                                                                                                                                                                           
  };                                                                                                                                                                                                                                     

  users.groups.bareos = {                                                                                                                                                                                                                
    gid = 9016;                                                                                                                                                                                                                          
  };                           
  # Bareos File-Daemon as a systemd service                                                                                                                                                                                              
  systemd.services.bareos-fd = {                                                                                                                                                                                                         
    description = "Bareos File Daemon (local build)";                                                                                                                                                                                    
    wantedBy    = [ "multi-user.target" ];                                                                                                                                                                                               
    after       = [ "network.target" ];                                                                                                                                                                                                  
    #path        = [ pkgs.bareosFd python-bareos ];                                                                                                                                                                                      
    serviceConfig = {                                                                                                                                                                                                                    
      Type = "simple";                                                                                                                                                                                                                   
      ExecStart = "${bareosFd}/sbin/bareos-fd -f -c /etc/bareos/bareos-fd.conf";                                                                                                                                                         
      Restart = "on-failure";                                                                                                                                                                                                            
      User      = "bareos";      # or create a confined 'bareos' user                                                                                                                                                                    
      Group     = "bareos";                                                                                                                                                                                                              

      # Security Settings                                                                                                                                                                                                                
      CapabilityBoundingSet = "CAP_DAC_READ_SEARCH";                                                                                                                                                                                     
      NoNewPrivileges = true;                                                                                                                                                                                                            
      PrivateTmp = true;                                                                                                                                                                                                                 
      ProtectHome = false;                                                                                                                                                                                                               
      ProtectSystem = "full";                                                                                                                                                                                                            
      ReadWritePaths = "/var/lib/bareos /var/log/bareos";                                                                                                                                                                                
    };                                                                                                                                                                                                                                   
  };                                                                                                                                                                                                                                     

  # Minimal matching fd-config (keep the password synced with the Director)                                                                                                                                                              
  environment.etc."bareos/bareos-fd.conf".text = ''                                                                                                                                                                                      
    Director {                                                                                                                                                                                                                           
      Name     = "bareos-dir"                                                                                                                                                                                                            
      Password = "*************"                                                                                                                                                                                                         
    }                                                                                                                                                                                                                                    

    FileDaemon {                                                                                                                                                                                                                         
      Name   = "hub-andrewcz-org-firefly-mariadb-ct0-fd"                                                                                                                                                                                 
      FDPort = 9102                                                                                                                                                                                                                      
      Plugin Directory = ${bareosFd}/lib/bareos/plugins                                                                                                                                                                                  
    }                                                                                                                                                                                                                                    

    Messages {                                                                                                                                                                                                                           
      Name = Standard                                                                                                                                                                                                                    
      director = bareos-dir = all, !skipped, !restored                                                                                                                                                                                   
    }                                                                                                                                                                                                                                    
  '';                                                                                                                                                                                                                                    
}

I really do think there’s probably a nicer, or cleaner, or better way of doing this, but this is what works thus far.