Files
init.sh/doc/dev.md

39 KiB

init.sh developer's reference

Table of content

1. Getting started

This is a developer's reference. It's not intended to be a manual, but a reference for all internal functions, so you can easily build your own modules. This supposes you already read the README file. Creating modules will also require some good knowledge of Bash programming.

Writing conventions are the classical ones:

  • <param>: writen like this, the parameter is mandatory
  • [param]: that parameter is optionnal
  • [ab|cd]: optionnal parmeter have to be "ab" or "cd"
  • [0..15,20]: acceptable values start at 0 and goes up to 15 or be 20.

2. The aaa_error.sh file

2.1. Functions

2.1.1. check_root

Check if user is root. If the user is not root, script execution is interrupted and exit with error.

This function has no parameter.

If the variable NO_ROOT_CHECK is set to true, the function always exit without error and no check is done.

2.1.2. die <exitcode> [--force]

Trigger an error, print a back trace and exit the script, unless KEEPGOING variable is set to true. In that situation, we just display a warning.

If the parameter --force is given, we exit even if the KEEPGOING variable is set to true.

2.1.3. noerror [--noout] <command>

Allow the execution of a command bypassing the error management system. The purpose is to allow execution of tests returning normally a non-zero value without triggering an error and the exit coming with.

If the first parameter is --noout any outputs on standard and error console are disabled. The other parameters are the raw command line to execute.

In any case, the function echoes the error code returned by the executed command.

2.2. Other functionalities

The simple integration of aaa_error.sh file into a script, will change the entire script behavior regarding errors. The following Bash signals will be trapped:

  • ERR: The ERR signal is triggered every time Bash encounters an error or if a command return a non-zero value. The function called on that signal will stop execution of the script, displaying an error message with error code and a back trace to help identify the error origin. Because of this behavior, the function supersedes the internal errexit Bash configuration switch, unless the noerror function is used.
  • SIGINT: That signal is triggered when Ctrl + C is pressed by the user. That signal will be interpreted only if the command being executed when the event occurs is a Bash internal. If an executable program receive the signal it will be interpreted with its own mechanisms, generally resulting in an execution error that will trigger an ERR signal as described above. The script will exit after cleanup when that signal is trapped.
  • SIGTERM: That signal is typically the result of an external kill of the bash process running the script. The kill signal can come from the kernel or through the use of a kill command. The script will exit after cleanup.

3. The chroot.sh file

3.1. Functions

3.1.1. chroot_bootstrap

That function is called if a chroot option have been given. It's in charge of performing the chroot, copying a full working directory structure of init.sh tree. After that copy, a chroot command is runned launching that new copy of init.sh.

If the child init.sh end with error, the stage file is gathered in parent directory tree to allow launching again the chrooted init.sh with the resume option.

At the end the function will clean up removing the second copy of init.sh.

3.2. Other functionnalities

So far, only one function is provided in this file.

4. The diskman.sh file

4.1. Global warning

The goal of that unit is to provide disk manipulation function, like partitionning, blanking or formative. All those functions are potentially very destructive. Please use with extra care and do not hesitate to highly protect your code when using those. By defaults the functions try to be as conservative as they can, triguering errors on the smallest doubt.

4.2. Function

4.2.1. blank_disk <bloc_device> [--full]

Blank a block device using two different method to be sure it's all clear. First we use the wipefs method specialized in reseting all possible flags on the drive and it's partitions. It will also blank the partition table. A second pass will fill the first 512 MB with zeroes to also blank MBR and other parts of the drive wipefs would have ignored.

If the parameter --full is provided as second parameter, the entire disk will be filled with zeroes. Please consider that such operation might take a very long time (can be several hours).

That function only take parameter which must be a bloc device.

4.2.2. is_blank <bloc_device>

That function will try to detect if a drive is blank of not. It will return 0 if the drive is blank, and return 1 otherwise. If the function return 2, either the provided parameter is not a block device or that block device do not exists.

Please consider that special drive configuration could be detected as blank while it's not. Only one parameter will be accepted, a bloc device.

The function will give different information depending on the bloc device you test:

  • on a whole disk drive it while return 0 if the drive is blank, meaning no MBR and no partition table (either GTP or DOS);
  • on a partition it will tell if it's formated or not. Beware that an erased then recreated partion will continue to have old partition data available and will be shown as non blank.

