# Unless explicitly stated otherwise all files in this repository are licensed
# under the Apache License Version 2.0.
# This product includes software developed at Datadog (https://www.datadoghq.com/).
# Copyright 2016-present Datadog, Inc.
# Datadog Agent install script for macOS.
set -e
install_script_version=1.4.0
dmg_file=/tmp/datadog-agent.dmg
dmg_base_url="https://s3.amazonaws.com/dd-agent"
etc_dir=/opt/datadog-agent/etc
log_dir=/opt/datadog-agent/logs
run_dir=/opt/datadog-agent/run
service_name="com.datadoghq.agent"
systemwide_servicefile_name="/Library/LaunchDaemons/${service_name}.plist"
if [ -n "$DD_REPO_URL" ]; then
dmg_base_url=$DD_REPO_URL
fi
upgrade=
if [ -n "$DD_UPGRADE" ]; then
upgrade=$DD_UPGRADE
fi
# Root user detection
if [ "$(echo "$UID")" = "0" ]; then
sudo_cmd=''
else
sudo_cmd='sudo'
fi
apikey=
if [ -n "$DD_API_KEY" ]; then
apikey=$DD_API_KEY
fi
site=
if [ -n "$DD_SITE" ]; then
site=$DD_SITE
fi
agent_dist_channel=
if [ -n "$DD_AGENT_DIST_CHANNEL" ]; then
agent_dist_channel="$DD_AGENT_DIST_CHANNEL"
fi
if [ -n "$DD_AGENT_MINOR_VERSION" ]; then
# Examples:
# - 20 = defaults to highest patch version x.20.2
# - 20.0 = sets explicit patch version x.20.0
# Note: Specifying an invalid minor version will terminate the script.
agent_minor_version=${DD_AGENT_MINOR_VERSION}
# Handle pre-release versions like "35.0~rc.5" -> "35.0" or "27.1~viper~conflict~fix" -> "27.1"
clean_agent_minor_version=$(echo "${DD_AGENT_MINOR_VERSION}" | sed -E 's/-.*//g')
# remove the patch version if the minor version includes it (eg: 33.1 -> 33)
agent_minor_version_without_patch="${clean_agent_minor_version%.*}"
if [ "$clean_agent_minor_version" != "$agent_minor_version_without_patch" ]; then
agent_patch_version="${clean_agent_minor_version#*.}"
fi
fi
arch=$(/usr/bin/arch)
if [ "$arch" == "arm64" ]; then
if ! /usr/bin/pgrep oahd >/dev/null 2>&1; then
printf "\033[31mRosetta is needed to run datadog-agent on $arch.\nYou can install it by running the following command :\n/usr/sbin/softwareupdate --install-rosetta --agree-to-license\033[0m\n"
exit 1
fi
fi
# Cleanup tmp files used for installation
rm -f /tmp/install-ddagent/system-wide
function find_latest_patch_version_for() {
major_minor="$1"
patch_versions=$(curl "https://s3.amazonaws.com/dd-agent?prefix=datadog-agent-${major_minor}." 2>/dev/null | grep -o "datadog-agent-${major_minor}.[0-9]*-1.dmg")
if [ -z "$patch_versions" ]; then
echo "-1"
fi
# first `cut` extracts patch version and `-1`, e.g. 2-1
# second `cut` removes the `-1`, e.g. 2
# then we sort numerically in reverse order
# and finally take the first (== the latest) patch version
latest_patch=$(echo "$patch_versions" | cut -d. -f3 | cut -d- -f1 | sort -rn | head -n 1)
echo "$latest_patch"
}
function prepare_dmg_file() {
dmg_file_to_prepare=$1
$sudo_cmd rm -f "$dmg_file_to_prepare"
$sudo_cmd touch "$dmg_file_to_prepare"
$sudo_cmd chmod 600 "$dmg_file_to_prepare"
if stat --help >/dev/null 2>&1; then # Handle differences between GNU and BSD stat command
file_owner=$(stat -c %u "$dmg_file_to_prepare")
file_permission=$(stat -c %a "$dmg_file_to_prepare")
file_size=$(stat -c %s "$dmg_file_to_prepare")
else
file_owner=$(stat -f %u "$dmg_file_to_prepare")
file_permission=$(stat -f %OLp "$dmg_file_to_prepare")
file_size=$(stat -f %z "$dmg_file_to_prepare")
fi
if [[ "$file_owner" -ne 0 ]] || [[ "$file_permission" -ne 600 ]] || [[ "$file_size" -ne 0 ]]; then
echo -e "\033[31mFailed to prepare datadog-agent dmg file\033[0m\n"
exit 1;
fi
}
systemdaemon_install=false
systemdaemon_user_group=
if [ -n "$DD_SYSTEMDAEMON_INSTALL" ]; then
systemdaemon_install=$DD_SYSTEMDAEMON_INSTALL
if [ -n "$DD_SYSTEMDAEMON_USER_GROUP" ]; then
systemdaemon_user_group=$DD_SYSTEMDAEMON_USER_GROUP
else
printf "\033[31mDD_SYSTEMDAEMON_INSTALL set without DD_SYSTEDAEMON_USER_GROUP\033[0m\n"
exit 1;
fi
if ! echo "$systemdaemon_user_group" | grep "^[^:]\+:[^:]\+$" > /dev/null; then
printf "\033[31mDD_SYSTEMDAEMON_USER_GROUP must be in format UserName:GroupName\033[0m\n"
exit 1;
fi
if echo "$systemdaemon_user_group" | grep ">\|<" > /dev/null; then
printf "\033[31mDD_SYSTEMDAEMON_USER_GROUP can't contain '>' or '<', because it will be used in XML file\033[0m\n"
exit 1;
fi
systemdaemon_user="$(echo "$systemdaemon_user_group" | awk -F: '{ print $1 }')"
systemdaemon_group="$(echo "$systemdaemon_user_group" | awk -F: '{ print $2 }')"
if ! id -u "$systemdaemon_user" >/dev/null 2>&1 ; then
printf "\033[31mUser $systemdaemon_user not found, can't proceed with installation\033[0m\n"
exit 1;
fi
# dscacheutil -q group output is in form:
# name: groupname
# password: *
# gid: 1001
# users: user1 user2
# so we use `grep` and `awk` to get the group name
if ! dscacheutil -q group | grep "name:" | awk '{print $2}' | grep -w "$systemdaemon_group" >/dev/null 2>&1; then
printf "\033[31mGroup $systemdaemon_group not found, can't proceed with installation\033[0m\n"
exit 1;
fi
fi
if [ "$systemdaemon_install" != false ]; then
mkdir -p /tmp/install-ddagent
touch /tmp/install-ddagent/system-wide
fi
macos_full_version=$(sw_vers -productVersion)
macos_major_version=$(echo "${macos_full_version}" | cut -d '.' -f 1)
macos_minor_version=$(echo "${macos_full_version}" | cut -d '.' -f 2)
agent_major_version=7
if [ -n "$DD_AGENT_MAJOR_VERSION" ]; then
if [ "$DD_AGENT_MAJOR_VERSION" != "6" ] && [ "$DD_AGENT_MAJOR_VERSION" != "7" ]; then
echo "DD_AGENT_MAJOR_VERSION must be either 6 or 7. Current value: $DD_AGENT_MAJOR_VERSION"
exit 1;
fi
agent_major_version=$DD_AGENT_MAJOR_VERSION
else
echo -e "\033[33mWarning: DD_AGENT_MAJOR_VERSION not set. Installing Agent version 7 by default.\033[0m"
fi
dmg_version=
if [ "${macos_major_version}" -lt 10 ] || { [ "${macos_major_version}" -eq 10 ] && [ "${macos_minor_version}" -lt 12 ]; }; then
echo -e "\033[31mDatadog Agent doesn't support macOS < 10.12.\033[0m\n"
exit 1
elif [ "${macos_major_version}" -eq 10 ] && [ "${macos_minor_version}" -eq 12 ]; then
if [ -n "${clean_agent_minor_version}" ]; then
if [ "${agent_minor_version_without_patch}" -gt 34 ]; then
echo -e "\033[31mmacOS 10.12 only supports Datadog Agent $agent_major_version up to $agent_major_version.34.\033[0m\n"
exit 1;
fi
else
echo -e "\033[33mWarning: Agent ${agent_major_version}.34.0 is the last supported version for macOS 10.12. Selecting it for installation.\033[0m"
agent_minor_version_without_patch=34
agent_patch_version=0
fi
elif [ "${macos_major_version}" -eq 10 ] && [ "${macos_minor_version}" -eq 13 ]; then
if [ -n "${clean_agent_minor_version}" ]; then
if [ "${agent_minor_version_without_patch}" -gt 38 ]; then
echo -e "\033[31mmacOS 10.13 only supports Datadog Agent $agent_major_version up to $agent_major_version.38.\033[0m\n"
exit 1;
fi
else
echo -e "\033[33mWarning: Agent ${agent_major_version}.38.2 is the last supported version for macOS 10.13. Selecting it for installation.\033[0m"
agent_minor_version_without_patch=38
agent_patch_version=2
fi
else
if [ "${agent_major_version}" -eq 6 ]; then
echo -e "\033[31mThe latest Agent 6 is no longer built for for macOS $macos_full_version. Please invoke again with DD_AGENT_MAJOR_VERSION=7\033[0m\n"
exit 1
else
if [ -z "${agent_minor_version}" ]; then
dmg_version="7-latest"
fi
fi
fi
if [ -z "$dmg_version" ]; then
if [ -z "$agent_patch_version" ]; then
agent_patch_version=$(find_latest_patch_version_for "${agent_major_version}.${agent_minor_version_without_patch}")
if [ -z "$agent_patch_version" ] || [ "$agent_patch_version" -lt 0 ]; then
echo -e "\033[33mWarning: Failed to obtain latest patch version for Agent ${agent_major_version}.${agent_minor_version_without_patch}. Defaulting to '0'.\033[0m"
agent_patch_version=0
fi
fi
# Check if the version is a classic release version or a pre-release version
if [ "$agent_minor_version" = "$clean_agent_minor_version" ];then
dmg_version="${agent_major_version}.${agent_minor_version_without_patch}.${agent_patch_version}-1"
else
dmg_version="${agent_major_version}.${agent_minor_version}-1"
fi
fi
if [ -z "$agent_dist_channel" ]; then
dmg_url="$dmg_base_url/datadog-agent-${dmg_version}.dmg"
else
dmg_url="$dmg_base_url/$agent_dist_channel/datadog-agent-${dmg_version}.dmg"
fi
if [ "$upgrade" ]; then
if [ ! -f $etc_dir/datadog.conf ]; then
printf "\033[31mDD_UPGRADE set but no config was found at $etc_dir/datadog.conf.\033[0m\n"
exit 1;
fi
fi
if [ ! "$apikey" ]; then
# if it's an upgrade, then we will use the transition script
if [ ! "$upgrade" ]; then
printf "\033[31mAPI key not available in DD_API_KEY environment variable.\033[0m\n"
exit 1;
fi
fi
if [ "$systemdaemon_install" == false ] && [ -f "$systemwide_servicefile_name" ]; then
printf "\033[31m
$systemwide_servicefile_name exists, suggesting a
systemwide Agent installation is present. Individual users
can't install the Agent when systemwide installation exists.
If no systemwide installation is present or you want to remove it, run:
sudo launchctl unload -wF $systemwide_servicefile_name
sudo rm $systemwide_servicefile_name
Then rerun this script to install the Agent for your user account.
\033[0m\n"
exit 1;
fi
# SUDO_USER is defined in man sudo: https://linux.die.net/man/8/sudo
# "SUDO_USER Set to the login name of the user who invoked sudo."
# USER is defined in man login: https://ss64.com/osx/login.html
# "Login enters information into the environment (see environ(7))
# specifying the user's home directory (HOME), command interpreter (SHELL),
# search path (PATH), terminal type (TERM) and user name (both LOGNAME and USER)."
# We want to get the real user who executed the command. Two situations can happen:
# - the command was run as the current user: then $USER contains the user which launched the command, and $SUDO_USER is empty,
# - the command was run with sudo: then $USER contains the name of the user targeted by the sudo command (by default, root)
# and $SUDO_USER contains the user which launched the sudo command.
# The following block covers both cases so that we have tbe username we want in the real_user variable.
real_user=`if [ "$SUDO_USER" ]; then
echo "$SUDO_USER"
else
echo "$USER"
fi`
# If this is a systemwide install done over SSH or a similar method, the real_user is now root
# which will eventually make the installation fail in the postinstall script. In this case, we
# set real_user to the target user of the systemwide installation.
if [ "$systemdaemon_install" = true ] && [ "$real_user" = root ]; then
real_user="$(echo "$systemdaemon_user_group" | awk -F: '{ print $1 }')"
# The install will copy plist file to real_user home dir => we add `-H`
# as a sudo argument to properly get its home for access to the plist file.
cmd_real_user="sudo -EHu $real_user"
else
cmd_real_user="sudo -Eu $real_user"
fi
TMPDIR=`sudo -u "$real_user" getconf DARWIN_USER_TEMP_DIR`
export TMPDIR
# shellcheck disable=SC2016
install_user_home=$($cmd_real_user bash -c 'echo "$HOME"')
# shellcheck disable=SC2016
user_uid=$($cmd_real_user bash -c 'id -u')
user_plist_file=${install_user_home}/Library/LaunchAgents/${service_name}.plist
# In order to install with the right user
rm -f /tmp/datadog-install-user
echo "$real_user" > /tmp/datadog-install-user
function on_error() {
printf "\033[31m$ERROR_MESSAGE
It looks like you hit an issue when trying to install the Agent.
Troubleshooting and basic usage information for the Agent are available at:
https://docs.datadoghq.com/agent/basic_agent_usage/
If you're still having problems, please send an email to support@datadoghq.com
with the contents of ddagent-install.log and we'll do our very best to help you
solve your problem.\n\033[0m\n"
}
trap on_error ERR
cmd_agent="$cmd_real_user /opt/datadog-agent/bin/agent/agent"
cmd_launchctl="$cmd_real_user launchctl"
function sed_inplace_arg() {
# Check for vanilla OS X sed or GNU sed
if [ "$(sed --version 2>/dev/null | grep -c "GNU")" -ne 0 ]; then
echo "-i"
fi
echo "-i ''"
}
function new_config() {
i_cmd="$(sed_inplace_arg)"
$sudo_cmd sh -c "sed $i_cmd 's/api_key:.*/api_key: $apikey/' \"$etc_dir/datadog.yaml\""
if [ "$site" ]; then
$sudo_cmd sh -c "sed $i_cmd 's/# site:.*/site: $site/' \"$etc_dir/datadog.yaml\""
fi
$sudo_cmd chown "$real_user":admin "$etc_dir/datadog.yaml"
$sudo_cmd chmod 640 $etc_dir/datadog.yaml
}
function import_config() {
printf "\033[34m\n* Converting old datadog.conf file to new datadog.yaml format\n\033[0m\n"
$cmd_agent import $etc_dir $etc_dir -f
}
function plist_modify_user_group() {
plist_file="$1"
user_value="$2"
group_value="$3"
user_parameter="UserName"
group_parameter="GroupName"
# if, in a future agent version we add UserName/GroupName to the plist file,
# we want this older version of install script fail, because it wouldn't know what to do
terms="UserName GroupName"
for term in $terms; do
if grep "$term" "$1"; then
printf "\033[31m$plist_file already contains $term, please update this script to the latest version\033[0m\n"
return 1
fi
done
## to insert user/group into the xml file, we'll find the last "" occurrence and insert before it
i_cmd="$(sed_inplace_arg)"
closing_dict_line=$($sudo_cmd cat "$plist_file" | grep -n "" | tail -1 | cut -f1 -d:)
$sudo_cmd sh -c "sed $i_cmd -e \"${closing_dict_line},${closing_dict_line}s||$user_parameter$user_value\n|\" -e \"${closing_dict_line},${closing_dict_line}s||$group_parameter$group_value\n|\" \"$plist_file\""
}
# # Install the agent
printf "\033[34m\n* Downloading datadog-agent\n\033[0m"
prepare_dmg_file $dmg_file
if ! $sudo_cmd curl --fail --progress-bar "$dmg_url" --output $dmg_file; then
printf "\033[31mCouldn't download the installer for macOS Agent version ${dmg_version}.\033[0m\n"
exit 1;
fi
printf "\033[34m\n* Installing datadog-agent, you might be asked for your sudo password...\n\033[0m"
$sudo_cmd hdiutil detach "/Volumes/datadog_agent" >/dev/null 2>&1 || true
printf "\033[34m\n - Mounting the DMG installer...\n\033[0m"
$sudo_cmd hdiutil attach "$dmg_file" -mountpoint "/Volumes/datadog_agent" >/dev/null
if [ "$systemdaemon_install" != false ] && [ -f "$systemwide_servicefile_name" ]; then
printf "\033[34m\n - Stopping systemwide Datadog Agent daemon ...\n\033[0m"
# we use "|| true" because if the service is not started/loaded, the commands fail
$sudo_cmd launchctl stop $service_name || true
if $sudo_cmd launchctl print system/$service_name 2>/dev/null >/dev/null; then
$sudo_cmd launchctl unload -wF $systemwide_servicefile_name || true
fi
fi
printf "\033[34m\n - Unpacking and copying files (this usually takes about a minute) ...\n\033[0m"
cd / && $sudo_cmd /usr/sbin/installer -pkg "`find "/Volumes/datadog_agent" -name \*.pkg 2>/dev/null`" -target / >/dev/null
printf "\033[34m\n - Unmounting the DMG installer ...\n\033[0m"
$sudo_cmd hdiutil detach "/Volumes/datadog_agent" >/dev/null
# Creating or overriding the install information
install_info_content="---
install_method:
tool: install_script_mac
tool_version: install_script_mac
installer_version: install_script_mac-$install_script_version
"
$sudo_cmd sh -c "echo '$install_info_content' > $etc_dir/install_info"
$sudo_cmd chown "$real_user":admin "$etc_dir/install_info"
$sudo_cmd chmod 640 $etc_dir/install_info
# Set the configuration
if grep -E 'api_key:( APIKEY)?$' "$etc_dir/datadog.yaml" > /dev/null 2>&1; then
if [ "$upgrade" ]; then
import_config
else
new_config
fi
printf "\n\033[34m* Restarting the Agent...\n\033[0m\n"
# systemwide installation is stopped at this point and will be started later on
if [ "$systemdaemon_install" != true ]; then
$cmd_launchctl stop $service_name
# Wait for the agent to fully stop
retry=0
until [ "$retry" -ge 5 ]; do
curl -m 5 -o /dev/null -s -I http://127.0.0.1:5002 || break
retry=$[$retry+1]
sleep 5
done
if [ "$retry" -ge 5 ]; then
printf "\n\033[33mCould not restart the agent.
You may have to restart it manually using the systray app or the
\"launchctl start $service_name\" command.\n\033[0m\n"
fi
$cmd_launchctl start $service_name
fi
else
printf "\033[34m\n* A datadog.yaml configuration file already exists. It will not be overwritten.\n\033[0m\n"
fi
# Starting the app
if [ "$systemdaemon_install" = false ]; then
$cmd_real_user open -a 'Datadog Agent.app'
else
printf "\033[34m\n* Installing $service_name as a systemwide LaunchDaemon ...\n\n\033[0m"
# Remove the Agent login item and unload the agent for current user
# if it is running - it's not running if the script was launched when
# the GUI was not running for the user (e.g. a run of this script via
# ssh for user not logged in via GUI).
# This condition is true only when installing an agent < 7.52.0
if $cmd_launchctl print "gui/$user_uid/$service_name" 1>/dev/null 2>/dev/null; then
$cmd_real_user osascript -e 'tell application "System Events" to if login item "Datadog Agent" exists then delete login item "Datadog Agent"'
$cmd_launchctl stop "$service_name"
$cmd_launchctl unload "$user_plist_file"
fi
# move the plist file to the system location
$sudo_cmd mv "$user_plist_file" /Library/LaunchDaemons/
# make sure the daemon launches under proper user/group and that it has access
# to all files/dirs it needs; then start it
plist_modify_user_group "$systemwide_servicefile_name" "$systemdaemon_user" "$systemdaemon_group"
$sudo_cmd chown "0:0" "$systemwide_servicefile_name"
$sudo_cmd chown -R "$systemdaemon_user_group" "$etc_dir" "$log_dir" "$run_dir"
$sudo_cmd launchctl load -w "$systemwide_servicefile_name"
$sudo_cmd launchctl kickstart "system/$service_name"
fi
# Agent works, echo some instructions and exit
printf "\033[32m
Your Agent is running properly. It will continue to run in the
background and submit metrics to Datadog.
You can check the agent status using the \"datadog-agent status\" command
or by opening the webui using the \"datadog-agent launch-gui\" command.
\033[0m"
if [ "$systemdaemon_install" = false ]; then
printf "\033[32m
If you ever want to stop the Agent, please use the Datadog Agent App or
the launchctl command. It will start automatically at login.
\033[0m"
else
printf "\033[32m
If you ever want to stop the Agent, please use the the launchctl command.
The Agent will start automatically at system startup.
\033[0m"
fi