blob: 29a12297f12141fd3947a043b2e3e124f6cf61b1 [file] [log] [blame]
Steve Fung0e8746d2015-08-20 17:07:50 -07001#!/system/bin/sh
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +00002
Steve Fung6c34c252015-08-20 00:27:30 -07003# Copyright (C) 2010 The Android Open Source Project
4#
5# Licensed under the Apache License, Version 2.0 (the "License");
6# you may not use this file except in compliance with the License.
7# You may obtain a copy of the License at
8#
9# http://www.apache.org/licenses/LICENSE-2.0
10#
11# Unless required by applicable law or agreed to in writing, software
12# distributed under the License is distributed on an "AS IS" BASIS,
13# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14# See the License for the specific language governing permissions and
15# limitations under the License.
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000016
17set -e
18
19# Default product ID in crash report (used if GOOGLE_CRASH_* is undefined).
Steve Fung0e8746d2015-08-20 17:07:50 -070020BRILLO_PRODUCT=Brillo
21
22# Base directory that contains any crash reporter state files.
23CRASH_STATE_DIR="/data/misc/crash_reporter"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000024
Steve Fung12e61ca2015-09-15 16:36:33 -070025# File containing crash_reporter's anonymized guid.
26GUID_FILE="${CRASH_STATE_DIR}/guid"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000027
28# Crash sender lock in case the sender is already running.
Steve Fung0e8746d2015-08-20 17:07:50 -070029CRASH_SENDER_LOCK="${CRASH_STATE_DIR}/lock/crash_sender"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000030
31# Path to file that indicates a crash test is currently running.
Steve Fung0e8746d2015-08-20 17:07:50 -070032CRASH_TEST_IN_PROGRESS_FILE="${CRASH_STATE_DIR}/tmp/crash-test-in-progress"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000033
34# Set this to 1 in the environment to allow uploading crash reports
35# for unofficial versions.
36FORCE_OFFICIAL=${FORCE_OFFICIAL:-0}
37
38# Path to hardware class description.
39HWCLASS_PATH="/sys/devices/platform/chromeos_acpi/HWID"
40
41# Path to file that indicates this is a developer image.
Steve Fung0e8746d2015-08-20 17:07:50 -070042LEAVE_CORE_FILE="${CRASH_STATE_DIR}/.leave_core"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000043
44# Path to list_proxies.
Steve Fung0e8746d2015-08-20 17:07:50 -070045LIST_PROXIES="list_proxies"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000046
47# Maximum crashes to send per day.
48MAX_CRASH_RATE=${MAX_CRASH_RATE:-32}
49
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000050# File whose existence mocks crash sending. If empty we pretend the
51# crash sending was successful, otherwise unsuccessful.
Steve Fung0e8746d2015-08-20 17:07:50 -070052MOCK_CRASH_SENDING="${CRASH_STATE_DIR}/tmp/mock-crash-sending"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000053
54# Set this to 1 in the environment to pretend to have booted in developer
55# mode. This is used by autotests.
56MOCK_DEVELOPER_MODE=${MOCK_DEVELOPER_MODE:-0}
57
58# Ignore PAUSE_CRASH_SENDING file if set.
59OVERRIDE_PAUSE_SENDING=${OVERRIDE_PAUSE_SENDING:-0}
60
61# File whose existence causes crash sending to be delayed (for testing).
62# Must be stateful to enable testing kernel crashes.
Steve Fung0e8746d2015-08-20 17:07:50 -070063PAUSE_CRASH_SENDING="${CRASH_STATE_DIR}/lock/crash_sender_paused"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000064
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000065# Path to a directory of restricted certificates which includes
Steve Fung44aec5f2015-09-12 02:52:06 -070066# a certificate for the crash server.
Steve Fung0e8746d2015-08-20 17:07:50 -070067RESTRICTED_CERTIFICATES_PATH="/system/etc/security/cacerts"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000068
69# File whose existence implies we're running and not to start again.
Steve Fung0e8746d2015-08-20 17:07:50 -070070RUN_FILE="${CRASH_STATE_DIR}/run/crash_sender.pid"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000071
72# Maximum time to sleep between sends.
73SECONDS_SEND_SPREAD=${SECONDS_SEND_SPREAD:-600}
74
Peter Qiu6aa551e2015-03-06 11:42:04 -080075# Set this to 1 to allow uploading of device coredumps.
Steve Fung0e8746d2015-08-20 17:07:50 -070076DEVCOREDUMP_UPLOAD_FLAG_FILE="${CRASH_STATE_DIR}/device_coredump_upload_allowed"
Peter Qiu6aa551e2015-03-06 11:42:04 -080077
Steve Funged789302015-09-14 16:14:23 -070078# The weave configuration file.
79WEAVE_CONF_FILE="/etc/weaved/weaved.conf"
80
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000081# The syslog tag for all logging we emit.
82TAG="$(basename $0)[$$]"
83
84# Directory to store timestamp files indicating the uploads in the past 24
85# hours.
Steve Fung0e8746d2015-08-20 17:07:50 -070086TIMESTAMPS_DIR="${CRASH_STATE_DIR}/crash_sender"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000087
88# Temp directory for this process.
89TMP_DIR=""
90
Steve Fung0e8746d2015-08-20 17:07:50 -070091# Crash report log file.
92CRASH_LOG="${CRASH_STATE_DIR}/log/uploads.log"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000093
94lecho() {
Steve Fung0e8746d2015-08-20 17:07:50 -070095 log -t "${TAG}" "$@"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +000096}
97
98# Returns true if mock is enabled.
99is_mock() {
100 [ -f "${MOCK_CRASH_SENDING}" ] && return 0
101 return 1
102}
103
104is_mock_successful() {
105 local mock_in=$(cat "${MOCK_CRASH_SENDING}")
106 [ "${mock_in}" = "" ] && return 0 # empty file means success
107 return 1
108}
109
110cleanup() {
111 if [ -n "${TMP_DIR}" ]; then
112 rm -rf "${TMP_DIR}"
113 fi
114 rm -f "${RUN_FILE}"
Steve Fung0e8746d2015-08-20 17:07:50 -0700115 if [ -n "${CRASH_SENDER_LOCK}" ]; then
116 rm -rf "${CRASH_SENDER_LOCK}"
117 fi
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000118 crash_done
119}
120
121crash_done() {
122 if is_mock; then
123 # For testing purposes, emit a message to log so that we
124 # know when the test has received all the messages from this run.
125 lecho "crash_sender done."
126 fi
127}
128
129is_official_image() {
130 [ ${FORCE_OFFICIAL} -ne 0 ] && return 0
Steve Fung0e8746d2015-08-20 17:07:50 -0700131 getprop ro.product.description | grep -q Official
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000132}
133
134# Returns 0 if the a crash test is currently running. NOTE: Mirrors
135# crash_collector.cc:CrashCollector::IsCrashTestInProgress().
136is_crash_test_in_progress() {
137 [ -f "${CRASH_TEST_IN_PROGRESS_FILE}" ] && return 0
138 return 1
139}
140
141# Returns 0 if we should consider ourselves to be running on a developer
142# image. NOTE: Mirrors crash_collector.cc:CrashCollector::IsDeveloperImage().
143is_developer_image() {
144 # If we're testing crash reporter itself, we don't want to special-case
145 # for developer images.
146 is_crash_test_in_progress && return 1
147 [ -f "${LEAVE_CORE_FILE}" ] && return 0
148 return 1
149}
150
151# Returns 0 if we should consider ourselves to be running on a test image.
152is_test_image() {
153 # If we're testing crash reporter itself, we don't want to special-case
154 # for test images.
155 is_crash_test_in_progress && return 1
156 case $(get_channel) in
157 test*) return 0;;
158 esac
159 return 1
160}
161
162# Returns 0 if the machine booted up in developer mode.
163is_developer_mode() {
164 [ ${MOCK_DEVELOPER_MODE} -ne 0 ] && return 0
165 # If we're testing crash reporter itself, we don't want to special-case
166 # for developer mode.
167 is_crash_test_in_progress && return 1
Steve Fung0e8746d2015-08-20 17:07:50 -0700168 if [ "$(getprop ro.build.type)" = "eng" ]; then
169 return 0
170 else
171 return 1
172 fi
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000173}
174
Peter Qiu6aa551e2015-03-06 11:42:04 -0800175# Return 0 if the uploading of device coredumps is allowed.
176is_device_coredump_upload_allowed() {
177 [ -f "${DEVCOREDUMP_UPLOAD_FLAG_FILE}" ] && return 0
178 return 1
179}
180
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000181# Generate a uniform random number in 0..max-1.
Steve Fung18ca9b32015-09-12 03:21:59 -0700182# POSIX arithmetic expansion requires support of at least signed long integers.
183# On 32-bit systems, that may mean 32-bit signed integers, in which case the
184# 32-bit random number read from /dev/urandom may be interpreted as negative
185# when used inside an arithmetic expansion (since the high bit might be set).
186# mksh at least is known to behave this way.
187# For this case, simply take the absolute value, which will still give a
188# roughly uniform random distribution for the modulo (as we are merely ignoring
189# the high/sign bit).
190# See corresponding Arithmetic Expansion and Arithmetic Expression sections:
191# POSIX: http://pubs.opengroup.org/onlinepubs/009695399/utilities/xcu_chap02.html#tag_02_06_04
192# mksh: http://linux.die.net/man/1/mksh
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000193generate_uniform_random() {
194 local max=$1
195 local random="$(od -An -N4 -tu /dev/urandom)"
Steve Fung18ca9b32015-09-12 03:21:59 -0700196 echo $(((random < 0 ? -random : random) % max))
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000197}
198
199# Check if sending a crash now does not exceed the maximum 24hr rate and
200# commit to doing so, if not.
201check_rate() {
202 mkdir -p ${TIMESTAMPS_DIR}
203 # Only consider minidumps written in the past 24 hours by removing all older.
Steve Fung0e8746d2015-08-20 17:07:50 -0700204 find "${TIMESTAMPS_DIR}" -mindepth 1 -mtime +1 \
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000205 -exec rm -- '{}' ';'
206 local sends_in_24hrs=$(echo "${TIMESTAMPS_DIR}"/* | wc -w)
207 lecho "Current send rate: ${sends_in_24hrs}sends/24hrs"
208 if [ ${sends_in_24hrs} -ge ${MAX_CRASH_RATE} ]; then
209 lecho "Cannot send more crashes:"
210 lecho " current ${sends_in_24hrs}send/24hrs >= " \
211 "max ${MAX_CRASH_RATE}send/24hrs"
212 return 1
213 fi
Steve Fung0e8746d2015-08-20 17:07:50 -0700214 mktemp "${TIMESTAMPS_DIR}"/XXXXXX > /dev/null
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000215 return 0
216}
217
218# Gets the base part of a crash report file, such as name.01234.5678.9012 from
219# name.01234.5678.9012.meta or name.01234.5678.9012.log.tar.xz. We make sure
220# "name" is sanitized in CrashCollector::Sanitize to not include any periods.
221get_base() {
222 echo "$1" | cut -d. -f-4
223}
224
225get_extension() {
226 local extension="${1##*.}"
227 local filename="${1%.*}"
228 # For gzipped file, we ignore .gz and get the real extension
229 if [ "${extension}" = "gz" ]; then
230 echo "${filename##*.}"
231 else
232 echo "${extension}"
233 fi
234}
235
236# Return which kind of report the given metadata file relates to
237get_kind() {
238 local payload="$(get_key_value "$1" "payload")"
239 if [ ! -r "${payload}" ]; then
240 lecho "Missing payload: ${payload}"
241 echo "undefined"
242 return
243 fi
244 local kind="$(get_extension "${payload}")"
245 if [ "${kind}" = "dmp" ]; then
246 echo "minidump"
247 return
248 fi
249 echo "${kind}"
250}
251
252get_key_value() {
253 local file="$1" key="$2" value
254
255 if [ -f "${file}" ]; then
256 # Return the first entry. There shouldn't be more than one anyways.
257 # Substr at length($1) + 2 skips past the key and following = sign (awk
258 # uses 1-based indexes), but preserves embedded = characters.
259 value=$(sed -n "/^${key}[[:space:]]*=/{s:^[^=]*=::p;q}" "${file}")
260 fi
261
262 echo "${value:-undefined}"
263}
264
265get_keys() {
266 local file="$1" regex="$2"
267
Steve Fung0e8746d2015-08-20 17:07:50 -0700268 cut -d '=' -f1 "${file}" | grep --color=never "${regex}"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000269}
270
271# Return the channel name (sans "-channel" suffix).
272get_channel() {
Steve Fung0e8746d2015-08-20 17:07:50 -0700273 getprop ro.product.channel | sed 's:-channel$::'
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000274}
275
276# Return the hardware class or "undefined".
277get_hardware_class() {
278 if [ -r "${HWCLASS_PATH}" ]; then
279 cat "${HWCLASS_PATH}"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000280 else
281 echo "undefined"
282 fi
283}
284
285send_crash() {
286 local meta_path="$1"
287 local report_payload="$(get_key_value "${meta_path}" "payload")"
288 local kind="$(get_kind "${meta_path}")"
289 local exec_name="$(get_key_value "${meta_path}" "exec_name")"
Steve Fung44aec5f2015-09-12 02:52:06 -0700290 local url="$(getprop crash_reporter.server)"
Steve Fung0e8746d2015-08-20 17:07:50 -0700291 local brillo_version="$(get_key_value "${meta_path}" "ver")"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000292 local hwclass="$(get_hardware_class)"
293 local write_payload_size="$(get_key_value "${meta_path}" "payload_size")"
294 local log="$(get_key_value "${meta_path}" "log")"
295 local sig="$(get_key_value "${meta_path}" "sig")"
Steve Fung0e8746d2015-08-20 17:07:50 -0700296 local send_payload_size="$(stat -c "%s" "${report_payload}" 2>/dev/null)"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000297 local product="$(get_key_value "${meta_path}" "upload_var_prod")"
298 local version="$(get_key_value "${meta_path}" "upload_var_ver")"
299 local upload_prefix="$(get_key_value "${meta_path}" "upload_prefix")"
300 local guid
Steve Funged789302015-09-14 16:14:23 -0700301 local model_manifest_id="$(get_key_value "${WEAVE_CONF_FILE}" "model_id")"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000302
Steve Fung44aec5f2015-09-12 02:52:06 -0700303 # If crash_reporter.server is not set return with an error.
304 if [ -z "${url}" ]; then
305 lecho "Configuration error: crash_reporter.server not set."
306 return 1
307 fi
308
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000309 set -- \
310 -F "write_payload_size=${write_payload_size}" \
311 -F "send_payload_size=${send_payload_size}"
312 if [ "${sig}" != "undefined" ]; then
313 set -- "$@" \
314 -F "sig=${sig}" \
315 -F "sig2=${sig}"
316 fi
317 if [ -r "${report_payload}" ]; then
318 set -- "$@" \
319 -F "upload_file_${kind}=@${report_payload}"
320 fi
321 if [ "${log}" != "undefined" -a -r "${log}" ]; then
322 set -- "$@" \
323 -F "log=@${log}"
324 fi
325
326 if [ "${upload_prefix}" = "undefined" ]; then
327 upload_prefix=""
328 fi
329
330 # Grab any variable that begins with upload_.
331 local v
332 for k in $(get_keys "${meta_path}" "^upload_"); do
333 v="$(get_key_value "${meta_path}" "${k}")"
334 case ${k} in
335 # Product & version are handled separately.
336 upload_var_prod) ;;
337 upload_var_ver) ;;
338 upload_var_*)
339 set -- "$@" -F "${upload_prefix}${k#upload_var_}=${v}"
340 ;;
341 upload_file_*)
342 if [ -r "${v}" ]; then
343 set -- "$@" -F "${upload_prefix}${k#upload_file_}=@${v}"
344 fi
345 ;;
346 esac
347 done
348
349 # When uploading Chrome reports we need to report the right product and
350 # version. If the meta file does not specify it, use GOOGLE_CRASH_ID
351 # as the product and GOOGLE_CRASH_VERSION_ID as the version.
352 if [ "${product}" = "undefined" ]; then
353 product="$(get_key_value /etc/os-release 'GOOGLE_CRASH_ID')"
354 fi
355 if [ "${version}" = "undefined" ]; then
356 version="$(get_key_value /etc/os-release 'GOOGLE_CRASH_VERSION_ID')"
357 fi
358
359 # If GOOGLE_CRASH_* is undefined, we look for ID and VERSION_ID in
360 # /etc/os-release.
361 if [ "${product}" = "undefined" ]; then
362 product="$(get_key_value /etc/os-release 'ID')"
363 fi
364 if [ "${version}" = "undefined" ]; then
365 version="$(get_key_value /etc/os-release 'VERSION_ID')"
366 fi
367
368 # If ID or VERSION_ID is undefined, we use the default product name
369 # and CHROMEOS_RELEASE_VERSION from /etc/lsb-release.
370 if [ "${product}" = "undefined" ]; then
Steve Fung0e8746d2015-08-20 17:07:50 -0700371 product="${BRILLO_PRODUCT}"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000372 fi
373 if [ "${version}" = "undefined" ]; then
Steve Fung0e8746d2015-08-20 17:07:50 -0700374 version="${brillo_version}"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000375 fi
376
377 local image_type
378 if is_test_image; then
379 image_type="test"
380 elif is_developer_image; then
381 image_type="dev"
382 elif [ ${FORCE_OFFICIAL} -ne 0 ]; then
383 image_type="force-official"
384 elif is_mock && ! is_mock_successful; then
385 image_type="mock-fail"
386 fi
387
388 local boot_mode
Steve Fung0e8746d2015-08-20 17:07:50 -0700389 if is_developer_mode; then
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000390 boot_mode="dev"
391 fi
392
393 # Need to strip dashes ourselves as Chrome preserves it in the file
394 # nowadays. This is also what the Chrome breakpad client does.
Steve Fung12e61ca2015-09-15 16:36:33 -0700395 guid=$(tr -d '-' < "${GUID_FILE}")
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000396
397 local error_type="$(get_key_value "${meta_path}" "error_type")"
398 [ "${error_type}" = "undefined" ] && error_type=
399
400 lecho "Sending crash:"
Steve Fung0e8746d2015-08-20 17:07:50 -0700401 if [ "${product}" != "${BRILLO_PRODUCT}" ]; then
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000402 lecho " Sending crash report on behalf of ${product}"
403 fi
404 lecho " Metadata: ${meta_path} (${kind})"
405 lecho " Payload: ${report_payload}"
406 lecho " Version: ${version}"
407 [ -n "${image_type}" ] && lecho " Image type: ${image_type}"
408 [ -n "${boot_mode}" ] && lecho " Boot mode: ${boot_mode}"
409 if is_mock; then
410 lecho " Product: ${product}"
411 lecho " URL: ${url}"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000412 lecho " HWClass: ${hwclass}"
413 lecho " write_payload_size: ${write_payload_size}"
414 lecho " send_payload_size: ${send_payload_size}"
415 if [ "${log}" != "undefined" ]; then
416 lecho " log: @${log}"
417 fi
418 if [ "${sig}" != "undefined" ]; then
419 lecho " sig: ${sig}"
420 fi
421 fi
422 lecho " Exec name: ${exec_name}"
423 [ -n "${error_type}" ] && lecho " Error type: ${error_type}"
424 if is_mock; then
425 if ! is_mock_successful; then
426 lecho "Mocking unsuccessful send"
427 return 1
428 fi
429 lecho "Mocking successful send"
430 return 0
431 fi
432
433 # Read in the first proxy, if any, for a given URL. NOTE: The
434 # double-quotes are necessary due to a bug in dash with the "local"
435 # builtin command and values that have spaces in them (see
436 # "https://bugs.launchpad.net/ubuntu/+source/dash/+bug/139097").
Dan Colish8a59e112015-06-03 15:11:21 -0700437 if [ -f "${LIST_PROXIES}" ]; then
438 local proxy ret
439 proxy=$("${LIST_PROXIES}" --quiet "${url}")
440 ret=$?
441 if [ ${ret} -ne 0 ]; then
442 proxy=''
443 lecho -psyslog.warn \
444 "Listing proxies failed with exit code ${ret}"
445 else
446 proxy=$(echo "${proxy}" | head -1)
447 fi
448 fi
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000449 # if a direct connection should be used, unset the proxy variable.
450 [ "${proxy}" = "direct://" ] && proxy=
451 local report_id="${TMP_DIR}/report_id"
452 local curl_stderr="${TMP_DIR}/curl_stderr"
453
454 set +e
455 curl "${url}" -v ${proxy:+--proxy "$proxy"} \
456 --capath "${RESTRICTED_CERTIFICATES_PATH}" --ciphers HIGH \
457 -F "prod=${product}" \
458 -F "ver=${version}" \
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000459 -F "hwclass=${hwclass}" \
460 -F "exec_name=${exec_name}" \
Steve Funged789302015-09-14 16:14:23 -0700461 -F "model_manifest_id=${model_manifest_id}" \
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000462 ${image_type:+-F "image_type=${image_type}"} \
463 ${boot_mode:+-F "boot_mode=${boot_mode}"} \
464 ${error_type:+-F "error_type=${error_type}"} \
465 -F "guid=${guid}" \
466 -o "${report_id}" \
467 "$@" \
468 2>"${curl_stderr}"
469 curl_result=$?
470 set -e
471
472 if [ ${curl_result} -eq 0 ]; then
473 local id="$(cat "${report_id}")"
474 local product_name
475 local timestamp="$(date +%s)"
476 case ${product} in
477 Chrome_ChromeOS)
478 if is_official_image; then
479 product_name="Chrome"
480 else
481 product_name="Chromium"
482 fi
483 ;;
484 *)
Steve Fung0e8746d2015-08-20 17:07:50 -0700485 product_name="Brillo"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000486 ;;
487 esac
488 printf '%s,%s,%s\n' \
Steve Fung0e8746d2015-08-20 17:07:50 -0700489 "${timestamp}" "${id}" "${product_name}" >> "${CRASH_LOG}"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000490 lecho "Crash report receipt ID ${id}"
491 else
492 lecho "Crash sending failed with exit code ${curl_result}: " \
493 "$(cat "${curl_stderr}")"
494 fi
495
496 rm -f "${report_id}"
497
498 return ${curl_result}
499}
500
501# *.meta files always end with done=1 so we can tell if they are complete.
502is_complete_metadata() {
503 grep -q "done=1" "$1"
504}
505
506# Remove the given report path.
507remove_report() {
508 local base="${1%.*}"
509 rm -f -- "${base}".*
510}
511
512# Send all crashes from the given directory. This applies even when we're on a
513# 3G connection (see crosbug.com/3304 for discussion).
514send_crashes() {
515 local dir="$1"
Steve Fung0e8746d2015-08-20 17:07:50 -0700516 lecho "Sending crashes for ${dir}"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000517
518 if [ ! -d "${dir}" ]; then
519 return
520 fi
521
522 # Consider any old files which still have no corresponding meta file
523 # as orphaned, and remove them.
Steve Fung0e8746d2015-08-20 17:07:50 -0700524 for old_file in $(find "${dir}" -mindepth 1 \
525 -mtime +1 -type f); do
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000526 if [ ! -e "$(get_base "${old_file}").meta" ]; then
527 lecho "Removing old orphaned file: ${old_file}."
528 rm -f -- "${old_file}"
529 fi
530 done
531
532 # Look through all metadata (*.meta) files, oldest first. That way, the rate
533 # limit does not stall old crashes if there's a high amount of new crashes
534 # coming in.
535 # For each crash report, first evaluate conditions that might lead to its
536 # removal to honor user choice and to free disk space as soon as possible,
537 # then decide whether it should be sent right now or kept for later sending.
538 for meta_path in $(ls -1tr "${dir}"/*.meta 2>/dev/null); do
539 lecho "Considering metadata ${meta_path}."
540
541 local kind=$(get_kind "${meta_path}")
542 if [ "${kind}" != "minidump" ] && \
543 [ "${kind}" != "kcrash" ] && \
Peter Qiu6aa551e2015-03-06 11:42:04 -0800544 [ "${kind}" != "log" ] &&
545 [ "${kind}" != "devcore" ]; then
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000546 lecho "Unknown report kind ${kind}. Removing report."
547 remove_report "${meta_path}"
548 continue
549 fi
550
551 if ! is_complete_metadata "${meta_path}"; then
552 # This report is incomplete, so if it's old, just remove it.
Steve Fung0e8746d2015-08-20 17:07:50 -0700553 local old_meta=$(find "${dir}" -mindepth 1 -name \
554 $(basename "${meta_path}") -mtime +1 -type f)
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000555 if [ -n "${old_meta}" ]; then
556 lecho "Removing old incomplete metadata."
557 remove_report "${meta_path}"
558 else
559 lecho "Ignoring recent incomplete metadata."
560 fi
561 continue
562 fi
563
Peter Qiu6aa551e2015-03-06 11:42:04 -0800564 # Ignore device coredump if device coredump uploading is not allowed.
565 if [ "${kind}" = "devcore" ] && ! is_device_coredump_upload_allowed; then
566 lecho "Ignoring device coredump. Device coredump upload not allowed."
567 continue
568 fi
569
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000570 if ! is_mock && ! is_official_image; then
571 lecho "Not an official OS version. Removing crash."
572 remove_report "${meta_path}"
573 continue
574 fi
575
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000576 # Remove existing crashes in case user consent has not (yet) been given or
577 # has been revoked. This must come after the guest mode check because
Steve Fung0e8746d2015-08-20 17:07:50 -0700578 # metrics_client always returns "not consented" in guest mode.
579 if ! metrics_client -c; then
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000580 lecho "Crash reporting is disabled. Removing crash."
581 remove_report "${meta_path}"
582 continue
583 fi
584
585 # Skip report if the upload rate is exceeded. (Don't exit right now because
586 # subsequent reports may be candidates for deletion.)
587 if ! check_rate; then
588 lecho "Sending ${meta_path} would exceed rate. Leaving for later."
589 continue
590 fi
591
592 # The .meta file should be written *after* all to-be-uploaded files that it
593 # references. Nevertheless, as a safeguard, a hold-off time of thirty
594 # seconds after writing the .meta file is ensured. Also, sending of crash
595 # reports is spread out randomly by up to SECONDS_SEND_SPREAD. Thus, for
596 # the sleep call the greater of the two delays is used.
597 local now=$(date +%s)
Steve Fung0e8746d2015-08-20 17:07:50 -0700598 local holdoff_time=$(($(stat -c "%Y" "${meta_path}") + 30 - ${now}))
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000599 local spread_time=$(generate_uniform_random "${SECONDS_SEND_SPREAD}")
600 local sleep_time
601 if [ ${spread_time} -gt ${holdoff_time} ]; then
602 sleep_time="${spread_time}"
603 else
604 sleep_time="${holdoff_time}"
605 fi
606 lecho "Scheduled to send in ${sleep_time}s."
607 if ! is_mock; then
608 if ! sleep "${sleep_time}"; then
609 lecho "Sleep failed"
610 return 1
611 fi
612 fi
613
614 # Try to upload.
615 if ! send_crash "${meta_path}"; then
616 lecho "Problem sending ${meta_path}, not removing."
617 continue
618 fi
619
620 # Send was successful, now remove.
621 lecho "Successfully sent crash ${meta_path} and removing."
622 remove_report "${meta_path}"
623 done
624}
625
626usage() {
627 cat <<EOF
628Usage: crash_sender [options]
629
630Options:
631 -e <var>=<val> Set env |var| to |val| (only some vars)
632EOF
633 exit ${1:-1}
634}
635
636parseargs() {
637 # Parse the command line arguments.
638 while [ $# -gt 0 ]; do
639 case $1 in
640 -e)
641 shift
642 case $1 in
643 FORCE_OFFICIAL=*|\
644 MAX_CRASH_RATE=*|\
645 MOCK_DEVELOPER_MODE=*|\
646 OVERRIDE_PAUSE_SENDING=*|\
647 SECONDS_SEND_SPREAD=*)
648 export "$1"
649 ;;
650 *)
651 lecho "Unknown var passed to -e: $1"
652 exit 1
653 ;;
654 esac
655 ;;
656 -h)
657 usage 0
658 ;;
659 *)
660 lecho "Unknown options: $*"
661 exit 1
662 ;;
663 esac
664 shift
665 done
666}
667
668main() {
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000669 parseargs "$@"
670
671 if [ -e "${PAUSE_CRASH_SENDING}" ] && \
672 [ ${OVERRIDE_PAUSE_SENDING} -eq 0 ]; then
673 lecho "Exiting early due to ${PAUSE_CRASH_SENDING}."
674 exit 1
675 fi
676
677 if is_test_image; then
678 lecho "Exiting early due to test image."
679 exit 1
680 fi
681
682 # We don't perform checks on this because we have a master lock with the
683 # CRASH_SENDER_LOCK file. This pid file is for the system to keep track
684 # (like with autotests) that we're still running.
685 echo $$ > "${RUN_FILE}"
686
Steve Fung0e8746d2015-08-20 17:07:50 -0700687 for dependency in "${RESTRICTED_CERTIFICATES_PATH}"; do
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000688 if [ ! -x "${dependency}" ]; then
689 lecho "Fatal: Crash sending disabled: ${dependency} not found."
690 exit 1
691 fi
692 done
693
Steve Fung0e8746d2015-08-20 17:07:50 -0700694 TMP_DIR="$(mktemp -d "${CRASH_STATE_DIR}/tmp/crash_sender.XXXXXX")"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000695
696 # Send system-wide crashes
Steve Fung0e8746d2015-08-20 17:07:50 -0700697 send_crashes "${CRASH_STATE_DIR}/crash"
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000698}
699
Steve Fung0e8746d2015-08-20 17:07:50 -0700700trap cleanup EXIT INT TERM
701
702#TODO(http://b/23937249): Change the locking logic back to using flock.
703if ! mkdir "${CRASH_SENDER_LOCK}" 2>/dev/null; then
Benjamin Lermanc8cb4ac2014-09-11 12:43:41 +0000704 lecho "Already running; quitting."
705 crash_done
706 exit 1
707fi
708main "$@"