4.2.3. mkparts <disk> [dos|gpt] [size_part1 [... size_partN]]

This function create partitions on the disk given as the first parameter. The second parameter can be gpt or dos, respectively to instruct the creation of a GPT partition table (which is default when not mentioned) or a DOS partition table, deprecated but suported for compatibility purposes. Then a list of size can be given to generate more than one partition. In the case of a DOS partition table, only primary partition are possible, four of it maximum.

Partition size can be :

  • simple number: will be interpreted as a precise number of cylinder, this is the only method that will be precise;
  • 100M: will create a 100 MiB partition, more or less to the nearest cylinder;
  • 100G: will create a 100 GiB partition, more or less to the nearest cylinder;
  • 100T: same again 100 TiB, ang you really have a lot of space...
  • 0: will be interpreted as all remaining space in the final partition scheme. It must come only once.

Be warned that a size (whatever the unit is) can result in slightly different space depending on the drive model and cylinder size.

4.3. Other functionnalities

That file don't provide any other things that the previously listed functions.

5. The command_line.sh file

5.1. Functions

5.1.1. read_commandline

That function consist in a loop that analyse command line one parameter after the other. Most of command line parameters will result in the positionning of some global variables. The following table details the variable with their type associated to the corresponding parameter:

Parameter Variable Type Descrition
--help none n/a Trigger help display directly and exit
--version none n/a Trigger version display directly and exit
--module MANUAL_MODULE_LIST string The following parameter will set a list of module to use
--check-only CHECK_ONLY boolean Activate check only mode
--jump JUMP boolean Activate no checks mode
--keep-going KEEPGOING boolean Activate keep going option
--resume RESUME boolean Activate resume mode if stage file exists
--no-root-check NO_ROOT_CHECK boolean Activate option to not check if user is root
--no-deps NO_DEPS boolean Activate not checking module dependencies option
--logfile NEW_LOGFILE string The following parameter will be the log filename
--file CONFFILES string The following parameter will be a configuration filename
--shell RUN_SHELL boolean Activate the shell mode
--chroot CHROOT_PATH string The following parameter will be the path to chroot in
--cron CRON_MODE boolean Activate cron mode

The function will do some basinc synthax checks. For exemple if you put an option just after one supposing a value declaration, an error will be trigered directly.

5.1.5. process_commandline_and_vars

That function have the role to check the concistancy of command line parameters. It will triger errors if incompatible parameters have been given or if those parameters might lead to a non predictable situation.

When those checks are done, the definitive module list to load is created. With that list we then checks the modules are available and do not contain the dash character.

That function will also triger an error if the definitive module list is empty.

5.2. Other functionnalities

That file don't provide any other things that the previously listed functions.

6. The display.sh file

6.1. Functions

6.1.1. prnt [-n] [I|W|E|m] <message>

Print a message with timestamp and header. The header depends on a single character parameter, will be colored and have a fixed length, so the messages will always be aligned.

If the first parameter is -n, we won't go to a new line after displaying the message.

The first parameter (if -n is not provided) is the header type, having those possible values:

  • I: Display an informative message in green
  • W: Display a warning in yellow
  • E: Display an error in red
  • m: Display a message without header but aligned
  • Anything else will be treated as the message and will lose alignment.

The second parameter is the message to display.

As this function is widely used almost everywhere in the code at runtime, consider it as being a base dependency of all libraries and modules. Consequently that function can only contain code that cannot trigger errors or fail as it's also used to display errors. Thus it only contains echoes and some variables manipulation.

6.1.2. separator

That function display a seprator made with dash, to fill the length of the screen minus one character if screen length is 80 character or less. If more than 80 the lenght of the separator will be 80 plus half of additionnal length.

It takes no parameters and return no value.

6.1.3. dsleep <miliseconds> [char]

That function is an equivalent to sleep bash command but will display a countdown every second until it reaches zero. Optionnally a character (or a string) can be given as a second parameter to replace the countdown by that character. For exemple, you can use a dot to display a dot every second until the wait is over.

The function returns nothing useful.

6.1.4. dump_key_buffer

That function dumps keyboard's buffer. It's used to clear eventual key press before any critical keyboard action.

That function takes no parameter and returns no useful value.

6.2. Other functionalities

