-
Notifications
You must be signed in to change notification settings - Fork 1.5k
Expand file tree
/
Copy pathpostinst
More file actions
executable file
·370 lines (330 loc) · 17.3 KB
/
Copy pathpostinst
File metadata and controls
executable file
·370 lines (330 loc) · 17.3 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
#!/bin/sh
#
# Perform necessary datadog-agent setup steps after package is installed.
#
INSTALL_DIR=/opt/datadog-agent
# macOS-specific variables
OPT_APP_DIR="$INSTALL_DIR/Datadog Agent.app"
APP_DIR="/Applications/Datadog Agent.app"
CONF_DIR=$INSTALL_DIR/etc
RUN_DIR=$INSTALL_DIR/run
AI_USAGE_DESKTOP_MONITOR_LABEL="com.datadoghq.ai-usage-agent.desktop-monitor"
OLD_AI_USAGE_DESKTOP_MONITOR_LABEL="com.datadoghq.ai-prompt-logger.desktop-monitor"
AI_USAGE_DESKTOP_MONITOR_PLIST="/Library/LaunchAgents/${AI_USAGE_DESKTOP_MONITOR_LABEL}.plist"
AI_USAGE_DESKTOP_MONITOR_PLIST_EXAMPLE="$CONF_DIR/${AI_USAGE_DESKTOP_MONITOR_LABEL}.plist.example"
# On Mac, the real log folder is located under /opt/datadog-agent/logs, because MacOS upgrades delete /var/log
LOG_DIR=/opt/datadog-agent/logs
# Root-only staging directory shared with the install script.
# Contains env (install-time vars) and saved-conf/ (backup from preinst).
INSTALL_STAGING_DIR="/private/var/root/datadog-install"
INSTALL_ENV_FILE="$INSTALL_STAGING_DIR/env"
SAVED_CONF_DIR="$INSTALL_STAGING_DIR/saved-conf"
AI_USAGE_CHROME_EXTENSION_ID_DEFAULT="gkmbhgbippkmmmidcikijiblbagbjgjj"
# Let's log the standard outputs of this script
LOG_FILE="$LOG_DIR/postinstall.log"
mkdir -vp $LOG_DIR
chmod 750 $LOG_DIR
exec > $LOG_FILE 2>&1
echo "# State at the beginning"
echo "## Agent version"
$INSTALL_DIR/bin/agent/agent version || true
echo "## $INSTALL_DIR"
ls -al $INSTALL_DIR || true
echo "## $APP_DIR/Contents/Resources"
ls -al "$APP_DIR/Contents/Resources" || true
# Stop any running GUI instances before installation/upgrade
# This prevents "Load failed" errors when KeepAlive=true
echo "# Stopping GUI app if running"
for logged_user in $(who | awk '{print $1}' | sort -u); do
logged_uid=$(id -u "$logged_user" 2>/dev/null) || continue
launchctl bootout "gui/$logged_uid/com.datadoghq.gui" 2>/dev/null || true
launchctl bootout "gui/$logged_uid/$AI_USAGE_DESKTOP_MONITOR_LABEL" 2>/dev/null || true
launchctl bootout "gui/$logged_uid/$OLD_AI_USAGE_DESKTOP_MONITOR_LABEL" 2>/dev/null || true
done
# Wait for GUI processes to actually terminate (with 10 second timeout to match ExitTimeOut)
max_wait=10 # 10 * 1s = 10 seconds
count=0
while [ $count -lt $max_wait ]; do
if ! pgrep -f "Datadog Agent.app/Contents/MacOS/gui" > /dev/null 2>&1; then
echo "GUI app processes terminated"
break
fi
sleep 1
count=$((count + 1))
done
if pgrep -f "Datadog Agent.app/Contents/MacOS/gui" > /dev/null 2>&1; then
echo "Warning: GUI processes still running after 10s, proceeding anyway"
fi
echo "# Preparing log dir"
DDAGENT_USER="_dd-agent"
chown -vR "$DDAGENT_USER:admin" "$LOG_DIR"
chmod -v 770 "$LOG_DIR" # admin group can read and write logs
echo "# Installing the app"
mv -v "$OPT_APP_DIR" /Applications || echo "App already installed"
# Set the run directory for the agent
mkdir -vp "$RUN_DIR"
chown -vR "$DDAGENT_USER:admin" "$RUN_DIR"
chmod -v 775 "$RUN_DIR" # admin group can read and write runtime files
# Create separate world-writable directory ONLY for multi-user GUI sockets
mkdir -vp "$RUN_DIR/ipc"
# Remove stale GUI socket files before chown to prevent "Address already in use" on reinstall
find "$RUN_DIR/ipc" -maxdepth 1 -name 'gui-*.sock' -exec rm -fv {} \;
chown -vR root:wheel "$RUN_DIR/ipc"
chmod -v 1777 "$RUN_DIR/ipc" # Sticky bit: users can create GUI sockets but not delete others' files
# GUI sockets are created by per-user LaunchAgents (owned by the logged-in user).
# The agent runs as _dd-agent and needs to connect to them. Inheritable ACL
# ensures sockets created by bind() in this directory grant _dd-agent access.
chmod +a "user:_dd-agent allow read,write,readattr,readextattr,readsecurity,list,search,file_inherit,directory_inherit" "$RUN_DIR/ipc"
# Restore config from pre-install (upgrade) or create default (fresh install)
echo "# Setting up configuration"
mkdir -vp $CONF_DIR/checks.d
if [ -d "$SAVED_CONF_DIR" ]; then
cp -vf "$SAVED_CONF_DIR/datadog.yaml" $CONF_DIR/ 2>/dev/null || true
cp -vf "$SAVED_CONF_DIR/system-probe.yaml" $CONF_DIR/ 2>/dev/null || true
cp -vfR "$SAVED_CONF_DIR/conf.d/"* $CONF_DIR/conf.d 2>/dev/null || true
cp -vn "$SAVED_CONF_DIR/checks.d/"* $CONF_DIR/checks.d 2>/dev/null || true
fi
if [ ! -e "$CONF_DIR/datadog.yaml" ]; then
sed -E 's/^api_key:$/api_key: APIKEY/' $CONF_DIR/datadog.yaml.example > $CONF_DIR/datadog.yaml
fi
# Always regenerate from the packaged template (even on upgrade) so default changes
# (e.g. EVP track, desktop monitoring defaults) reach already-installed machines.
if [ -f "$CONF_DIR/ai_usage_native_host.yaml.example" ]; then
cp "$CONF_DIR/ai_usage_native_host.yaml.example" "$CONF_DIR/ai_usage_native_host.yaml"
apm_receiver_port=$(awk '
/^[[:space:]]*#/ { next }
/^[^[:space:]][^:]*:/ { in_apm = 0 }
/^[[:space:]]*apm_config:[[:space:]]*($|#)/ { in_apm = 1; next }
in_apm && /^[[:space:]]*receiver_port:/ {
sub(/^[^:]*:[[:space:]]*/, "")
sub(/[[:space:]]*#.*/, "")
gsub(/["'\''[:space:]]/, "")
if ($0 ~ /^[0-9]+$/) {
print
exit
}
}
' "$CONF_DIR/datadog.yaml" 2>/dev/null || true)
# Only activate the line when the port differs from the compiled-in default (8126); when it
# matches, leave the line as shipped (commented) so a future default change applies without
# needing another overwrite of this file.
if [ -n "$apm_receiver_port" ] && [ "$apm_receiver_port" != "8126" ]; then
sed -i '' "s#^[ #]*trace_agent_url:.*#trace_agent_url: \"http://127.0.0.1:$apm_receiver_port\"#" "$CONF_DIR/ai_usage_native_host.yaml"
fi
fi
# Read configuration written by the install script (if present).
# When the pkg is installed via GUI Installer or MDM (no install script),
# this file won't exist and the config is left as-is for the admin to configure.
install_method="macos_dmg"
install_method_version="macos_dmg"
if [ -f "$INSTALL_ENV_FILE" ]; then
dd_api_key=$(grep '^DD_API_KEY=' "$INSTALL_ENV_FILE" | cut -d= -f2-)
dd_site=$(grep '^DD_SITE=' "$INSTALL_ENV_FILE" | cut -d= -f2-)
dd_gui_menu=$(grep '^DD_GUI_APP_MENU_ENABLED=' "$INSTALL_ENV_FILE" | cut -d= -f2-)
dd_infra_mode=$(grep '^DD_INFRASTRUCTURE_MODE=' "$INSTALL_ENV_FILE" | cut -d= -f2-)
dd_install_method=$(grep '^DD_INSTALL_METHOD=' "$INSTALL_ENV_FILE" | cut -d= -f2-)
dd_install_version=$(grep '^DD_INSTALL_SCRIPT_VERSION=' "$INSTALL_ENV_FILE" | cut -d= -f2-)
dd_ai_usage_chrome_extension_id=$(grep '^DD_AI_USAGE_CHROME_EXTENSION_ID=' "$INSTALL_ENV_FILE" | cut -d= -f2-)
if [ -n "$dd_ai_usage_chrome_extension_id" ] && [ -f "$CONF_DIR/ai_usage_native_host.yaml" ]; then
if grep -qE '^[[:space:]]*#?[[:space:]]*chrome_extension_id:' "$CONF_DIR/ai_usage_native_host.yaml"; then
sed -i '' "s/^# *chrome_extension_id:.*/chrome_extension_id: $dd_ai_usage_chrome_extension_id/" "$CONF_DIR/ai_usage_native_host.yaml"
sed -i '' "s/^chrome_extension_id:.*/chrome_extension_id: $dd_ai_usage_chrome_extension_id/" "$CONF_DIR/ai_usage_native_host.yaml"
else
printf '\nchrome_extension_id: %s\n' "$dd_ai_usage_chrome_extension_id" >> "$CONF_DIR/ai_usage_native_host.yaml"
fi
fi
if [ -n "$dd_api_key" ]; then
sed -i '' "s/^api_key:.*/api_key: $dd_api_key/" "$CONF_DIR/datadog.yaml"
fi
if [ -n "$dd_site" ]; then
sed -i '' "s/^#* *site:.*/site: $dd_site/" "$CONF_DIR/datadog.yaml"
fi
if [ -n "$dd_infra_mode" ]; then
if grep -Eq '^#* *infrastructure_mode:' "$CONF_DIR/datadog.yaml"; then
sed -i '' "s/^#* *infrastructure_mode:.*/infrastructure_mode: $dd_infra_mode/" "$CONF_DIR/datadog.yaml"
else
# The restored datadog.yaml predates the infrastructure_mode entry
# (older Agent versions had no example line to replace).
printf '\ninfrastructure_mode: %s\n' "$dd_infra_mode" >> "$CONF_DIR/datadog.yaml"
fi
fi
if [ -n "$dd_install_method" ]; then
install_method="$dd_install_method"
install_method_version="${dd_install_method}-${dd_install_version}"
fi
fi
install_info_content="---
install_method:
tool: $install_method
tool_version: $install_method
installer_version: $install_method_version
"
echo "$install_info_content" > $CONF_DIR/install_info
echo "# Setting correct rights on conf"
chown -vR "$DDAGENT_USER:admin" $CONF_DIR
chmod -v 770 $CONF_DIR
find $CONF_DIR -type f -exec chmod 660 {} \;
# Ensure _dd-agent can read/write config even after a user edits a file with
# an editor that replaces the file (TextEdit, VS Code, vim, etc.), changing
# Unix ownership. macOS editors use atomic save (write temp + rename), which
# replaces the inode and changes the owner to the editing user.
#
# Two layers of protection:
# 1. Inheritable ACLs on directories — covers files created directly in the
# directory (open(O_CREAT)), e.g. new check configs, agent-generated files.
# 2. Explicit ACLs on existing files — macOS NSDocument (used by TextEdit,
# VS Code, etc.) preserves ACLs from the original file during atomic save,
# so the replacement file retains _dd-agent access.
find "$CONF_DIR" -type d -exec chmod +a "user:_dd-agent allow read,write,readattr,writeattr,readextattr,writeextattr,readsecurity,list,search,add_file,add_subdirectory,file_inherit,directory_inherit" {} \;
find "$CONF_DIR" -type f -exec chmod +a "user:_dd-agent allow read,write,readattr,writeattr,readextattr,writeextattr,readsecurity" {} \;
if [ -f "$CONF_DIR/ai_usage_native_host.yaml" ]; then
# The AI usage native host runs as the logged-in user for Chrome native
# messaging and desktop monitoring. Grant only path traversal plus read
# access to this single non-secret config file, not the rest of etc/.
chmod +a "everyone allow search,readattr,readextattr,readsecurity" "$CONF_DIR"
chmod +a "everyone allow read,readattr,readextattr,readsecurity" "$CONF_DIR/ai_usage_native_host.yaml"
fi
# Chrome NativeMessagingHosts for AI usage native host (per-user)
CHROME_EXT_ID=""
if [ -f "$INSTALL_ENV_FILE" ]; then
CHROME_EXT_ID=$(grep '^DD_AI_USAGE_CHROME_EXTENSION_ID=' "$INSTALL_ENV_FILE" | cut -d= -f2- || true)
fi
if [ -z "$CHROME_EXT_ID" ] && [ -f "$CONF_DIR/ai_usage_native_host.yaml" ]; then
CHROME_EXT_ID=$(awk '/^chrome_extension_id:/{sub(/^[^:]*:[[:space:]]*/, ""); gsub(/["'\'']/, ""); print; exit}' "$CONF_DIR/ai_usage_native_host.yaml")
fi
CHROME_EXT_ID=$(echo "$CHROME_EXT_ID" | tr -d '\r' | sed 's/^[[:space:]]*//;s/[[:space:]]*$//')
if [ -z "$CHROME_EXT_ID" ]; then
CHROME_EXT_ID="$AI_USAGE_CHROME_EXTENSION_ID_DEFAULT"
fi
# Launcher passes --config like `agent run -c` / `system-probe --config` (Chrome does not pass argv).
NATIVE_HOST_BIN="$INSTALL_DIR/embedded/bin/run_ai_usage_native_host.sh"
NM_HOST_NAME="com.datadoghq.ai_usage_agent.native_host"
OLD_NM_HOST_NAME="com.datadoghq.ai_prompt_logger.native_host"
if [ -n "$CHROME_EXT_ID" ] && [ -f "$NATIVE_HOST_BIN" ]; then
echo "# Installing Chrome NativeMessagingHosts for AI usage native host"
for user_home in /Users/*; do
[ -d "$user_home" ] || continue
u=$(basename "$user_home")
[ "$u" = "Shared" ] && continue
id -u "$u" >/dev/null 2>&1 || continue
nmh_dir="$user_home/Library/Application Support/Google/Chrome/NativeMessagingHosts"
sudo -u "$u" mkdir -p "$nmh_dir"
manifest="$nmh_dir/${NM_HOST_NAME}.json"
old_manifest="$nmh_dir/${OLD_NM_HOST_NAME}.json"
sudo -u "$u" rm -f "$old_manifest"
sudo -u "$u" tee "$manifest" >/dev/null <<EOF
{
"name": "${NM_HOST_NAME}",
"description": "Datadog AI usage native messaging host",
"path": "${NATIVE_HOST_BIN}",
"type": "stdio",
"allowed_origins": [
"chrome-extension://${CHROME_EXT_ID}/"
]
}
EOF
sudo -u "$u" chmod 644 "$manifest"
done
elif [ -n "$CHROME_EXT_ID" ]; then
echo "WARNING: AI usage native host launcher missing at $NATIVE_HOST_BIN; skipping Chrome manifest install"
fi
# `datadog-agent` command line
mkdir -vp /usr/local/bin
ln -vs $INSTALL_DIR/bin/agent/agent /usr/local/bin/datadog-agent
if [ ! -e "$CONF_DIR/datadog.yaml" ]; then
exit 1
fi
# Install agent as system-wide LaunchDaemon
echo "# Installing agent as system-wide LaunchDaemon"
cp -vf "$CONF_DIR/com.datadoghq.agent.plist.example" /Library/LaunchDaemons/com.datadoghq.agent.plist
chown root:wheel /Library/LaunchDaemons/com.datadoghq.agent.plist
chmod 644 /Library/LaunchDaemons/com.datadoghq.agent.plist
launchctl enable system/com.datadoghq.agent 2>/dev/null || true
if ! launchctl bootstrap system /Library/LaunchDaemons/com.datadoghq.agent.plist 2>&1; then
echo "WARNING: Failed to bootstrap agent service."
fi
# Install system-probe as system-wide LaunchDaemon
echo "# Installing system-probe as system-wide LaunchDaemon"
cp -vf "$CONF_DIR/com.datadoghq.sysprobe.plist.example" /Library/LaunchDaemons/com.datadoghq.sysprobe.plist
chown root:wheel /Library/LaunchDaemons/com.datadoghq.sysprobe.plist
chmod 644 /Library/LaunchDaemons/com.datadoghq.sysprobe.plist
launchctl enable system/com.datadoghq.sysprobe 2>/dev/null || true
if ! launchctl bootstrap system /Library/LaunchDaemons/com.datadoghq.sysprobe.plist 2>&1; then
echo "WARNING: Failed to bootstrap system-probe service."
fi
# Install Agent Data Plane as system-wide LaunchDaemon
echo "# Installing Agent Data Plane as system-wide LaunchDaemon"
cp -vf "$CONF_DIR/com.datadoghq.data-plane.plist.example" /Library/LaunchDaemons/com.datadoghq.data-plane.plist
chown root:wheel /Library/LaunchDaemons/com.datadoghq.data-plane.plist
chmod 644 /Library/LaunchDaemons/com.datadoghq.data-plane.plist
launchctl enable system/com.datadoghq.data-plane 2>/dev/null || true
if ! launchctl bootstrap system /Library/LaunchDaemons/com.datadoghq.data-plane.plist 2>&1; then
echo "WARNING: Failed to bootstrap Agent Data Plane service."
fi
# Install GUI LaunchAgent for all users (system-wide)
echo "# Configuring GUI app for system-wide installation"
cp -vf "$CONF_DIR/com.datadoghq.gui.plist.example" /Library/LaunchAgents/com.datadoghq.gui.plist
# The GUI plist template includes --headless by default (no menu bar icon).
# DD_GUI_APP_MENU_ENABLED=true removes it to show the menu bar icon.
if [ "$dd_gui_menu" = "true" ]; then
echo "# Enabling GUI menu bar icon (removing --headless)"
sed -i '' '/<string>--headless<\/string>/d' /Library/LaunchAgents/com.datadoghq.gui.plist
fi
chown root:wheel /Library/LaunchAgents/com.datadoghq.gui.plist
chmod 644 /Library/LaunchAgents/com.datadoghq.gui.plist
# Load GUI immediately for the console user if they have an active GUI session
CONSOLE_USER=$(stat -f '%Su' /dev/console 2>/dev/null || echo 'root')
CONSOLE_USER_UID=$(id -u "$CONSOLE_USER" 2>/dev/null)
if [ -n "$CONSOLE_USER_UID" ] && sudo -u "$CONSOLE_USER" launchctl managername 2>/dev/null | grep -q "Aqua"; then
echo "# Loading GUI app for $CONSOLE_USER"
launchctl enable "gui/$CONSOLE_USER_UID/com.datadoghq.gui" 2>/dev/null || true
if ! launchctl bootstrap "gui/$CONSOLE_USER_UID" /Library/LaunchAgents/com.datadoghq.gui.plist 2>&1; then
echo "WARNING: Failed to bootstrap GUI app."
fi
else
echo "# No GUI session detected; GUI app will launch at next user login"
fi
# Remove previous LaunchAgent labels if this package is upgrading an in-branch install.
rm -f "/Library/LaunchAgents/${OLD_AI_USAGE_DESKTOP_MONITOR_LABEL}.plist"
rm -f "$CONF_DIR/${OLD_AI_USAGE_DESKTOP_MONITOR_LABEL}.plist.example"
# Install AI Usage Agent desktop monitor LaunchAgent for all users (system-wide)
if [ -f "$AI_USAGE_DESKTOP_MONITOR_PLIST_EXAMPLE" ]; then
echo "# Configuring AI Usage Agent desktop monitor for system-wide installation"
cp -vf "$AI_USAGE_DESKTOP_MONITOR_PLIST_EXAMPLE" "$AI_USAGE_DESKTOP_MONITOR_PLIST"
chown root:wheel "$AI_USAGE_DESKTOP_MONITOR_PLIST"
chmod 644 "$AI_USAGE_DESKTOP_MONITOR_PLIST"
if [ -n "$CONSOLE_USER_UID" ] && sudo -u "$CONSOLE_USER" launchctl managername 2>/dev/null | grep -q "Aqua"; then
echo "# Loading AI Usage Agent desktop monitor for $CONSOLE_USER"
launchctl enable "gui/$CONSOLE_USER_UID/$AI_USAGE_DESKTOP_MONITOR_LABEL" 2>/dev/null || true
if ! launchctl bootstrap "gui/$CONSOLE_USER_UID" "$AI_USAGE_DESKTOP_MONITOR_PLIST" 2>&1; then
echo "WARNING: Failed to bootstrap AI Usage Agent desktop monitor."
fi
else
echo "# No GUI session detected; AI Usage Agent desktop monitor will launch at next user login"
fi
else
echo "WARNING: AI Usage Agent desktop monitor LaunchAgent template missing at $AI_USAGE_DESKTOP_MONITOR_PLIST_EXAMPLE"
fi
echo ""
echo "=========================================="
echo "Datadog Agent GUI (System-Wide)"
echo "=========================================="
echo "The GUI app will launch automatically for each user when they log in."
echo ""
echo "For WiFi data collection (SSID/BSSID), users must grant Location permission:"
echo " System Settings -> Privacy & Security -> Location Services -> Enable 'Datadog Agent'"
echo ""
echo "Note: The GUI will prompt for this permission when needed."
echo "=========================================="
echo ""
# Clean up the install staging directory (env file with API key, saved config)
rm -rf "$INSTALL_STAGING_DIR"
# A little debriefing won't hurt
echo "# State at the end"
echo "## Agent version"
$INSTALL_DIR/bin/agent/agent version || true
echo "## $INSTALL_DIR"
ls -al $INSTALL_DIR || true
echo "## $APP_DIR/Contents/Resources"
ls -al "$APP_DIR/Contents/Resources" || true
exit 0