You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

2684 lines
62 KiB

4 years ago
#!/bin/bash -e
###################
# ARRAY UTILITIES #
###################
function arrayToParameters()
{
local -r array=("${@}")
local -r string="$(printf "'%s' " "${array[@]}")"
echo "${string:0:${#string} - 1}"
}
function arrayToString()
{
local -r array=("${@}")
arrayToStringWithDelimiter ',' "${array[@]}"
}
function arrayToStringWithDelimiter()
{
local -r delimiter="${1}"
local -r list=("${@:2}")
local -r string="$(printf "%s${delimiter}" "${list[@]}")"
echo "${string:0:${#string} - ${#delimiter}}"
}
function excludeElementFromArray()
{
local -r element="${1}"
local array=("${@:2}")
local i=0
for ((i = 0; i < ${#array[@]}; i = i + 1))
do
if [[ "${array[i]}" = "${element}" ]]
then
unset array['${i}']
fi
done
echo "${array[@]}"
}
function isElementInArray()
{
local -r element="${1}"
local -r array=("${@:2}")
local walker=''
for walker in "${array[@]}"
do
[[ "${walker}" = "${element}" ]] && echo 'true' && return 0
done
echo 'false' && return 1
}
function sortUniqArray()
{
local -r array=("${@}")
trimString "$(tr ' ' '\n' <<< "${array[@]}" | sort -u | tr '\n' ' ')"
}
#####################
# COMPILE UTILITIES #
#####################
function compileAndInstallFromSource()
{
local -r downloadURL="${1}"
local -r installFolderPath="${2}"
local -r installFileOrFolderBinPath="${3}"
local -r user="${4}"
initializeFolder "${installFolderPath}"
local -r currentWorkingDirectory="$(pwd)"
local -r tempFolder="$(getTemporaryFolder)"
unzipRemoteFile "${downloadURL}" "${tempFolder}"
cd "${tempFolder}"
"${tempFolder}/configure" --prefix="${installFolderPath}"
make
make install
chown -R "${user}:$(getUserGroupName "${user}")" "${installFolderPath}"
symlinkUsrBin "${installFileOrFolderBinPath}"
cd "${currentWorkingDirectory}"
rm -f -r "${tempFolder}"
}
#######################
# DATE TIME UTILITIES #
#######################
function convertISO8601ToSeconds()
{
local -r time="${1}"
if [[ "$(isMacOperatingSystem)" = 'true' ]]
then
date -j -u -f '%FT%T' "$(awk -F '.' '{ print $1 }' <<< "${time}" | tr -d 'Z')" +'%s'
elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' || "$(isUbuntuDistributor)" = 'true' ]]
then
date -d "${time}" +'%s'
else
fatal '\nFATAL : only support Amazon-Linux, CentOS, Mac, RedHat, or Ubuntu OS'
fi
}
function getISO8601DateTimeNow()
{
date -u +'%Y-%m-%dT%H:%M:%SZ'
}
function getUTCNowInSeconds()
{
date -u +'%s'
}
function secondsToReadableTime()
{
local -r time="${1}"
local -r day="$((time / 60 / 60 / 24))"
local -r hour="$((time / 60 / 60 % 24))"
local -r minute="$((time / 60 % 60))"
local -r second="$((time % 60))"
if [[ "${day}" = '0' ]]
then
printf '%02d:%02d:%02d' "${hour}" "${minute}" "${second}"
elif [[ "${day}" = '1' ]]
then
printf '%d day and %02d:%02d:%02d' "${day}" "${hour}" "${minute}" "${second}"
else
printf '%d days and %02d:%02d:%02d' "${day}" "${hour}" "${minute}" "${second}"
fi
}
########################
# FILE LOCAL UTILITIES #
########################
function appendToFileIfNotFound()
{
local -r file="${1}"
local -r pattern="${2}"
local -r string="${3}"
local -r patternAsRegex="${4}"
local -r stringAsRegex="${5}"
local -r addNewLine="${6}"
# Validate Inputs
checkExistFile "${file}"
checkNonEmptyString "${pattern}" 'undefined pattern'
checkNonEmptyString "${string}" 'undefined string'
checkTrueFalseString "${patternAsRegex}"
checkTrueFalseString "${stringAsRegex}"
if [[ "${stringAsRegex}" = 'false' ]]
then
checkTrueFalseString "${addNewLine}"
fi
# Append String
if [[ "${patternAsRegex}" = 'true' ]]
then
local -r found="$(grep -E -o "${pattern}" "${file}")"
else
local -r found="$(grep -F -o "${pattern}" "${file}")"
fi
if [[ "$(isEmptyString "${found}")" = 'true' ]]
then
if [[ "${stringAsRegex}" = 'true' ]]
then
echo -e "${string}" >> "${file}"
else
if [[ "${addNewLine}" = 'true' ]]
then
echo >> "${file}"
fi
echo "${string}" >> "${file}"
fi
fi
}
function checkExistFile()
{
local -r file="${1}"
local -r errorMessage="${2}"
if [[ "${file}" = '' || ! -f "${file}" ]]
then
if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]]
then
fatal "\nFATAL : file '${file}' not found"
fi
fatal "\nFATAL : ${errorMessage}"
fi
}
function checkExistFolder()
{
local -r folder="${1}"
local -r errorMessage="${2}"
if [[ "${folder}" = '' || ! -d "${folder}" ]]
then
if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]]
then
fatal "\nFATAL : folder '${folder}' not found"
fi
fatal "\nFATAL : ${errorMessage}"
fi
}
function checkValidJSONContent()
{
local -r content="${1}"
if [[ "$(isValidJSONContent "${content}")" = 'false' ]]
then
fatal '\nFATAL : invalid JSON'
fi
}
function checkValidJSONFile()
{
local -r file="${1}"
if [[ "$(isValidJSONFile "${file}")" = 'false' ]]
then
fatal "\nFATAL : invalid JSON file '${file}'"
fi
}
function cleanUpSystemFolders()
{
header 'CLEANING UP SYSTEM FOLDERS'
local -r folders=(
'/tmp'
'/var/tmp'
)
local folder=''
for folder in "${folders[@]}"
do
echo "Cleaning up folder '${folder}'"
emptyFolder "${folder}"
done
}
function copyFolderContent()
{
local -r sourceFolder="${1}"
local -r destinationFolder="${2}"
checkExistFolder "${sourceFolder}"
checkExistFolder "${destinationFolder}"
find "${sourceFolder}" \
-mindepth 1 \
-maxdepth 1 \
-exec cp -p -r '{}' "${destinationFolder}" \;
}
function createAbsoluteUsrBin()
{
local -r binFileName="${1}"
local -r sourceFilePath="${2}"
checkExistFile "${sourceFilePath}"
mkdir -p '/usr/bin'
printf "#!/bin/bash -e\n\n'%s' \"\${@}\"" "${sourceFilePath}" > "/usr/bin/${binFileName}"
chmod 755 "/usr/bin/${binFileName}"
}
function createFileFromTemplate()
{
local -r sourceFile="${1}"
local -r destinationFile="${2}"
local -r oldNewData=("${@:3}")
checkExistFile "${sourceFile}"
checkExistFolder "$(dirname "${destinationFile}")"
local content=''
content="$(cat "${sourceFile}")"
local i=0
for ((i = 0; i < ${#oldNewData[@]}; i = i + 2))
do
content="$(replaceString "${content}" "${oldNewData[${i}]}" "${oldNewData[${i} + 1]}")"
done
echo "${content}" > "${destinationFile}"
}
function createInitFileFromTemplate()
{
local -r serviceName="${1}"
local -r templateFolderPath="${2}"
local -r initConfigDataFromTemplate=("${@:3}")
createFileFromTemplate \
"${templateFolderPath}/${serviceName}.service.systemd" \
"/etc/systemd/system/${serviceName}.service" \
"${initConfigDataFromTemplate[@]}"
}
function deleteOldLogs()
{
local logFolderPaths=("${@}")
header 'DELETING OLD LOGS'
# Default Log Folder Path
if [[ "${#logFolderPaths[@]}" -lt '1' ]]
then
logFolderPaths+=('/var/log')
fi
# Walk Each Log Folder Path
local i=0
for ((i = 0; i < ${#logFolderPaths[@]}; i = i + 1))
do
checkExistFolder "${logFolderPaths[i]}"
find \
-L \
"${logFolderPaths[i]}" \
-type f \
\( \
-regex '.*-[0-9]+' -o \
-regex '.*\.[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\.log' -o \
-regex '.*\.[0-9][0-9][0-9][0-9]-[0-9][0-9]-[0-9][0-9]\.txt' -o \
-regex '.*\.[0-9]+' -o \
-regex '.*\.[0-9]+\.log' -o \
-regex '.*\.gz' -o \
-regex '.*\.log\.[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]T[0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9][0-9]' -o \
-regex '.*\.old' -o \
-regex '.*\.xz' \
\) \
-delete \
-print
done
}
function emptyFolder()
{
local -r folder="${1}"
checkExistFolder "${folder}"
find "${folder}" \
-mindepth 1 \
-delete
}
function getFileExtension()
{
local -r string="${1}"
local -r fullFileName="$(basename "${string}")"
echo "${fullFileName##*.}"
}
function getFileName()
{
local -r string="${1}"
local -r fullFileName="$(basename "${string}")"
echo "${fullFileName%.*}"
}
function getTemporaryFile()
{
local extension="${1}"
if [[ "$(isEmptyString "${extension}")" = 'false' && "$(grep -i -o "^." <<< "${extension}")" != '.' ]]
then
extension=".${extension}"
fi
mktemp "$(getTemporaryFolderRoot)/$(date +'%Y%m%d-%H%M%S')-XXXXXXXXXX${extension}"
}
function getTemporaryFolder()
{
mktemp -d "$(getTemporaryFolderRoot)/$(date +'%Y%m%d-%H%M%S')-XXXXXXXXXX"
}
function getTemporaryFolderRoot()
{
local temporaryFolder='/tmp'
if [[ "$(isEmptyString "${TMPDIR}")" = 'false' ]]
then
temporaryFolder="$(formatPath "${TMPDIR}")"
fi
echo "${temporaryFolder}"
}
function initializeFolder()
{
local -r folder="${1}"
if [[ -d "${folder}" ]]
then
emptyFolder "${folder}"
else
mkdir -p "${folder}"
fi
}
function isValidJSONContent()
{
local -r content="${1}"
if ( python -m 'json.tool' <<< "${content}" &> '/dev/null' )
then
echo 'true' && return 0
fi
echo 'false' && return 1
}
function isValidJSONFile()
{
local -r file="${1}"
checkExistFile "${file}"
isValidJSONContent "$(cat "${file}")"
}
function moveFolderContent()
{
local -r sourceFolder="${1}"
local -r destinationFolder="${2}"
checkExistFolder "${sourceFolder}"
checkExistFolder "${destinationFolder}"
find "${sourceFolder}" \
-mindepth 1 \
-maxdepth 1 \
-exec mv '{}' "${destinationFolder}" \;
}
function redirectOutputToLogFile()
{
local -r logFile="${1}"
mkdir -p "$(dirname "${logFile}")"
exec > >(tee -a "${logFile}") 2>&1
}
function resetFolderPermission()
{
local -r folderPath="${1}"
local -r userLogin="${2}"
local -r groupName="${3}"
checkExistFolder "${folderPath}"
checkExistUserLogin "${userLogin}"
checkExistGroupName "${groupName}"
header "RESETTING FOLDER PERMISSION ${folderPath}"
chown -R "${userLogin}:${groupName}" "${folderPath}"
find "${folderPath}" \
-type d \
\( \
-not -path "*/.git" -a \
-not -path "*/.git/*" \
\) \
-exec chmod 700 '{}' \; \
-print
find "${folderPath}" \
-type f \
\( \
-not -path "*/.git" -a \
-not -path "*/.git/*" \
\) \
-exec chmod 600 '{}' \; \
-print
}
function resetLogs()
{
local logFolderPaths=("${@}")
# Default Log Folder Path
if [[ "${#logFolderPaths[@]}" -lt '1' ]]
then
logFolderPaths+=('/var/log')
fi
# Delete Old Logs
deleteOldLogs "${logFolderPaths[@]}"
# Reset Logs
header 'RESETTING LOGS'
local i=0
for ((i = 0; i < ${#logFolderPaths[@]}; i = i + 1))
do
checkExistFolder "${logFolderPaths[i]}"
find "${logFolderPaths[i]}" \
-type f \
-exec cp -f '/dev/null' '{}' \; \
-print
done
}
function symlinkListUsrBin()
{
local -r sourceFilePaths=("${@}")
local sourceFilePath=''
for sourceFilePath in "${sourceFilePaths[@]}"
do
chmod 755 "${sourceFilePath}"
rm -f -r "/usr/bin/$(basename "${sourceFilePath}")"
ln -f -s "${sourceFilePath}" "/usr/bin/$(basename "${sourceFilePath}")"
done
}
function symlinkUsrBin()
{
local -r sourceBinFileOrFolder="${1}"
if [[ "$(isMacOperatingSystem)" = 'true' ]]
then
mkdir -p '/usr/bin'
if [[ -d "${sourceBinFileOrFolder}" ]]
then
find "${sourceBinFileOrFolder}" -maxdepth 1 \( -type f -o -type l \) -perm -u+x -exec bash -c -e '
for file
do
fileType="$(stat -f "%HT" "${file}")"
if [[ "${fileType}" = "Regular File" ]]
then
ln -f -s "${file}" "/usr/bin/$(basename "${file}")"
elif [[ "${fileType}" = "Symbolic Link" ]]
then
cd "$(dirname "${file}")"
if [[ -f "$(readlink "${file}")" ]]
then
ln -f -s "${file}" "/usr/bin/$(basename "${file}")"
fi
fi
done' bash '{}' \;
elif [[ -f "${sourceBinFileOrFolder}" ]]
then
ln -f -s "${sourceBinFileOrFolder}" "/usr/bin/$(basename "${sourceBinFileOrFolder}")"
else
fatal "\nFATAL : '${sourceBinFileOrFolder}' is not directory or file"
fi
elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' || "$(isUbuntuDistributor)" = 'true' ]]
then
mkdir -p '/usr/bin'
if [[ -d "${sourceBinFileOrFolder}" ]]
then
find "${sourceBinFileOrFolder}" -maxdepth 1 -xtype f -perm -u+x -exec bash -c -e '
for file
do
ln -f -s "${file}" "/usr/bin/$(basename "${file}")"
done' bash '{}' \;
elif [[ -f "${sourceBinFileOrFolder}" ]]
then
ln -f -s "${sourceBinFileOrFolder}" "/usr/bin/$(basename "${sourceBinFileOrFolder}")"
else
fatal "\nFATAL : '${sourceBinFileOrFolder}' is not directory or file"
fi
else
fatal '\nFATAL : only support Amazon-Linux, CentOS, Mac, RedHat, or Ubuntu OS'
fi
}
function trimFile()
{
local -r filePath="${1}"
checkExistFile "${filePath}"
printf '%s' "$(< "${filePath}")" > "${filePath}"
}
#########################
# FILE REMOTE UTILITIES #
#########################
function checkExistURL()
{
local -r url="${1}"
if [[ "$(existURL "${url}")" = 'false' ]]
then
fatal "\nFATAL : url '${url}' not found"
fi
}
function downloadFile()
{
local -r url="${1}"
local -r destinationFile="${2}"
local overwrite="${3}"
checkExistURL "${url}"
# Check Overwrite
if [[ "$(isEmptyString "${overwrite}")" = 'true' ]]
then
overwrite='false'
fi
checkTrueFalseString "${overwrite}"
# Validate
if [[ -f "${destinationFile}" ]]
then
if [[ "${overwrite}" = 'false' ]]
then
fatal "\nFATAL : file '${destinationFile}' found"
fi
rm -f "${destinationFile}"
elif [[ -e "${destinationFile}" ]]
then
fatal "\nFATAL : file '${destinationFile}' already exists"
fi
# Download
debug "\nDownloading '${url}' to '${destinationFile}'\n"
curl -L "${url}" -o "${destinationFile}" --retry 12 --retry-delay 5
}
function existURL()
{
local -r url="${1}"
# Install Curl
installCURLCommand > '/dev/null'
# Check URL
if ( curl -f --head -L "${url}" -o '/dev/null' -s --retry 12 --retry-delay 5 ||
curl -f -L "${url}" -o '/dev/null' -r 0-0 -s --retry 12 --retry-delay 5 )
then
echo 'true' && return 0
fi
echo 'false' && return 1
}
function getRemoteFileContent()
{
local -r url="${1}"
checkExistURL "${url}"
curl -s -X 'GET' -L "${url}" --retry 12 --retry-delay 5
}
function unzipRemoteFile()
{
local -r downloadURL="${1}"
local -r installFolder="${2}"
local extension="${3}"
# Install Curl
installCURLCommand
# Validate URL
checkExistURL "${downloadURL}"
# Find Extension
local exExtension=''
if [[ "$(isEmptyString "${extension}")" = 'true' ]]
then
extension="$(getFileExtension "${downloadURL}")"
exExtension="$(rev <<< "${downloadURL}" | cut -d '.' -f 1-2 | rev)"
fi
# Unzip
if [[ "$(grep -i '^tgz$' <<< "${extension}")" != '' || "$(grep -i '^tar\.gz$' <<< "${extension}")" != '' || "$(grep -i '^tar\.gz$' <<< "${exExtension}")" != '' ]]
then
debug "\nDownloading '${downloadURL}'\n"
curl -L "${downloadURL}" --retry 12 --retry-delay 5 | tar -C "${installFolder}" -x -z --strip 1
echo
elif [[ "$(grep -i '^tar\.bz2$' <<< "${exExtension}")" != '' ]]
then
# Install BZip2
installBZip2Command
# Unzip
debug "\nDownloading '${downloadURL}'\n"
curl -L "${downloadURL}" --retry 12 --retry-delay 5 | tar -C "${installFolder}" -j -x --strip 1
echo
elif [[ "$(grep -i '^zip$' <<< "${extension}")" != '' ]]
then
# Install Unzip
installUnzipCommand
# Unzip
if [[ "$(existCommand 'unzip')" = 'false' ]]
then
fatal 'FATAL : command unzip not found'
fi
local -r zipFile="${installFolder}/$(basename "${downloadURL}")"
downloadFile "${downloadURL}" "${zipFile}" 'true'
unzip -q "${zipFile}" -d "${installFolder}"
rm -f "${zipFile}"
echo
else
fatal "\nFATAL : file extension '${extension}' not supported"
fi
}
#################
# GIT UTILITIES #
#################
function getGitPrivateRepositorySSHURL()
{
local -r user="${1}"
local -r token="${2}"
local -r orgName="${3}"
local -r gitURL="${4}"
getGitUserRepositoryObjectKey "${user}" "${token}" 'ssh_url' 'private' "${orgName}" "${gitURL}"
}
function getGitPublicRepositorySSHURL()
{
local -r user="${1}"
local -r token="${2}"
local -r orgName="${3}"
local -r gitURL="${4}"
getGitUserRepositoryObjectKey "${user}" "${token}" 'ssh_url' 'public' "${orgName}" "${gitURL}"
}
function getGitRepositoryNameFromCloneURL()
{
local -r cloneURL="${1}"
checkNonEmptyString "${cloneURL}" 'undefined clone url'
if [[ "$(grep -F -o '@' <<< "${cloneURL}")" != '' ]]
then
awk -F '/' '{ print $2 }' <<< "${cloneURL}" | rev | cut -d '.' -f 2- | rev
else
awk -F '/' '{ print $5 }' <<< "${cloneURL}" | rev | cut -d '.' -f 2- | rev
fi
}
function getGitUserName()
{
local -r user="${1}"
local -r token="${2}"
local gitURL="${3}"
# Default Value
if [[ "$(isEmptyString "${gitURL}")" = 'true' ]]
then
gitURL='https://api.github.com'
fi
# Validation
checkNonEmptyString "${user}" 'undefined user'
checkNonEmptyString "${token}" 'undefined token'
# Get User Name
curl \
-s \
-X 'GET' \
-u "${user}:${token}" \
-L "${gitURL}/user" \
--retry 12 \
--retry-delay 5 |
jq \
--compact-output \
--raw-output \
--sort-keys \
'.["name"] // empty'
}
function getGitUserPrimaryEmail()
{
local -r user="${1}"
local -r token="${2}"
local gitURL="${3}"
# Default Values
if [[ "$(isEmptyString "${gitURL}")" = 'true' ]]
then
gitURL='https://api.github.com'
fi
# Validation
checkNonEmptyString "${user}" 'undefined user'
checkNonEmptyString "${token}" 'undefined token'
# Pagination
local page=1
local exitCount=0
for ((page = 1; page > exitCount; page = page + 1))
do
local emails=''
emails="$(
curl \
-s \
-X 'GET' \
-u "${user}:${token}" \
-L "${gitURL}/user/emails?page=${page}&per_page=100" \
--retry 12 \
--retry-delay 5 |
jq \
--compact-output \
--raw-output \
--sort-keys \
'.[] // empty' \
)"
local primaryEmail=''
primaryEmail="$(
jq \
--compact-output \
--raw-output \
--sort-keys \
'select(.["primary"] == true) |
.["email"] // empty' \
<<< "${emails}"
)"
if [[ "$(isEmptyString "${primaryEmail}")" = 'false' || "$(isEmptyString "${emails}")" = 'true' ]]
then
echo "${primaryEmail}"
exitCount="$((page + 1))"
fi
done
}
function getGitUserRepositoryObjectKey()
{
local -r user="${1}"
local -r token="${2}"
local -r objectKey="${3}"
local -r kind="${4}"
local -r orgName="${5}"
local gitURL="${6}"
# Default Value
if [[ "$(isEmptyString "${gitURL}")" = 'true' ]]
then
gitURL='https://api.github.com'
fi
# Validation
checkNonEmptyString "${user}" 'undefined user'
checkNonEmptyString "${token}" 'undefined token'
checkNonEmptyString "${objectKey}" 'undefined object key'
checkNonEmptyString "${gitURL}" 'undefined git url'
# Pagination
local results=''
local page=1
local exitCount=0
for ((page = 1; page > exitCount; page = page + 1))
do
# User or Organization
if [[ "$(isEmptyString "${orgName}")" = 'true' ]]
then
local targetURL="${gitURL}/user/repos?affiliation=owner&page=${page}&per_page=100&visibility=${kind}"
else
local targetURL="${gitURL}/orgs/${orgName}/repos?page=${page}&per_page=100&type=${kind}"
fi
# Retrieve Objects
local currentObjectValue=''
currentObjectValue="$(
curl \
-s \
-X 'GET' \
-u "${user}:${token}" \
-L "${targetURL}" \
--retry 12 \
--retry-delay 5 |
jq \
--arg jqObjectKey "${objectKey}" \
--compact-output \
--raw-output \
--sort-keys \
'.[] |
.[$jqObjectKey] // empty'
)"
if [[ "$(isEmptyString "${currentObjectValue}")" = 'true' ]]
then
exitCount="$((page + 1))"
elif [[ "${page}" = '1' ]]
then
results="$(printf '%s' "${currentObjectValue}")"
else
results="$(printf '%s\n%s' "${results}" "${currentObjectValue}")"
fi
done
# Return Results
echo "${results}" | sort -f
}
#####################
# INSTALL UTILITIES #
#####################
function installPortableBinary()
{
local -r appTitleName="${1}"
local -r downloadURL="${2}"
local -r installFolderPath="${3}"
local -r binarySubPaths=($(sortUniqArray "$(replaceString "${4}" ',' ' ')"))
local -r versionOption="${5}"
local -r remoteUnzip="${6}"
checkNonEmptyString "${appTitleName}" 'undefined app title name'
checkNonEmptyString "${versionOption}" 'undefined version option'
checkTrueFalseString "${remoteUnzip}"
if [[ "${#binarySubPaths[@]}" -lt '1' ]]
then
fatal '\nFATAL : undefined binary sub paths'
fi
header "INSTALLING ${appTitleName}"
checkRequireLinuxSystem
checkRequireRootUser
umask '0022'
initializeFolder "${installFolderPath}"
if [[ "${remoteUnzip}" = 'true' ]]
then
if [[ "$(getFileExtension "${downloadURL}")" = 'sh' ]]
then
curl -s -L "${downloadURL}" --retry 12 --retry-delay 5 | bash -e
else
unzipRemoteFile "${downloadURL}" "${installFolderPath}"
fi
printf '%s\n\nexport PATH="%s/%s:${PATH}"' \
'#!/bin/sh -e' \
"${installFolderPath}" \
"$(dirname "${binarySubPaths[0]}")" \
> "/etc/profile.d/$(basename "${installFolderPath}").sh"
chmod 644 "/etc/profile.d/$(basename "${installFolderPath}").sh"
else
downloadFile "${downloadURL}" "${installFolderPath}/${binarySubPaths[0]}" 'true'
fi
chown -R "$(whoami):$(whoami)" "${installFolderPath}"
local binarySubPath=''
for binarySubPath in "${binarySubPaths[@]}"
do
symlinkListUsrBin "${installFolderPath}/${binarySubPath}"
done
displayVersion "$("/usr/bin/$(basename "${binarySubPaths[0]}")" "${versionOption}")"
umask '0077'
installCleanUp
}
#################
# MAC UTILITIES #
#################
function clearMacAppExtendedAttributes()
{
local -r headerMessage="${1}"
local -r applicationPaths=("${@:2}")
checkRequireMacSystem
if [[ "${#applicationPaths[@]}" -gt '0' ]]
then
header "${headerMessage}"
fi
local applicationPath=''
for applicationPath in "${applicationPaths[@]}"
do
# Find Non-Default Apple App
if [[ "$(ls -d -l -O "${applicationPath}" | grep -E '\s+restricted\s+')" = '' ]]
then
info "clearing extended attributes of '${applicationPath}'"
xattr -c -r -s "${applicationPath}"
fi
done
}
function closeMacApplications()
{
local -r headerMessage="${1}"
local -r applicationNames=("${@:2}")
checkRequireMacSystem
if [[ "${#applicationNames[@]}" -gt '0' ]]
then
header "${headerMessage}"
fi
local applicationName=''
for applicationName in "${applicationNames[@]}"
do
applicationName="$(getFileName "${applicationName}")"
if [[ "${applicationName}" != 'Terminal' ]]
then
local errorMessage=''
errorMessage="$(osascript -e "tell application \"${applicationName}\" to quit" 2>&1)"
if [[ "$(isEmptyString "${errorMessage}")" = 'true' || "$(grep -E -o '\(-128)$' <<< "${errorMessage}")" != '' ]]
then
info "closing '${applicationName}'"
else
error "${errorMessage}"
fi
fi
done
}
function getMacCurrentUserICloudDriveFolderPath()
{
local -r iCloudFolderPath="$(getCurrentUserHomeFolder)/Library/Mobile Documents/com~apple~CloudDocs"
if [[ -d "${iCloudFolderPath}" ]]
then
echo "${iCloudFolderPath}"
else
echo
fi
}
function openMacApplications()
{
local -r headerMessage="${1}"
local -r applicationNames=("${@:2}")
checkRequireMacSystem
if [[ "${#applicationNames[@]}" -gt '0' ]]
then
header "${headerMessage}"
fi
local applicationName=''
for applicationName in "${applicationNames[@]}"
do
info "openning '${applicationName}'"
osascript -e "tell application \"${applicationName}\" to activate"
done
}
####################
# NUMBER UTILITIES #
####################
function checkNaturalNumber()
{
local -r string="${1}"
local -r errorMessage="${2}"
if [[ "$(isNaturalNumber "${string}")" = 'false' ]]
then
if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]]
then
fatal '\nFATAL : not natural number detected'
fi
fatal "\nFATAL : ${errorMessage}"
fi
}
function checkPositiveInteger()
{
local -r string="${1}"
local -r errorMessage="${2}"
if [[ "$(isPositiveInteger "${string}")" = 'false' ]]
then
if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]]
then
fatal '\nFATAL : not positive number detected'
fi
fatal "\nFATAL : ${errorMessage}"
fi
}
function isNaturalNumber()
{
local -r string="${1}"
if [[ "${string}" =~ ^[0-9]+$ ]]
then
echo 'true' && return 0
fi
echo 'false' && return 1
}
function isPositiveInteger()
{
local -r string="${1}"
if [[ "${string}" =~ ^[1-9][0-9]*$ ]]
then
echo 'true' && return 0
fi
echo 'false' && return 1
}
################
# OS UTILITIES #
################
function checkRequireLinuxSystem()
{
if [[ "$(isAmazonLinuxDistributor)" = 'false' && "$(isCentOSDistributor)" = 'false' && "$(isRedHatDistributor)" = 'false' && "$(isUbuntuDistributor)" = 'false' ]]
then
fatal '\nFATAL : only support Amazon-Linux, CentOS, RedHat, or Ubuntu OS'
fi
if [[ "$(is64BitSystem)" = 'false' ]]
then
fatal '\nFATAL : non x86_64 OS found'
fi
}
function checkRequireMacSystem()
{
if [[ "$(isMacOperatingSystem)" = 'false' ]]
then
fatal '\nFATAL : only support Mac OS'
fi
if [[ "$(is64BitSystem)" = 'false' ]]
then
fatal '\nFATAL : non x86_64 OS found'
fi
}
function getMachineDescription()
{
lsb_release -d -s
}
function getMachineRelease()
{
lsb_release -r -s
}
function is64BitSystem()
{
isMachineHardware 'x86_64'
}
function isAmazonLinuxDistributor()
{
isDistributor 'amzn'
}
function isCentOSDistributor()
{
isDistributor 'centos'
}
function isDistributor()
{
local -r distributor="${1}"
local -r found="$(grep -F -i -o -s "${distributor}" '/proc/version')"
if [[ "$(isEmptyString "${found}")" = 'true' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function isLinuxOperatingSystem()
{
isOperatingSystem 'Linux'
}
function isMachineHardware()
{
local -r machineHardware="$(escapeGrepSearchPattern "${1}")"
local -r found="$(uname -m | grep -E -i -o "^${machineHardware}$")"
if [[ "$(isEmptyString "${found}")" = 'true' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function isMacOperatingSystem()
{
isOperatingSystem 'Darwin'
}
function isOperatingSystem()
{
local -r operatingSystem="$(escapeGrepSearchPattern "${1}")"
local -r found="$(uname -s | grep -E -i -o "^${operatingSystem}$")"
if [[ "$(isEmptyString "${found}")" = 'true' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function isRedHatDistributor()
{
isDistributor 'redhat'
}
function isUbuntuDistributor()
{
isDistributor 'ubuntu'
}
#####################
# PACKAGE UTILITIES #
#####################
function getLastAptGetUpdate()
{
if [[ "$(isUbuntuDistributor)" = 'true' ]]
then
local -r aptDate="$(stat -c %Y '/var/cache/apt')"
local -r nowDate="$(date +'%s')"
echo $((nowDate - aptDate))
fi
}
function installBuildEssential()
{
if [[ "$(isUbuntuDistributor)" = 'true' ]]
then
installPackages 'build-essential'
elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' ]]
then
installPackages 'gcc-c++' 'kernel-devel' 'make'
else
fatal '\nFATAL : only support Amazon-Linux, CentOS, RedHat, or Ubuntu OS'
fi
}
function installBZip2Command()
{
local -r commandPackage=('bzip2' 'bzip2')
installCommands "${commandPackage[@]}"
}
function installCleanUp()
{
header 'CLEANING UP INSTALLATION'
if [[ "$(isUbuntuDistributor)" = 'true' ]]
then
DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' autoremove
DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' clean
DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' autoclean
elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' ]]
then
yum clean all
else
fatal '\nFATAL : only support Amazon-Linux, CentOS, RedHat, or Ubuntu OS'
fi
}
function installCommands()
{
local -r commandPackageData=("${@}")
if [[ "$(isUbuntuDistributor)" = 'true' ]]
then
runAptGetUpdate ''
fi
local i=0
for ((i = 0; i < ${#commandPackageData[@]}; i = i + 2))
do
local command="${commandPackageData[${i}]}"
local package="${commandPackageData[${i} + 1]}"
checkNonEmptyString "${command}" 'undefined command'
checkNonEmptyString "${package}" 'undefined package'
if [[ "$(existCommand "${command}")" = 'false' ]]
then
installPackages "${package}"
fi
done
}
function installCURLCommand()
{
local -r commandPackage=('curl' 'curl')
installCommands "${commandPackage[@]}"
}
function installPackage()
{
local -r aptPackage="${1}"
local -r rpmPackage="${2}"
if [[ "$(isUbuntuDistributor)" = 'true' ]]
then
if [[ "$(isEmptyString "${aptPackage}")" = 'false' ]]
then
if [[ "$(isAptGetPackageInstall "${aptPackage}")" = 'true' ]]
then
debug "\nApt-Get Package '${aptPackage}' has already been installed"
else
echo -e "\033[1;35m\nInstalling Apt-Get Package '${aptPackage}'\033[0m"
DEBIAN_FRONTEND='noninteractive' apt-get install "${aptPackage}" --fix-missing -y ||
(DEBIAN_FRONTEND='noninteractive' apt-get install --fix-missing --yes -f -y && DEBIAN_FRONTEND='noninteractive' apt-get install "${aptPackage}" --fix-missing -y)
fi
fi
elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' ]]
then
if [[ "$(isEmptyString "${rpmPackage}")" = 'false' ]]
then
yum install -y "${rpmPackage}"
fi
else
fatal '\nFATAL : only support Amazon-Linux, CentOS, RedHat, or Ubuntu OS'
fi
}
function installPackages()
{
local -r packages=("${@}")
if [[ "$(isUbuntuDistributor)" = 'true' ]]
then
runAptGetUpdate ''
fi
local package=''
for package in "${packages[@]}"
do
if [[ "$(isUbuntuDistributor)" = 'true' ]]
then
installPackage "${package}"
elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' ]]
then
installPackage '' "${package}"
else
fatal '\nFATAL : only support Amazon-Linux, CentOS, RedHat, or Ubuntu OS'
fi
done
}
function installPIPCommand()
{
local -r commandPackage=('pip' 'python-pip')
installCommands "${commandPackage[@]}"
}
function installPIPPackage()
{
local -r package="${1}"
if [[ "$(isPIPPackageInstall "${package}")" = 'true' ]]
then
debug "PIP Package '${package}' found"
else
echo -e "\033[1;35m\nInstalling PIP package '${package}'\033[0m"
pip install "${package}"
fi
}
function installUnzipCommand()
{
local -r commandPackage=('unzip' 'unzip')
installCommands "${commandPackage[@]}"
}
function isAptGetPackageInstall()
{
local -r package="$(escapeGrepSearchPattern "${1}")"
local -r found="$(dpkg --get-selections | grep -E -o "^${package}(:amd64)*\s+install$")"
if [[ "$(isEmptyString "${found}")" = 'true' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function isPIPPackageInstall()
{
local -r package="$(escapeGrepSearchPattern "${1}")"
# Install PIP
installPIPCommand > '/dev/null'
# Check Command
if [[ "$(existCommand 'pip')" = 'false' ]]
then
fatal 'FATAL : command python-pip not found'
fi
local -r found="$(pip list | grep -E -o "^${package}\s+\(.*\)$")"
if [[ "$(isEmptyString "${found}")" = 'true' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function runAptGetUpdate()
{
local updateInterval="${1}"
if [[ "$(isUbuntuDistributor)" = 'true' ]]
then
local -r lastAptGetUpdate="$(getLastAptGetUpdate)"
if [[ "$(isEmptyString "${updateInterval}")" = 'true' ]]
then
# Default To 24 hours
updateInterval="$((24 * 60 * 60))"
fi
if [[ "${lastAptGetUpdate}" -gt "${updateInterval}" ]]
then
info 'apt-get update'
apt-get update -m
else
local -r lastUpdate="$(date -u -d @"${lastAptGetUpdate}" +'%-Hh %-Mm %-Ss')"
info "\nSkip apt-get update because its last run was '${lastUpdate}' ago"
fi
fi
}
function runUpgrade()
{
header 'UPGRADING SYSTEM'
if [[ "$(isUbuntuDistributor)" = 'true' ]]
then
runAptGetUpdate ''
info '\napt-get upgrade'
DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' upgrade
info '\napt-get dist-upgrade'
DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' dist-upgrade
info '\napt-get autoremove'
DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' autoremove
info '\napt-get clean'
DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' clean
info '\napt-get autoclean'
DEBIAN_FRONTEND='noninteractive' apt-get --fix-missing -y -o Dpkg::Options::='--force-confdef' -o Dpkg::Options::='--force-confold' autoclean
elif [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isCentOSDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' ]]
then
yum -y --security update
yum -y update --nogpgcheck --skip-broken
fi
}
function upgradePIPPackage()
{
local -r package="${1}"
if [[ "$(isPIPPackageInstall "${package}")" = 'true' ]]
then
echo -e "\033[1;35mUpgrading PIP package '${package}'\033[0m"
pip install --upgrade "${package}"
else
debug "PIP Package '${package}' not found"
fi
}
#####################
# SERVICE UTILITIES #
#####################
function disableService()
{
local -r serviceName="${1}"
checkNonEmptyString "${serviceName}" 'undefined service name'
if [[ "$(existCommand 'systemctl')" = 'true' ]]
then
header "DISABLE SYSTEMD ${serviceName}"
systemctl daemon-reload
systemctl disable "${serviceName}"
systemctl stop "${serviceName}" || true
else
header "DISABLE SERVICE ${serviceName}"
chkconfig "${serviceName}" off
service "${serviceName}" stop || true
fi
statusService "${serviceName}"
}
function enableService()
{
local -r serviceName="${1}"
checkNonEmptyString "${serviceName}" 'undefined service name'
if [[ "$(existCommand 'systemctl')" = 'true' ]]
then
header "ENABLE SYSTEMD ${serviceName}"
systemctl daemon-reload
systemctl enable "${serviceName}" || true
else
header "ENABLE SERVICE ${serviceName}"
chkconfig "${serviceName}" on
fi
statusService "${serviceName}"
}
function restartService()
{
local -r serviceName="${1}"
checkNonEmptyString "${serviceName}" 'undefined service name'
stopService "${serviceName}"
startService "${serviceName}"
}
function startService()
{
local -r serviceName="${1}"
checkNonEmptyString "${serviceName}" 'undefined service name'
if [[ "$(existCommand 'systemctl')" = 'true' ]]
then
header "STARTING SYSTEMD ${serviceName}"
systemctl daemon-reload
systemctl enable "${serviceName}" || true
systemctl start "${serviceName}"
else
header "STARTING SERVICE ${serviceName}"
chkconfig "${serviceName}" on
service "${serviceName}" start
fi
statusService "${serviceName}"
}
function statusService()
{
local -r serviceName="${1}"
checkNonEmptyString "${serviceName}" 'undefined service name'
if [[ "$(existCommand 'systemctl')" = 'true' ]]
then
header "STATUS SYSTEMD ${serviceName}"
systemctl status "${serviceName}" --full --no-pager || true
else
header "STATUS SERVICE ${serviceName}"
service "${serviceName}" status || true
fi
}
function stopService()
{
local -r serviceName="${1}"
checkNonEmptyString "${serviceName}" 'undefined service name'
if [[ "$(existCommand 'systemctl')" = 'true' ]]
then
header "STOPPING SYSTEMD ${serviceName}"
systemctl daemon-reload
systemctl stop "${serviceName}" || true
else
header "STOPPING SERVICE ${serviceName}"
service "${serviceName}" stop || true
fi
statusService "${serviceName}"
}
####################
# STRING UTILITIES #
####################
function checkNonEmptyString()
{
local -r string="${1}"
local -r errorMessage="${2}"
if [[ "$(isEmptyString "${string}")" = 'true' ]]
then
if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]]
then
fatal '\nFATAL : empty value detected'
fi
fatal "\nFATAL : ${errorMessage}"
fi
}
function checkTrueFalseString()
{
local -r string="${1}"
local -r errorMessage="${2}"
if [[ "${string}" != 'true' && "${string}" != 'false' ]]
then
if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]]
then
fatal "\nFATAL : '${string}' is not 'true' or 'false'"
fi
fatal "\nFATAL : ${errorMessage}"
fi
}
function debug()
{
local -r message="${1}"
if [[ "$(isEmptyString "${message}")" = 'false' ]]
then
echo -e "\033[1;34m${message}\033[0m" 2>&1
fi
}
function deleteSpaces()
{
local -r content="${1}"
replaceString "${content}" ' ' ''
}
function displayVersion()
{
local -r message="${1}"
local -r applicationName="${2}"
if [[ "$(isEmptyString "${applicationName}")" = 'true' ]]
then
header 'DISPLAYING VERSION'
else
header "DISPLAYING ${applicationName} VERSION"
fi
info "${message}"
}
function encodeURL()
{
local -r url="${1}"
local i=0
for ((i = 0; i < ${#url}; i++))
do
local walker=''
walker="${url:i:1}"
case "${walker}" in
[a-zA-Z0-9.~_-])
printf '%s' "${walker}"
;;
' ')
printf +
;;
*)
printf '%%%X' "'${walker}"
;;
esac
done
}
function error()
{
local -r message="${1}"
if [[ "$(isEmptyString "${message}")" = 'false' ]]
then
echo -e "\033[1;31m${message}\033[0m" 1>&2
fi
}
function escapeGrepSearchPattern()
{
local -r searchPattern="${1}"
sed 's/[]\.|$(){}?+*^]/\\&/g' <<< "${searchPattern}"
}
function escapeSearchPattern()
{
local -r searchPattern="${1}"
sed -e "s@\@@\\\\\\@@g" -e "s@\[@\\\\[@g" -e "s@\*@\\\\*@g" -e "s@\%@\\\\%@g" <<< "${searchPattern}"
}
function fatal()
{
local -r message="${1}"
error "${message}"
exit 1
}
function formatPath()
{
local path="${1}"
while [[ "$(grep -F '//' <<< "${path}")" != '' ]]
do
path="$(sed -e 's/\/\/*/\//g' <<< "${path}")"
done
sed -e 's/\/$//g' <<< "${path}"
}
function header()
{
local -r title="${1}"
if [[ "$(isEmptyString "${title}")" = 'false' ]]
then
echo -e "\n\033[1;33m>>>>>>>>>> \033[1;4;35m${title}\033[0m \033[1;33m<<<<<<<<<<\033[0m\n"
fi
}
function indentString()
{
local -r indentString="$(escapeSearchPattern "${1}")"
local -r string="$(escapeSearchPattern "${2}")"
sed "s@^@${indentString}@g" <<< "${string}"
}
function info()
{
local -r message="${1}"
if [[ "$(isEmptyString "${message}")" = 'false' ]]
then
echo -e "\033[1;36m${message}\033[0m" 2>&1
fi
}
function invertTrueFalseString()
{
local -r string="${1}"
checkTrueFalseString "${string}"
if [[ "${string}" = 'true' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function isEmptyString()
{
local -r string="${1}"
if [[ "$(trimString "${string}")" = '' ]]
then
echo 'true' && return 0
fi
echo 'false' && return 1
}
function postUpMessage()
{
echo -e "\n\033[1;32m¯\_(ツ)_/¯\033[0m"
}
function printTable()
{
local -r delimiter="${1}"
local -r tableData="$(removeEmptyLines "${2}")"
local -r colorHeader="${3}"
local -r displayTotalCount="${4}"
if [[ "${delimiter}" != '' && "$(isEmptyString "${tableData}")" = 'false' ]]
then
local -r numberOfLines="$(trimString "$(wc -l <<< "${tableData}")")"
if [[ "${numberOfLines}" -gt '0' ]]
then
local table=''
local i=1
for ((i = 1; i <= "${numberOfLines}"; i = i + 1))
do
local line=''
line="$(sed "${i}q;d" <<< "${tableData}")"
local numberOfColumns=0
numberOfColumns="$(awk -F "${delimiter}" '{print NF}' <<< "${line}")"
# Add Line Delimiter
if [[ "${i}" -eq '1' ]]
then
table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")"
fi
# Add Header Or Body
table="${table}\n"
local j=1
for ((j = 1; j <= "${numberOfColumns}"; j = j + 1))
do
table="${table}$(printf '#| %s' "$(cut -d "${delimiter}" -f "${j}" <<< "${line}")")"
done
table="${table}#|\n"
# Add Line Delimiter
if [[ "${i}" -eq '1' ]] || [[ "${numberOfLines}" -gt '1' && "${i}" -eq "${numberOfLines}" ]]
then
table="${table}$(printf '%s#+' "$(repeatString '#+' "${numberOfColumns}")")"
fi
done
if [[ "$(isEmptyString "${table}")" = 'false' ]]
then
local output=''
output="$(echo -e "${table}" | column -s '#' -t | awk '/^\+/{gsub(" ", "-", $0)}1')"
if [[ "${colorHeader}" = 'true' ]]
then
echo -e "\033[1;32m$(head -n 3 <<< "${output}")\033[0m"
tail -n +4 <<< "${output}"
else
echo "${output}"
fi
fi
fi
if [[ "${displayTotalCount}" = 'true' && "${numberOfLines}" -ge '0' ]]
then
echo -e "\n\033[1;36mTOTAL ROWS : $((numberOfLines - 1))\033[0m"
fi
fi
}
function removeEmptyLines()
{
local -r content="${1}"
echo -e "${content}" | sed '/^\s*$/d'
}
function repeatString()
{
local -r string="${1}"
local -r numberToRepeat="${2}"
if [[ "${string}" != '' && "$(isPositiveInteger "${numberToRepeat}")" = 'true' ]]
then
local -r result="$(printf "%${numberToRepeat}s")"
echo -e "${result// /${string}}"
fi
}
function replaceString()
{
local -r content="${1}"
local -r oldValue="$(escapeSearchPattern "${2}")"
local -r newValue="$(escapeSearchPattern "${3}")"
sed "s@${oldValue}@${newValue}@g" <<< "${content}"
}
function stringToNumber()
{
local -r string="${1}"
checkNonEmptyString "${string}" 'undefined string'
if [[ "$(existCommand 'md5')" = 'true' ]]
then
md5 <<< "${string}" | tr -cd '0-9'
elif [[ "$(existCommand 'md5sum')" = 'true' ]]
then
md5sum <<< "${string}" | tr -cd '0-9'
else
fatal '\nFATAL : md5 or md5sum command not found'
fi
}
function stringToSearchPattern()
{
local -r string="$(trimString "${1}")"
if [[ "$(isEmptyString "${string}")" = 'true' ]]
then
echo "${string}"
else
echo "^\s*$(sed -e 's/\s\+/\\s+/g' <<< "$(escapeSearchPattern "${string}")")\s*$"
fi
}
function trimString()
{
local -r string="${1}"
sed 's,^[[:blank:]]*,,' <<< "${string}" | sed 's,[[:blank:]]*$,,'
}
function warn()
{
local -r message="${1}"
if [[ "$(isEmptyString "${message}")" = 'false' ]]
then
echo -e "\033[1;33m${message}\033[0m" 1>&2
fi
}
####################
# SYSTEM UTILITIES #
####################
function addSwapSpace()
{
local swapSize="${1}"
local swapFile="${2}"
header 'ADDING SWAP SPACE'
# Set Default Values
if [[ "$(isEmptyString "${swapSize}")" = 'true' ]]
then
swapSize='1024000'
fi
if [[ "$(isEmptyString "${swapFile}")" = 'true' ]]
then
swapFile='/mnt/swapfile'
fi
if [[ -f "${swapFile}" ]]
then
swapoff "${swapFile}"
fi
rm -f "${swapFile}"
touch "${swapFile}"
# Create Swap File
dd if=/dev/zero of="${swapFile}" bs=1024 count="${swapSize}"
mkswap "${swapFile}"
chmod 600 "${swapFile}"
swapon "${swapFile}"
# Config Swap File System
local -r fstabConfig="${swapFile} swap swap defaults 0 0"
appendToFileIfNotFound '/etc/fstab' "$(stringToSearchPattern "${fstabConfig}")" "${fstabConfig}" 'true' 'false' 'true'
# Display Swap Status
free -m
}
function checkExistCommand()
{
local -r command="${1}"
local -r errorMessage="${2}"
if [[ "$(existCommand "${command}")" = 'false' ]]
then
if [[ "$(isEmptyString "${errorMessage}")" = 'true' ]]
then
fatal "\nFATAL : command '${command}' not found"
fi
fatal "\nFATAL : ${errorMessage}"
fi
}
function checkRequirePorts()
{
local -r ports=("${@}")
installPackages 'lsof'
local -r headerRegex='^COMMAND\s\+PID\s\+USER\s\+FD\s\+TYPE\s\+DEVICE\s\+SIZE\/OFF\s\+NODE\s\+NAME$'
local -r status="$(lsof -i -n -P | grep "\( (LISTEN)$\)\|\(${headerRegex}\)")"
local open=''
local port=''
for port in "${ports[@]}"
do
local found=''
found="$(grep -i ":${port} (LISTEN)$" <<< "${status}" || echo)"
if [[ "$(isEmptyString "${found}")" = 'false' ]]
then
open="${open}\n${found}"
fi
done
if [[ "$(isEmptyString "${open}")" = 'false' ]]
then
echo -e "\033[1;31mFollowing ports are still opened. Make sure you uninstall or stop them before a new installation!\033[0m"
echo -e -n "\033[1;34m\n$(grep "${headerRegex}" <<< "${status}")\033[0m"
echo -e "\033[1;36m${open}\033[0m\n"
exit 1
fi
}
function displayOpenPorts()
{
local -r sleepTimeInSecond="${1}"
installPackages 'lsof'
header 'DISPLAYING OPEN PORTS'
if [[ "$(isEmptyString "${sleepTimeInSecond}")" = 'false' ]]
then
sleep "${sleepTimeInSecond}"
fi
lsof -i -n -P | grep -i ' (LISTEN)$' | sort -f
}
function existCommand()
{
local -r command="${1}"
if [[ "$(which "${command}" 2> '/dev/null')" = '' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function existDisk()
{
local -r disk="${1}"
local -r foundDisk="$(fdisk -l "${disk}" 2> '/dev/null' | grep -E -i -o "^Disk\s+$(escapeGrepSearchPattern "${disk}"): ")"
if [[ "$(isEmptyString "${disk}")" = 'false' && "$(isEmptyString "${foundDisk}")" = 'false' ]]
then
echo 'true' && return 0
fi
echo 'false' && return 1
}
function existDiskMount()
{
local -r disk="$(escapeGrepSearchPattern "${1}")"
local -r mountOn="$(escapeGrepSearchPattern "${2}")"
local -r foundMount="$(df | grep -E "^${disk}\s+.*\s+${mountOn}$")"
if [[ "$(isEmptyString "${foundMount}")" = 'true' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function existModule()
{
local -r module="${1}"
checkNonEmptyString "${module}" 'undefined module'
if [[ "$(lsmod | awk '{ print $1 }' | grep -F -o "${module}")" = '' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function existMount()
{
local -r mountOn="$(escapeGrepSearchPattern "${1}")"
local -r foundMount="$(df | grep -E ".*\s+${mountOn}$")"
if [[ "$(isEmptyString "${foundMount}")" = 'true' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function flushFirewall()
{
header 'FLUSHING FIREWALL'
iptables -P INPUT ACCEPT
iptables -P FORWARD ACCEPT
iptables -P OUTPUT ACCEPT
iptables -t nat -F
iptables -t mangle -F
iptables -F
iptables -X
iptables --list
saveFirewall
}
function isPortOpen()
{
local -r port="$(escapeGrepSearchPattern "${1}")"
checkNonEmptyString "${port}" 'undefined port'
if [[ "$(isAmazonLinuxDistributor)" = 'true' || "$(isRedHatDistributor)" = 'true' || "$(isUbuntuDistributor)" = 'true' ]]
then
local -r process="$(netstat -l -n -t -u | grep -E ":${port}\s+" | head -1)"
elif [[ "$(isCentOSDistributor)" = 'true' || "$(isMacOperatingSystem)" = 'true' ]]
then
if [[ "$(isCentOSDistributor)" = 'true' ]]
then
installPackages 'lsof'
fi
local -r process="$(lsof -i -n -P | grep -E -i ":${port}\s+\(LISTEN\)$" | head -1)"
else
fatal '\nFATAL : only support Amazon-Linux, CentOS, Mac, RedHat, or Ubuntu OS'
fi
if [[ "$(isEmptyString "${process}")" = 'true' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function redirectJDKTMPDir()
{
local -r option="_JAVA_OPTIONS='-Djava.io.tmpdir=/var/tmp'"
appendToFileIfNotFound '/etc/environment' "${option}" "${option}" 'false' 'false' 'true'
appendToFileIfNotFound '/etc/profile' "${option}" "${option}" 'false' 'false' 'true'
}
function remountTMP()
{
header 'RE-MOUNTING TMP'
if [[ "$(existMount '/tmp')" = 'true' ]]
then
mount -o 'remount,rw,exec,nosuid' -v '/tmp'
else
warn 'WARN : mount /tmp not found'
fi
}
function saveFirewall()
{
header 'SAVING FIREWALL'
local ruleFile=''
for ruleFile in '/etc/iptables/rules.v4' '/etc/iptables/rules.v6' '/etc/sysconfig/iptables' '/etc/sysconfig/ip6tables'
do
if [[ -f "${ruleFile}" ]]
then
if [[ "$(grep -F '6' <<< "${ruleFile}")" = '' ]]
then
iptables-save > "${ruleFile}"
else
ip6tables-save > "${ruleFile}"
fi
info "${ruleFile}"
cat "${ruleFile}"
echo
fi
done
}
############################
# USER AND GROUP UTILITIES #
############################
function addUser()
{
local -r userLogin="${1}"
local -r groupName="${2}"
local -r createHome="${3}"
local -r systemAccount="${4}"
local -r allowLogin="${5}"
checkNonEmptyString "${userLogin}" 'undefined user login'
checkNonEmptyString "${groupName}" 'undefined group name'
# Options
if [[ "${createHome}" = 'true' ]]
then
local -r createHomeOption=('-m')
else
local -r createHomeOption=('-M')
fi
if [[ "${allowLogin}" = 'true' ]]
then
local -r allowLoginOption=('-s' '/bin/bash')
else
local -r allowLoginOption=('-s' '/bin/false')
fi
# Add Group
groupadd -f -r "${groupName}"
# Add User
if [[ "$(existUserLogin "${userLogin}")" = 'true' ]]
then
if [[ "$(isUserLoginInGroupName "${userLogin}" "${groupName}")" = 'false' ]]
then
usermod -a -G "${groupName}" "${userLogin}"
fi
# Not Exist Home
if [[ "${createHome}" = 'true' ]]
then
local -r userHome="$(getUserHomeFolder "${userLogin}")"
if [[ "$(isEmptyString "${userHome}")" = 'true' || ! -d "${userHome}" ]]
then
mkdir -m 700 -p "/home/${userLogin}"
chown -R "${userLogin}:${groupName}" "/home/${userLogin}"
fi
fi
else
if [[ "${systemAccount}" = 'true' ]]
then
useradd "${createHomeOption[@]}" -r "${allowLoginOption[@]}" -g "${groupName}" "${userLogin}"
else
useradd "${createHomeOption[@]}" "${allowLoginOption[@]}" -g "${groupName}" "${userLogin}"
fi
fi
}
function addUserAuthorizedKey()
{
local -r userLogin="${1}"
local -r groupName="${2}"
local -r sshRSA="${3}"
configUserSSH "${userLogin}" "${groupName}" "${sshRSA}" 'authorized_keys'
}
function addUserSSHKnownHost()
{
local -r userLogin="${1}"
local -r groupName="${2}"
local -r sshRSA="${3}"
configUserSSH "${userLogin}" "${groupName}" "${sshRSA}" 'known_hosts'
}
function addUserToSudoWithoutPassword()
{
local -r userLogin="${1}"
echo "${userLogin} ALL=(ALL) NOPASSWD:ALL" > "/etc/sudoers.d/${userLogin}"
chmod 440 "/etc/sudoers.d/${userLogin}"
}
function checkExistGroupName()
{
local -r groupName="${1}"
if [[ "$(existGroupName "${groupName}")" = 'false' ]]
then
fatal "\nFATAL : group name '${groupName}' not found"
fi
}
function checkExistUserLogin()
{
local -r userLogin="${1}"
if [[ "$(existUserLogin "${userLogin}")" = 'false' ]]
then
fatal "\nFATAL : user login '${userLogin}' not found"
fi
}
function checkRequireNonRootUser()
{
if [[ "$(whoami)" = 'root' ]]
then
fatal '\nFATAL : non root login required'
fi
}
function checkRequireRootUser()
{
checkRequireUserLogin 'root'
}
function checkRequireUserLogin()
{
local -r userLogin="${1}"
if [[ "$(whoami)" != "${userLogin}" ]]
then
fatal "\nFATAL : user login '${userLogin}' required"
fi
}
function configUserGIT()
{
local -r userLogin="${1}"
local -r gitUserName="${2}"
local -r gitUserEmail="${3}"
header "CONFIGURING GIT FOR USER ${userLogin}"
checkExistUserLogin "${userLogin}"
checkNonEmptyString "${gitUserName}" 'undefined git user name'
checkNonEmptyString "${gitUserEmail}" 'undefined git user email'
su -l "${userLogin}" -c "git config --global user.name '${gitUserName}'"
su -l "${userLogin}" -c "git config --global user.email '${gitUserEmail}'"
su -l "${userLogin}" -c 'git config --global push.default simple'
info "$(su -l "${userLogin}" -c 'git config --list')"
}
function configUserSSH()
{
local -r userLogin="${1}"
local -r groupName="${2}"
local -r sshRSA="${3}"
local -r configFileName="${4}"
header "CONFIGURING ${configFileName} FOR USER ${userLogin}"
checkExistUserLogin "${userLogin}"
checkExistGroupName "${groupName}"
checkNonEmptyString "${sshRSA}" 'undefined SSH-RSA'
checkNonEmptyString "${configFileName}" 'undefined config file'
local -r userHome="$(getUserHomeFolder "${userLogin}")"
checkExistFolder "${userHome}"
mkdir -m 700 -p "${userHome}/.ssh"
touch "${userHome}/.ssh/${configFileName}"
appendToFileIfNotFound "${userHome}/.ssh/${configFileName}" "${sshRSA}" "${sshRSA}" 'false' 'false' 'false'
chmod 600 "${userHome}/.ssh/${configFileName}"
chown -R "${userLogin}:${groupName}" "${userHome}/.ssh"
cat "${userHome}/.ssh/${configFileName}"
}
function deleteUser()
{
local -r userLogin="${1}"
if [[ "$(existUserLogin "${userLogin}")" = 'true' ]]
then
userdel -f -r "${userLogin}" 2> '/dev/null' || true
fi
}
function existGroupName()
{
local -r group="${1}"
if [[ "$(grep -E -o "^${group}:" '/etc/group')" = '' ]]
then
echo 'false' && return 1
fi
echo 'true' && return 0
}
function existUserLogin()
{
local -r user="${1}"
if ( id -u "${user}" > '/dev/null' 2>&1 )
then
echo 'true' && return 0
fi
echo 'false' && return 1
}
function generateSSHPublicKeyFromPrivateKey()
{
local -r userLogin="${1}"
local groupName="${2}"
# Set Default
if [[ "$(isEmptyString "${groupName}")" = 'true' ]]
then
groupName="${userLogin}"
fi
# Validate Input
checkExistUserLogin "${userLogin}"
checkExistGroupName "${groupName}"
local -r userHome="$(getUserHomeFolder "${userLogin}")"
checkExistFile "${userHome}/.ssh/id_rsa"
# Generate SSH Public Key
header "GENERATING SSH PUBLIC KEY FOR USER '${userLogin}' FROM PRIVATE KEY"
rm -f "${userHome}/.ssh/id_rsa.pub"
su -l "${userLogin}" -c "ssh-keygen -f '${userHome}/.ssh/id_rsa' -y > '${userHome}/.ssh/id_rsa.pub'"
chmod 600 "${userHome}/.ssh/id_rsa.pub"
chown "${userLogin}:${groupName}" "${userHome}/.ssh/id_rsa.pub"
cat "${userHome}/.ssh/id_rsa.pub"
}
function generateUserSSHKey()
{
local -r userLogin="${1}"
local groupName="${2}"
# Set Default
if [[ "$(isEmptyString "${groupName}")" = 'true' ]]
then
groupName="${userLogin}"
fi
# Validate Input
checkExistUserLogin "${userLogin}"
checkExistGroupName "${groupName}"
local -r userHome="$(getUserHomeFolder "${userLogin}")"
checkExistFolder "${userHome}"
# Generate SSH Key
header "GENERATING SSH KEY FOR USER '${userLogin}'"
rm -f "${userHome}/.ssh/id_rsa" "${userHome}/.ssh/id_rsa.pub"
mkdir -m 700 -p "${userHome}/.ssh"
chown "${userLogin}:${groupName}" "${userHome}/.ssh"
su -l "${userLogin}" -c "ssh-keygen -q -t rsa -N '' -f '${userHome}/.ssh/id_rsa'"
chmod 600 "${userHome}/.ssh/id_rsa" "${userHome}/.ssh/id_rsa.pub"
chown "${userLogin}:${groupName}" "${userHome}/.ssh/id_rsa" "${userHome}/.ssh/id_rsa.pub"
cat "${userHome}/.ssh/id_rsa.pub"
}
function getCurrentUserHomeFolder()
{
getUserHomeFolder "$(whoami)"
}
function getProfileFilePath()
{
local -r user="${1}"
local -r userHome="$(getUserHomeFolder "${user}")"
if [[ "$(isEmptyString "${userHome}")" = 'false' && -d "${userHome}" ]]
then
local -r bashProfileFilePath="${userHome}/.bash_profile"
local -r profileFilePath="${userHome}/.profile"
if [[ ! -f "${bashProfileFilePath}" && -f "${profileFilePath}" ]]
then
echo "${profileFilePath}"
else
echo "${bashProfileFilePath}"
fi
fi
}
function getUserGroupName()
{
local -r userLogin="${1}"
checkExistUserLogin "${userLogin}"
id -g -n "${userLogin}"
}
function getUserHomeFolder()
{
local -r user="${1}"
if [[ "$(isEmptyString "${user}")" = 'false' ]]
then
local -r homeFolder="$(eval "echo ~${user}")"
if [[ "${homeFolder}" = "\~${user}" ]]
then
echo
else
echo "${homeFolder}"
fi
else
echo
fi
}
function isUserLoginInGroupName()
{
local -r userLogin="${1}"
local -r groupName="${2}"
checkNonEmptyString "${userLogin}" 'undefined user login'
checkNonEmptyString "${groupName}" 'undefined group name'
if [[ "$(existUserLogin "${userLogin}")" = 'true' ]] && [[ "$(groups "${userLogin}" | grep "\b${groupName}\b")" != '' ]]
then
echo 'true' && return 0
fi
echo 'false' && return 1
}