Using that script will declare some easy to remember variables containing Bash color codes:

  • Standard codes depending on your environment: DEFAULTFG, DEFAULTBG, DEFAULTCOL=${DEFAULTBG}${DEFAULTFG}
  • Regular colors: Black, Red, Green, Yellow, Blue, Purple, Cyan, White
  • Bold (only available in graphical console or some non standard console fonts): BBlack, BRed, BGreen, BYellow, BBlue, BPurple, BCyan, BWhite
  • Underline: UBlack, URed, UGreen, UYellow, UBlue, UPurple, UCyan, UWhite
  • Background: On_Black, On_Red, On_Green, On_Yellow, On_Blue, On_Purple, On_Cyan, On_White
  • High intensity: IBlack, IRed, IGreen, IYellow, IBlue, IPurple, ICyan, IWhite
  • Bold high intensity (only available in graphical console or some non standard console fonts): BIBlack, BIRed, BIGreen, BIYellow, BIBlue, BIPurple, BICyan, BIWhite
  • High intensity backgrounds: On_IBlack, On_IRed, On_IGreen, On_IYellow, On_IBlue, On_IPurple, On_ICyan, On_IWhite

For example, if you what to write "ATTENTION: this is a warning!" in red with "ATTENTION:" on yellow background, you should write:

echo -e "${IRed}${On_IYellow}ATTENTION:${DEFAULTBG} this is a warning!${DEFAULTCOL}"

Remember to always terminate an echo -e using colors with the $DEFAULTCOL variable. If not, any new line might be filled with the last used color and line ending will be filled with background color.

7. The filefct.sh file

7.1. Common behavior

In our terminology a source file can be of three different origins, selected automatically from highest to lowest priority:

  • repo/hosts/$HOSTNAME: this allows to provide system specific files. Use only relative path to access it.
  • repo/common: this one will provide files suitable for your entire infrastructure. Yet again provide a relative path to access it.
  • Any path: You can give fully qualified path names to access resources from other locations.

7.2. Functions

7.2.1. backup_dist <file_or_dir1> [file_or_dir2 [... file_or_dirN]]

That function will provide a backup of any given files or directories given in command line. The backup will be named name.dist-timestamp, where name is the original file or directory name and timestamp the date and time of the backup as retuned by the stdtime function. If a file given in parameter don't exists, the function will issue a warning and continue to the next.

If target file or directory is a symbolic link, the link will be resolved recursively until we backup the final target on its side.

The function don't take any other parameters than file and/or directory names.

7.2.2. select_file <filename>

Returns the best match in our priority system returning on stdout the resulting fully qualified path name as a result. The priorities applies on file existance.

Many functions manipullating files in init.sh depends on that function.

7.2.2. install_file <source1> [source2 [... sourceN]] <destination>

Install a list of source files to the given destination using our priority system.

Wildcards are not allowed in file names, so an error will occurs if you try to use any. It's also not yet possible to give an entire directory as a source.

The last parameter is always the destination. If the destination path does not exists, it will be created automatically.

7.2.3. append_file <source> <destination>

That function add the content of source file to destination file. The source file can have different origins, following the same path priority as the install_file function.

The destination file must exist and be on the root filesystem. Only two parameters are accepted, the source and destination files.

7.2.4. is_dir_empty <directory>

That function take only one parameter, a path name and return 0 if the given path don't exists or is empty. It will return one if there's at least one file in the given directory.

If the given parametter is a file (or a symlink to a file), it will terminate with an error.

7.2.5. patch_file <source> <destination> [VAR1 [VAR2 [... VARN]]]

That function will patch the given file using our priority system, patch it then place the result in the given destination. The patching will be done when any @VAR@ item in the source file will match an environment variable of the exact same name without the trailing @. Variables will be either the given list or, if nothing is given in parameter, in the global system variables, in the context of the init.sh execution.

Source file must exists and not be empty. The function returns nothing useful.

7.2.6. tag_file <file1> [file2 [... fileN]]

That function add a tag to the first line of the given files. If one file allready exists, the added line will be in the form:

# File automatically modified by init.sh on $(stdtime).

If it don't exists it is created with the line:

# File automatically generated by init.sh on $(stdtime).

It's not using the file selection system as our source file are not suposed to be modified directly. In consequence, you should always provide fully qualified path names to it.

7.2.7. file_exists <file1> [file2 [... fileN]]

