""" * Author: "PeppermintOS Team(peppermintosteam@proton.me) * * License: SPDX-License-Identifier: GPL-3.0-or-later * * Set the infrastructure for bubbles to begin the ISO build * This copies needed config files to the binary and chroot * locations, based on the build base and architecture. """ import os import collections from pathlib import Path import shutil import logging import conf # Public Variables used in the classes BSTRING_ISO_CONFIGS = '~/bubbles/iso_configs' HOME_FOLDER = str(Path(BSTRING_ISO_CONFIGS).expanduser()) WPCHROOT = "/fusato/config/includes.chroot" WPINSTALLER = "/fusato/config/includes.installer" BINARYPTH = "/fusato/config/includes.binary" BOOTSTRAP = "/fusato/config/includes.bootstrap" FUSATOCONFIG = "/fusato/config" # Set up the logging format logger = logging.getLogger() MSG_COPY = "Copying - " MSG_FIN = "Finished - " class Archive: """ Copy the multimedia to the ARCHIVES folder Depending on the architecture it will, copy folders as needed """ def __init__(self,sbase,sarch): logger.info("Copy Archive") self.sbase = sbase self.sarch = sarch src_archive = ('/multimedia/' + self.sbase + self.sarch,) des_archive =('/archives',) # Archives Folders. archive_src_q = collections.deque(src_archive) archive_des_q = collections.deque(des_archive) archive_size_q = len(archive_src_q) for size_length in range(archive_size_q): source = archive_src_q.popleft() des = archive_des_q.popleft() logger.info(MSG_COPY + HOME_FOLDER + source) shutil.copytree(HOME_FOLDER + source, HOME_FOLDER + FUSATOCONFIG + des, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + FUSATOCONFIG + des) class ChrootInstallerFiles: """ Copies all installer folders to CHROOT depending on the base. """ ALLOWED_BASES = {"deb", "dev", "debld", "devld"} def __init__(self, base, sarch=None): self.base = base self.sarch = sarch if self.base not in self.ALLOWED_BASES: logger.warning(f"Base '{self.base}' is not allowed. Skipping.") return logger.info(f"Copy Installer Files for base: {self.base}") calamares_path = '/calamares_settings/' self.src_paths = ( f'{calamares_path}{self.base}/settings/settings.conf', f'{calamares_path}{self.base}/conf/modules/', f'{calamares_path}{self.base}/sources/sources-final', f'{calamares_path}{self.base}/sources/sources-media', f'{calamares_path}{self.base}/scripts/bootloader-config', f'{calamares_path}{self.base}/scripts/update-system', f'{calamares_path}{self.base}/scripts/grub-defaults', f'{calamares_path}{self.base}/scripts/add-calamares-desktop-icon', f'{calamares_path}{self.base}/scripts/install-peppermint', f'{calamares_path}{self.base}/modules/', f'{calamares_path}{self.base}/branding/peppermint/', f'{calamares_path}{self.base}/schemas/96_calamares-settings-debian.gschema.override/', f'{calamares_path}{self.base}/applications/calamares-install-peppermint.desktop' ) self.des_paths = ( '/etc/calamares/', '/etc/calamares/', '/usr/sbin/', '/usr/sbin/', '/usr/sbin/', '/usr/sbin/', '/usr/sbin/', '/usr/bin/', '/usr/bin/', '/usr/lib/calamares/', '/etc/calamares/branding/', '/usr/share/glib-2.0/schemas/', '/usr/share/applications/' ) self.copy_files() def copy_files(self): """ Ensure destination directories exist before copying""" for des in self.des_paths: full_des_path = os.path.join(HOME_FOLDER, WPCHROOT.strip('/'), des.strip('/')) try: os.makedirs(full_des_path, exist_ok=True) logger.info(f"Ensured directory exists: {full_des_path}") except Exception as e: logger.error(f"Error creating directory {full_des_path}: {e}") src_q = collections.deque(self.src_paths) des_q = collections.deque(self.des_paths) size_q = len(src_q) for _ in range(size_q): source = src_q.popleft() des = des_q.popleft() full_src_path = os.path.join(HOME_FOLDER, source.strip('/')) full_des_path = os.path.join(HOME_FOLDER, WPCHROOT.strip('/'), des.strip('/') ) logger.info(MSG_COPY + full_src_path) try: if os.path.exists(full_src_path): if os.path.isdir(full_src_path): shutil.copytree(full_src_path, os.path.join(full_des_path, os.path.basename(full_src_path)), dirs_exist_ok=True ) else: shutil.copy(full_src_path, full_des_path) logger.info(MSG_FIN + full_des_path) else: logger.error(f"Source path does not exist: {full_src_path}") except Exception as e: logger.error(f"Error copying {full_src_path} to {full_des_path}: {e}") @classmethod def run_for_all_bases(cls): """ Define the bases to be used""" bases = ["deb", "dev", "debld", "devld", "invalid_base"] for base in bases: cls(base) #### This section is not really needed since this file is never executed #### outright so I commented this #if __name__ == "__main__": ## This seems incorrect, I am testing # ChrootInstallerFiles.run_for_all_bases() class BinaryFolders: """ Copy all the needed folders to BINARY Depending on the architecture it will, copy folders as needed """ def __init__(self, sbase, sarch): logger.info("Copy Binary") self.sbase = sbase self.sarch = sarch src_binary = ('/splash/' + self.sbase + self.sarch + '/boot', '/splash/' + self.sbase + self.sarch +'/isolinux', '/splash/' + self.sbase + '_live-theme', '/splash/' + self.sbase + '_splash', '/splash/' + self.sbase + '_splash' ) des_binary =('/boot', '/isolinux', '/boot/grub', '/isolinux', '/boot/grub' ) # Binary Folders binary_src_q = collections.deque(src_binary) binary_des_q = collections.deque(des_binary) binary_size_q = len(binary_src_q) for size_length in range(binary_size_q): source = binary_src_q.popleft() des = binary_des_q.popleft() logger.info(MSG_COPY+ HOME_FOLDER + source) shutil.copytree(HOME_FOLDER + source, HOME_FOLDER + BINARYPTH + des, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + BINARYPTH + des) class FusatoConfigs: """ Copy all the needed files to the hooks folders depending on the architecture it will, copy folders as needed """ def __init__(self,sbase,sarch): logger.info("Copy Hooks Files") self.sbase = sbase self.sarch = sarch src_fusatoconfig = ('/hooks/normal/'+ self.sbase + self.sarch,) des_fusatoconfig = ('/hooks/normal/',) # Fusatoconfig FIles fusatoconfig_src_q = collections.deque(src_fusatoconfig) fusatoconfig_des_q = collections.deque(des_fusatoconfig) fusatoconfig_size_q = len(fusatoconfig_src_q) for size_length in range(fusatoconfig_size_q): source = fusatoconfig_src_q.popleft() des = fusatoconfig_des_q.popleft() logger.info(MSG_COPY+ HOME_FOLDER + source) shutil.copytree(HOME_FOLDER + source, HOME_FOLDER + FUSATOCONFIG + des, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + FUSATOCONFIG + des) class InstallerFiles: """ Copy all the needed files to the installer depending on the architecture it will, copy folders as needed """ def __init__(self,sbase,sarch): logger.info("Copy Installer Files") self.sbase = sbase self.sarch = sarch src_installer = ('/installer/preseed/'+ self.sbase + self.sarch, '/installer/artwork/' + self.sbase + self.sarch, '/installer/scripts/'+ self.sbase + self.sarch, '/installer/grub/'+ self.sbase + self.sarch, '/installer/sources/'+ self.sbase + self.sarch, '/installer/sources/'+ self.sbase + self.sarch, '/osrelease/'+ self.sbase + self.sarch ) des_installer =('/', '/usr/share/', '/usr/lib/finish-install.d/', '/preseed/grub/', '/preseed/repos/', '/preseed/conf/', '/preseed/conf/' ) # installer files installer_src_q = collections.deque(src_installer) installer_des_q = collections.deque(des_installer) installer_size_q = len(installer_src_q) for size_length in range(installer_size_q): source = installer_src_q.popleft() des = installer_des_q.popleft() logger.info(MSG_COPY+ HOME_FOLDER + source) shutil.copytree(HOME_FOLDER + source, HOME_FOLDER + WPINSTALLER + des, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + WPINSTALLER + des) def mini_shared_installer_files(): """ This function will get the files that are shared commonly amongst all mini builds, """ logger.info("Copy mini installer files") src_paths = ('/installer/keyrings/', '/installer/applications/', '/PepProPixMaps/', '/pmostools/', '/PepProTools/', '/polkit/', '/issue/', '/pylibraries/requests/', '/pylibraries/tendo/', '/pylibraries/tendo-0.3.0.dist-info/', '/pylibraries/ttkbootstrap/', '/pylibraries/ttkbootstrap-1.10.1.dist-info/', '/pylibraries/ttkcreator/', '/lightdm/', '/autostart' ) des_paths =('/preseed/keyrings/', '/preseed/apps/', '/preseed/pixmaps/', '/preseed/tools/', '/preseed/protools/', '/preseed/polkit/', '/preseed/conf/', '/preseed/py/requests/', '/preseed/py/tendo/', '/preseed/py/tendo-0.3.0.dist-info/', '/preseed/py/ttkbootstrap/', '/preseed/py/ttkbootstrap-1.10.1.dist-info/', '/preseed/py/ttkcreator/', '/preseed/lightdm/', '/preseed/autostart/' ) for src, des in zip(src_paths, des_paths): src_path = HOME_FOLDER + src des_path = HOME_FOLDER + WPINSTALLER + des if os.path.isdir(src_path): for root, dirs, files in os.walk(src_path): for file in files: source_file = os.path.join(root, file) dest_file = os.path.join(des_path, os.path.relpath(source_file, src_path) ) logger.info(f"Copying {source_file} to {dest_file}") shutil.copy(source_file, dest_file) logger.info(f"Copy completed: {dest_file}") else: logger.info(f"Copying {src_path} to {des_path}") shutil.copy(src_path, des_path) logger.info(f"Copy completed: {des_path}") class ArchitectureFiles: """ Copy all the needed files and folders to CHROOT. Depending on the architecture, it will copy files and folders as needed. """ def __init__(self, sbase, sarch, force_copy=False): self.force_copy = force_copy logger.info("Copy Architecture") self.sbase = sbase self.sarch = sarch sources_path = '/sources/' src_paths = (sources_path + self.sbase + self.sarch + '/sources.list', '/id_files/pep_id', '/osrelease/' + self.sbase + self.sarch, '/osrelease/' + self.sbase + self.sarch, '/grub/'+ self.sbase + self.sarch, '/grub/' + self.sbase + '_themes' ) des_paths = ('/opt/pepconf/sources.list', '/usr/share/peppermint/pep_id', '/usr/lib', '/opt/pepconf', '/etc/default', '/boot/grub/themes' ) src_q = collections.deque(src_paths) des_q = collections.deque(des_paths) size_q = len(src_q) for size_length in range(size_q): source = src_q.popleft() des = des_q.popleft() src_path = HOME_FOLDER + source des_path = HOME_FOLDER + WPCHROOT + des if os.path.exists(des_path) and not self.force_copy: logger.info(f"Skipping {src_path} as it already exists at {des_path}") continue logger.info(f"Copying {src_path} to {des_path}") try: if os.path.isdir(src_path): shutil.copytree(src_path, des_path, ignore_dangling_symlinks=True) else: shutil.copy2(src_path, des_path) except Exception as e: logger.error(f"Error copying {src_path} to {des_path}: {e}") else: logger.info(f"Successfully copied {src_path} to {des_path}") def get_id_build_type(): """ This will get the type of build that is taking place """ source_folder = os.path.expanduser('~/start/bldtype') dest_folder = os.path.join(os.path.expanduser('~'),'bubbles', 'iso_configs','fusato','config','includes.chroot','opt', 'pepconf' ) os.makedirs(dest_folder, exist_ok=True) for filename in os.listdir(source_folder): src_file = os.path.join(source_folder, filename) dest_file = os.path.join(dest_folder, filename) if os.path.isfile(src_file): shutil.copy(src_file, dest_file) print(f'Copied: {src_file} to {dest_file}') def ignore_missing_files(src, names): return [name for name in names if not os.path.exists(os.path.join(src, name))] def add_web_profile(): """ This will move the web profile depending on the the build typ """ logger.info("Copy Web Profiles") path_to_bldtype_file = os.path.expanduser('~/start/bldtype') lw_src_path = '/browser_profiles/lw/' ff_src_path = '/browser_profiles/ff/' des_src_path ='/etc/skel/.local/share/pmostools' build_id_list = [ path_to_bldtype_file + 'deb.64xfc', path_to_bldtype_file + 'dev.64xfc', path_to_bldtype_file + 'deb.armxfc', path_to_bldtype_file + 'dev.armxfc', path_to_bldtype_file + 'deb.64gfb', path_to_bldtype_file + 'dev.64gfb', path_to_bldtype_file + 'deb.64opb', path_to_bldtype_file + 'dev.64opb' ] profile_copied = False for build_id in build_id_list: if os.path.exists(build_id): logger.info( f"Found build id file: {build_id}. Copying lw profile." ) shutil.copytree(HOME_FOLDER + lw_src_path, HOME_FOLDER + WPCHROOT + des_src_path, dirs_exist_ok=True, ignore=ignore_missing_files ) profile_copied = True break if not profile_copied: logger.info( "No macthing build file found. Copying ff profile." ) shutil.copytree(HOME_FOLDER + ff_src_path, HOME_FOLDER + WPCHROOT + des_src_path, dirs_exist_ok=True, ignore=ignore_missing_files ) def set_symlinks(): """ Set the symliknks that are used for all builds. """ logger.info("Copy Symlinks") pep_info = '/usr/share/python-apt/templates/Peppermint.info' pep_mirror = '/usr/share/python-apt/templates/Peppermint.mirrors' pep_csv = '/usr/share/distro-info/peppermint.csv' logger.info("Making - " + HOME_FOLDER + WPCHROOT + pep_info) os.symlink('Debian.info', HOME_FOLDER + WPCHROOT + pep_info) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + pep_info) logger.info("Making - " + HOME_FOLDER + WPCHROOT + pep_mirror) os.symlink('Debian.mirrors', HOME_FOLDER + WPCHROOT + pep_mirror) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + pep_mirror) logger.info("Making - " + HOME_FOLDER + WPCHROOT + pep_csv) os.symlink('debian.csv', HOME_FOLDER + WPCHROOT + pep_csv) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + pep_csv) def shared_folders(): """ This function will get the files that are shared commonly amongst all the builds, """ logger.info("Copy Shared folders") src_paths = ('/plymouth/joy', '/application', '/font', '/hooks/live', '/issue', '/issue', '/polkit', '/user_config', '/PepProPixMaps', '/wallpaper', '/menu/menus', '/face', '/pmostools', '/autostart', '/pylibraries' ) des_paths =('/usr/share/plymouth/themes/joy', '/usr/share/applications', '/usr/share/fonts/pepconf', '/usr/lib/live/config', '/etc', '/opt/pepconf', '/usr/share/polkit-1/actions', '/etc/live/config.conf.d', '/usr/share/pixmaps', '/usr/share/backgrounds', '/etc/skel/.config/menus', '/etc/skel/', '/etc/skel/.local/share/pmostools', '/etc/skel/.config/autostart', '/usr/lib/python3/dist-packages' ) src_q = collections.deque(src_paths) des_q = collections.deque(des_paths) size_q = len(des_q) for size_length in range(size_q): source = src_q.popleft() des = des_q.popleft() logger.info(MSG_COPY+ HOME_FOLDER + source) shutil.copytree(HOME_FOLDER + source, HOME_FOLDER + WPCHROOT + des, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des) # After get the maine files lets get the build type taking place get_id_build_type() def icons_themes(): """ This function will get the icons and themse that are for all the builds """ logger.info("Copy icons and themes") src_paths =('/theme/Marwaita Dark Debian/', '/theme/Marwaita Dark Manjaro/', '/theme/Marwaita Dark Peppermint/', '/theme/Marwaita Debian/', '/theme/Marwaita Manjaro/', '/theme/Marwaita Peppermint/', '/theme/Marwaita-Xfwm/', '/theme/Marwaita-Xfwm-Alt/', '/theme/Marwaita-Xfwm-Color-Dark-Text/', '/theme/Marwaita-Xfwm-Color-Light-Text/', '/theme/Marwaita-Xfwm-Dark/', '/icons/Tela-circle-blue-dark/', '/icons/Tela-circle-green-dark/', '/icons/Tela-circle-red-dark/', '/icons/Tela-circle-blue/', '/icons/Tela-circle-green/', '/icons/Tela-circle-red/', '/icons/Tela-circle/' ) des_paths = ('/usr/share/themes/Marwaita Dark Debian/', '/usr/share/themes/Marwaita Dark Manjaro/', '/usr/share/themes/Marwaita Dark Peppermint/', '/usr/share/themes/Marwaita Debian/', '/usr/share/themes/Marwaita Manjaro/', '/usr/share/themes/Marwaita Peppermint/', '/usr/share/themes/Marwaita-Xfwm/', '/usr/share/themes/Marwaita-Xfwm-Alt/', '/usr/share/themes/Marwaita-Xfwm-Color-Dark-Text/', '/usr/share/themes/Marwaita-Xfwm-Color-Light-Text/', '/usr/share/themes/Marwaita-Xfwm-Dark/', '/usr/share/icons/Tela-circle-blue-dark/', '/usr/share/icons/Tela-circle-green-dark/', '/usr/share/icons/Tela-circle-red-dark/', '/usr/share/icons/Tela-circle-blue/', '/usr/share/icons/Tela-circle-green/', '/usr/share/icons/Tela-circle-red/', '/usr/share/icons/Tela-circle/' ) src_q = collections.deque(src_paths) des_q = collections.deque(des_paths) size_q = len(src_q) for size_length in range(size_q): source = src_q.popleft() des = des_q.popleft() logger.info(MSG_COPY+ HOME_FOLDER + source) shutil.copytree(HOME_FOLDER + source, HOME_FOLDER + WPCHROOT + des, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des) def shared_files(): """ This will copy all specific files that a used for all builds. """ logger.info("Copy Shared Files") src_paths = ('/aliases/bash_aliases', '/sources/peppermint.list', '/PepProTools/xDaily', '/PepProTools/welcome', '/PepProTools/kumo', '/PepProTools/xdaily-gui', '/PepProTools/suggested', '/PepProTools/pfetch', '/lightdm/lightdm.conf', '/lightdm/lightdm-gtk-greeter.conf', '/plymouth/plymouthd.conf', ) des_paths = ('/etc/skel/.bash_aliases', '/etc/apt/sources.list.d', '/usr/local/bin/xDaily', '/usr/local/bin/welcome', '/usr/local/bin/kumo', '/usr/local/bin/xdaily-gui', '/usr/local/bin/suggested', '/usr/local/bin/pfetch', '/etc/lightdm/lightdm.conf', '/etc/lightdm/lightdm-gtk-greeter.conf', '/etc/plymouth/plymouthd.conf', ) src_q = collections.deque(src_paths) des_q = collections.deque(des_paths) size_q = len(src_q) for size_length in range(size_q): source = src_q.popleft() des = des_q.popleft() logger.info(MSG_COPY+ HOME_FOLDER + source) shutil.copy(HOME_FOLDER + source, HOME_FOLDER + WPCHROOT + des ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des) def shared_server_files(): """ This silll copy all specific files that a used for the server builds. """ logger.info("Copy Shared Files") src_paths = ('/server/firewall/public.xml', ) des_paths = ('/etc/firewalld/zones', ) # copy files to thier CHROOT Location src_q = collections.deque(src_paths) des_q = collections.deque(des_paths) size_q = len(src_q) for size_length in range(size_q): source = src_q.popleft() des = des_q.popleft() logger.info(MSG_COPY+ HOME_FOLDER + source) shutil.copy(HOME_FOLDER + source, HOME_FOLDER + WPCHROOT + des ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des) def boostrap_shared(): """ Copy specific folders in the boostrap location """ logger.info("Copy Shared BootStrap") src_paths = ('/issue',) des_paths = ('/etc',) src_q = collections.deque(src_paths) des_q = collections.deque(des_paths) size_q = len(src_q) for size_length in range(size_q): source = src_q.popleft() des = des_q.popleft() logger.info(MSG_COPY+ HOME_FOLDER + source) shutil.copytree(HOME_FOLDER + source, HOME_FOLDER + BOOTSTRAP + des, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des) def xfce_configs(): """ Copy the xfce files. """ logger.info("Copy xfce4 configs") src_xfce = '/xfce/xfce4' des_xfce = '/etc/skel/.config/xfce4' logger.info(MSG_COPY + HOME_FOLDER + src_xfce) shutil.copytree(HOME_FOLDER + src_xfce, HOME_FOLDER + WPCHROOT + des_xfce, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des_xfce) src_thunar = '/xfce/Thunar' des_thunar = '/etc/skel/.config/Thunar' logger.info(MSG_COPY+ HOME_FOLDER + src_thunar) shutil.copytree(HOME_FOLDER + src_thunar, HOME_FOLDER + WPCHROOT + des_thunar, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des_thunar) def gnome_flahsbak_configs(): """ Copy the gnome flashback files """ logger.info("Copy Gnome Flashback configs") src_gnomef = '/gnome-flashback' des_gnomef = '/etc/skel/' logger.info("INFO: Copying - " + HOME_FOLDER + src_gnomef) shutil.copytree(HOME_FOLDER + src_gnomef, HOME_FOLDER + WPCHROOT + des_gnomef, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des_gnomef) def open_box_configs(): """ Copy the openbox files """ logger.info("Copy openbox configs") src_ob = '/openbox' des_ob = '/etc/skel/' logger.info("INFO: Copying - " + HOME_FOLDER + src_ob) shutil.copytree(HOME_FOLDER + src_ob, HOME_FOLDER + WPCHROOT + des_ob, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des_ob) def loaded_configs(): """ Copy the loaded xfce files """ logger.info("Copy loaded xfce configs") src_loaded = '/loaded/xfce' des_loaded = '/etc/skel/.config/' logger.info("INFO: Copying - " + HOME_FOLDER + src_loaded) shutil.copytree(HOME_FOLDER + src_loaded, HOME_FOLDER + WPCHROOT + des_loaded, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des_loaded) def loaded_folders(): """ This function will get the files that are used by the loaded builds, """ logger.info("Copy loaded folders") src_paths = ('/loaded/application', '/loaded/wallpaper' ) des_paths =('/usr/share/applications', '/usr/share/backgrounds' ) src_q = collections.deque(src_paths) des_q = collections.deque(des_paths) size_q = len(des_q) for size_length in range(size_q): source = src_q.popleft() des = des_q.popleft() logger.info(MSG_COPY+ HOME_FOLDER + source) shutil.copytree(HOME_FOLDER + source, HOME_FOLDER + WPCHROOT + des, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des) def server_configs(): """ Copy the server files """ logger.info("Copy server configs") src_server = '/server/configs' des_server = '/etc/skel/.config/' logger.info("INFO: Copying - " + HOME_FOLDER + src_server) shutil.copytree(HOME_FOLDER + src_server, HOME_FOLDER + WPCHROOT + des_server, dirs_exist_ok = True ) logger.info(MSG_FIN + HOME_FOLDER + WPCHROOT + des_server)