That function check files existance within our file selection system. If one source file is missing it will return 1 and echo the first file name that have not been found in the list. If all the given files exists, it returns 0.

7.2.8. file_must_exists <file1> [file2 [... fileN]]

That function check files existance within our file selection system. If one source file is missing it will return an error and stop execution. That function is logicaly massively used during check phase to verify all source files are in place.

7.2.9. directory_exists <directory1> [directory2 [... directoryN]]

That function check directories existance within our file selection system. If one source directory is missing it will return 1 and echo the first directory name that have not been found in the list. If all the given directories exists, it returns 0.

7.2.10. directory_must_exists <directory1> [directory2 [... directoryN]]

That function check directories existance within our file selection system. If one source directory is missing it will return an error and stop execution. That function is logicaly massively used during check phase to verify all source directories are in place.

7.3. Other functionnalities

That file don't provide any other things that the previously listed functions.

8. The loaders.sh file

8.1. Functions

8.1.3. load_autoconf

That function will automatically load system specific configuration if file exist in the following order:

  1. auto/arch.conf.sh
  2. auto/distro.conf.sh
  3. auto/distro-arch.conf.sh
  4. auto/distro-version.conf.sh
  5. auto/distro-codename.conf.sh (if SYS_CODE defined)
  6. auto/distro-version-arch.conf.sh
  7. auto/distro-codename-arch.conf.sh (if SYS_CODE defined)

Plaese note that a situation where no such file exists would lead to error. Most of the time a basic package manager configuration will be required to make it work.

8.1.4. load_configuration

That function loads configuration files. It will first check for configuration given as command line parameter. If no such parameter exists, it will try to load a file named conf/${HOSTNAME}.conf.sh. If that file don't exists, the generic configuration will be loaded in the file conf/init.conf.sh.

If no configuration file can be found the function will trigger an error and exit the script.

8.2. Other functionnalities

That file don't provide any other things that the previously listed functions.

9. The pkgman.sh file

9.1. Global dependencies

Because it gives system independent function to the system dependent package manager, the entire file depends on PKG_MAN variable, defining the package manager executable to use. Other variables giving command line parameters to use for the different function will also be nedeed and detailed for every function. All those variable are defined in a system dependant configuration file automatically called on script startup.

9.2. Functions

9.2.1. pkgupdt

That function calls the package manager to update package database.

It depends on the COM_UPDATE variable wich define the parameters to use to accomplish that function.

That function takes no parameters and any given parameters will be ignored.

9.2.2. pkginst <package1> [package2 [... packageN]]

That function installs using the package manager the packages given in parameters. The list of parameters are all considered as package names.

Before installation, the list of package to be installed by the package manager will be extracted to allow execution of pre installation scripts and post installation scripts, even for dependencies (ie: packages not parts of the given parameters).

Preinstallation scripts have to be named preinst_<package_name>. Post installation script will be in the form postinst_<package_name>.

If the INSTALL_MODE variable is set to dev the package manger will be called surrounded by eventual pre and post install scripts, one package after the other. Elsewhere, all pre installation scripts are executed, followed by the package manager with the entire package list as parameter and finally all the post installation scripts.

The function depends on the COM_INSTALL variable wich define the parameter to use to accomplish that package manager function.

9.2.3. pkgupgd

That function calls the package manager to upgrade system. If pre upgrade scripts exists, they will be executed if the corresponding package are being upgraded. After the upgrade, the same behaviour will trigger post upgrade scripts.

Pre upgrade scripts have to be named preupgd_<package_name>. Post upgrade script will be in the form postupgd_<package_name>.

It depends on the COM_UPGRADE variable wich define the parameters to use to accomplish that function.

That function takes no parameters and any given parameters will be ignored.

9.2.4. pkgrm <package1> [package2 [... packageN]]

That function uninstalls using the package manager the packages given in parameters. The list of parameters are all considered as package names.

Before removal, the list of package to be uninstalled by the package manager will be extracted to allow execution of pre removal scripts and post removal scripts, even for dependencies (ie: packages not parts of the given parameters).

If the INSTALL_MODE variable is set to dev the package manger will be called one package after the other (allong with pre and post remove scripts). Elsewhere all pre removal scripts are executed, followed by the package manager with the entire package list as parameter and finally all the post removal scripts.

Pre remove scripts have to be named prerm_<package_name>. Post remove script will be in the form postrm_<package_name>.

The function depends on the COM_REMOVE variable wich define the parameter to use to accomplish that function.

9.2.5. pkgautorm

That function calls the package manager to remove no longer needed installed dependencies. Any package not manually installed is considered as a depndency.

Pre removal and post removal scripts will be executed accordingly if any matching package is to be removed. It's the same as the ones executed by pkgrm function.

It depends on the COM_AUTOREM variable wich define the parameters to use to accomplish that function.

That function takes no parameters and any given parameters will be ignored.

9.3. Other functionnalities

Other functions are declared to call pre and post actions for the corresponding package manager events. It doesn't make sense those functions to be called outside of the integrated package manager mechanisms as their functionnalities depends on variables managed by their respective package manager functions.

The following table resume those function sorted with their respective caller:

Pre/post functions Caller Required var Package triger Description
exec_preinst pkginst GET_INTALLLIST preinst_@pkgname@ GET_INTALLLIST variable defines the command that allows us to obtain the list of package that will be installed with @pkg@ as a substitute to the list given as pkginst parameters.
exec_postinst pkginst POSTINSTLIST postinst_@pkgname@ POSTINSTLIST is generated by exec_preinst and destroyed after exec_postinst execution.
exec_preupgd pkgupgd GET_UPGRADELIST preupgd_@pkgname@ GET_UPGRADELIST variable defines the command that allows us to obtain the list of package that will be installed.
exec_postupgd pkgupgd POSTUPGRADELIST postupgd_@pkgname@ POSTUPGDLIST is generated by exec_preupgd and destroyed after exec_postupgd execution.
exec_prerm pkgrm GET_REMOVELIST prerm_@pkgname@ GET_REMOVELIST variable defines the command that allows us to obtain the list of package that will be removed. @pkg@ will be substituted by the list given as pkgrm parameters.
exec_postrm pkgrm POSTRMLIST postrm_@pkgname@ POSTRMLIST is generated by exec_prerm and destroyed after exec_postrm execution.
exec_preautorm pkgautorm GET_AUTORMLIST prerm_@pkgname@ GET_AUTORMLIST variable defines the command that allows us to obtain the list of package that will be automatically removed.
exec_postautorm pkgautorm POSTRMLIST postrm_@pkgname@ POSTRMLIST is generated by exec_preautorm and destroyed after exec_postautorm execution.

10. The services.sh file

10.1. Global dependencies

That script relies on the INIT_COM variable, defining the program to use to manipulate services. It is defined in configuration file automatically called depending on your distribution. Nevertheless, even if it's system dependent, some distributions offers you to choose between different services call (and init system). If you're not using the standard init system of your distribution, you'll need to overload the INIT_COM variable in your configuration files.

It have been tested with SystemV, SystemD and UpStart init systems. Thus, the originally UpStart "service" program tend to be available on many systems and is privileged.

With the tested init systems, and considering %srv% the service name and %com% the command to execute, the INIT_COM variable can be:

  • $RC_SCRIPTS_PATH/%srv% %com% with $RC_SCRIPTS_PATH being /etc/init.d on Debian like systems when using SystemV init. The variable can be overloaded to change the access path.
  • systemctl %com% %srv% for systems using SystemD
  • service %srv% %com% for upstart like scripts (but widely available)

10.2. Functions

10.2.1. exec_serv <service> <command>

That function execute the given action to the given service. The service have to be the first parameter and the action, the second parameter. No more parameter will be acceted and an error will be triggered if there's any more than two.

The function returns the exit code of the service command.

10.2.2. svc_start <service1> [service2 [... serviceN]]

Start the services given in parmeters. You can give as many services you want.

That function relies on the previously documented exec_serv function.

10.2.3. svc_reload <service1> [service2 [... serviceN]]

Reload the configuration of the services given in parmeters. You can give as many services you want. Be careful using this as some services don't have that capability.

That function relies on the previously documented exec_serv function.

10.2.2. svc_restart <service1> [service2 [... serviceN]]

Restart the services given in parmeters. It consist generally in a stop immediately followed by a start. You can give as many services you want.

That function relies on the previously documented exec_serv function.

10.2.3. svc_stop <service1> [service2 [... serviceN]]

Stop the services given in parmeters. You can give as many services you want.

That function relies on the previously documented exec_serv function.

10.3. Other functionnalities

That file don't provide any other thing that the previously listed functions.

11. The support.sh file

11.1. Global behaviour

That file is designed to just display information. It only contains code that don't requires any special rights, and do nothing to the system. The idea is to have the minimal sets of dependencies. As it's sometimes using colors to display results, it depends only on color code declaration in the display.sh file.

11.2. Functions

11.2.1. disp_help

That function display the help screen, usually called using the --help switch.

It's not taking any parameter and return nothing but help text.

11.2.2. show_version

That function display the version of init.sh. It will also parse all the available modules to display a table with their respecting versions.

If user is not root an additionnal warning will be displayed to warn the fact the script requires root privileges to work properly.

11.3. Other functionnalities

That file don't provide any other thing that the previously listed functions.

12. The utils.sh file

12.1. Functions

12.1.1. stdtime

Display date and time based on RFC 3339 standard but slightly modified so it can be used in filename. Thus spaces are replaced by dash, and comas between hours, minutes and seconds are removed.

That function takes no parameters and return its result on standard output.

12.1.2. function_exists <function_name>

That function checks if the given name is a defined function in the execution environment. It returns 0 if yes and an undefined non zero value if not.

That functions prints nothing.

12.1.3. get_mod_name <module_file>

That function return the name of the module file given in parameter. It takes only one parameter: the module file name.

Result is sent to stdout.

12.1.4. set_system_proxy

That function applies proxy settings in the configuration files to the system proxy configuration, unless the --no-proxy parameters have been given command line.

That function takes no parameters and only change http_proxy and https_proxy standard POSIX variables. No usefull result will be returned.

12.2. Other functionnalities

That file don't provide any other thing that the previously listed functions.

13. The version.sh file

13.1. Functions

13.1.1. get_os_version

That function takes no parameters and will return three values in order:

  1. Distribution ID, in lowcase, usually equivalent to the distribution name.
  2. Distribution version, if available, elsewhere kernel version with it's major.
  3. Distribution codename (eg. buster for Debian 10) in lowercase if available. If not, the generic "null" value is returned instead.

The function mainly relies on the "/etc/os-release" new standard file. If your distribution do not provide that file it is required you generate it yourself before using init.sh. If you need help with the os-release file you can check the official documentation.

In "/etc/os-release" the variables ID, VERSION_ID and VERSION_CODENAME will be the ones being analysed. Only the ID variable is mandatory.

13.1.2. set_sys_var <arch> <dist> <version> <codename>

That function sets important variable that will store the system architecture. It will allow the automatic loading of mandatory system dependent code and variables. For debugging purpose it will be possible to call it manually.

Inside the init.sh initiallisation, it's called that way:

set_sys_vars $(uname -m) $(get_os_version)

All the four parameters have to be given in that order:

  1. System architecture (eg. x86_64, i386, arm64...)
  2. Distribution name (eg. debian, centos, ubuntu...)
  3. Distribution version (or kernel version for rolling releases)
  4. Distribution codename if available (eg. jessie, buster, bulleyes...)

If your distribution do not provide any codename, you have to give "null" as a replacement parameter.

The following global variables will be set at the end of the execution:

  • SYS_ARCH for the system architecture
  • SYS_DIST for the distribution name
  • SYS_VER for the distribution version
  • SYS_CODE for the distribution codename

The SYS_CODE variable won't be set if your distribution provides no codename.

13.2. Other functionnalities

That file don't provide any other thing that the previously listed functions.

14. Writing conventions

For readability and compatibility purpose, I adopted some writing conventions. First of all indentation is made with space only, as different editors can have a very different approach on tabs management. Please configure your editor accordingly if you want to share your work.

If, for and while statement are all written in that way:

if [[ condition ]]; then
    something
fi
for var in range; do
    something
done
while condition; do
    something
done

Case statement will look like this:

case var in
    state1)
        something
    ;;
    state2)
        something
    ;;
    *)
        something
    ;;
esac

Tests have to be done using if. Writting [[ test ]] && action is not encouraged even if elegant. It makes reading harder for beginners.


Documentation (c) 2019-2021 Geoffray Levasseur.

This file is distributed under3-clause BSD license. The complete license agreement can be obtained at: https://opensource.org/licenses/BSD-3-Clause