|
1 | 1 | #include "sentry_os.h"
|
| 2 | +#include "sentry_slice.h" |
2 | 3 | #include "sentry_string.h"
|
3 |
| -#include "sentry_utils.h" |
| 4 | +#if defined(SENTRY_PLATFORM_LINUX) || defined(SENTRY_PLATFORM_WINDOWS) |
| 5 | +# include "sentry_utils.h" |
| 6 | +#endif |
| 7 | +#ifdef SENTRY_PLATFORM_LINUX |
| 8 | +# include <unistd.h> |
| 9 | +#endif |
4 | 10 |
|
5 | 11 | #ifdef SENTRY_PLATFORM_WINDOWS
|
6 | 12 |
|
@@ -256,8 +262,123 @@ sentry__get_os_context(void)
|
256 | 262 | }
|
257 | 263 | #elif defined(SENTRY_PLATFORM_UNIX)
|
258 | 264 |
|
| 265 | +# include <fcntl.h> |
259 | 266 | # include <sys/utsname.h>
|
260 | 267 |
|
| 268 | +# if defined(SENTRY_PLATFORM_LINUX) |
| 269 | +# define OS_RELEASE_MAX_LINE_SIZE 256 |
| 270 | +# define OS_RELEASE_MAX_KEY_SIZE 64 |
| 271 | +# define OS_RELEASE_MAX_VALUE_SIZE 128 |
| 272 | + |
| 273 | +static int |
| 274 | +parse_os_release_line(const char *line, char *key, char *value) |
| 275 | +{ |
| 276 | + const char *equals = strchr(line, '='); |
| 277 | + if (equals == NULL) |
| 278 | + return 1; |
| 279 | + |
| 280 | + unsigned long key_length = MIN(equals - line, OS_RELEASE_MAX_KEY_SIZE - 1); |
| 281 | + strncpy(key, line, key_length); |
| 282 | + key[key_length] = '\0'; |
| 283 | + |
| 284 | + sentry_slice_t value_slice |
| 285 | + = { .ptr = equals + 1, .len = strlen(equals + 1) }; |
| 286 | + |
| 287 | + // some values are wrapped in double quotes |
| 288 | + if (value_slice.ptr[0] == '\"') { |
| 289 | + value_slice.ptr++; |
| 290 | + value_slice.len -= 2; |
| 291 | + } |
| 292 | + |
| 293 | + sentry__slice_to_buffer(value_slice, value, OS_RELEASE_MAX_VALUE_SIZE); |
| 294 | + |
| 295 | + return 0; |
| 296 | +} |
| 297 | + |
| 298 | +static void |
| 299 | +parse_line_into_object(const char *line, sentry_value_t os_dist) |
| 300 | +{ |
| 301 | + char value[OS_RELEASE_MAX_VALUE_SIZE]; |
| 302 | + char key[OS_RELEASE_MAX_KEY_SIZE]; |
| 303 | + |
| 304 | + if (parse_os_release_line(line, key, value) == 0) { |
| 305 | + if (strcmp(key, "ID") == 0) { |
| 306 | + sentry_value_set_by_key( |
| 307 | + os_dist, "name", sentry_value_new_string(value)); |
| 308 | + } |
| 309 | + |
| 310 | + if (strcmp(key, "VERSION_ID") == 0) { |
| 311 | + sentry_value_set_by_key( |
| 312 | + os_dist, "version", sentry_value_new_string(value)); |
| 313 | + } |
| 314 | + |
| 315 | + if (strcmp(key, "PRETTY_NAME") == 0) { |
| 316 | + sentry_value_set_by_key( |
| 317 | + os_dist, "pretty_name", sentry_value_new_string(value)); |
| 318 | + } |
| 319 | + } |
| 320 | +} |
| 321 | + |
| 322 | +# ifndef SENTRY_UNITTEST |
| 323 | +static |
| 324 | +# endif |
| 325 | + sentry_value_t |
| 326 | + get_linux_os_release(const char *os_rel_path) |
| 327 | +{ |
| 328 | + const int fd = open(os_rel_path, O_RDONLY); |
| 329 | + if (fd == -1) { |
| 330 | + return sentry_value_new_null(); |
| 331 | + } |
| 332 | + |
| 333 | + sentry_value_t os_dist = sentry_value_new_object(); |
| 334 | + char buffer[OS_RELEASE_MAX_LINE_SIZE]; |
| 335 | + ssize_t bytes_read; |
| 336 | + ssize_t buffer_rest = 0; |
| 337 | + const char *line = buffer; |
| 338 | + while ((bytes_read = read( |
| 339 | + fd, buffer + buffer_rest, sizeof(buffer) - buffer_rest - 1)) |
| 340 | + > 0) { |
| 341 | + ssize_t buffer_end = buffer_rest + bytes_read; |
| 342 | + buffer[buffer_end] = '\0'; |
| 343 | + |
| 344 | + // extract all lines from the valid buffer-range and parse them |
| 345 | + for (char *p = buffer; *p; ++p) { |
| 346 | + if (*p != '\n') { |
| 347 | + continue; |
| 348 | + } |
| 349 | + *p = '\0'; |
| 350 | + parse_line_into_object(line, os_dist); |
| 351 | + line = p + 1; |
| 352 | + } |
| 353 | + |
| 354 | + if (line < buffer + buffer_end) { |
| 355 | + // move the remaining partial line to the start of the buffer |
| 356 | + buffer_rest = buffer + buffer_end - line; |
| 357 | + memmove(buffer, line, buffer_rest); |
| 358 | + } else { |
| 359 | + // reset buffer_rest: the line-end coincided with the buffer-end |
| 360 | + buffer_rest = 0; |
| 361 | + } |
| 362 | + line = buffer; |
| 363 | + } |
| 364 | + |
| 365 | + if (bytes_read == -1) { |
| 366 | + // read() failed and we can't assume to have valid data |
| 367 | + sentry_value_decref(os_dist); |
| 368 | + os_dist = sentry_value_new_null(); |
| 369 | + } else if (buffer_rest > 0) { |
| 370 | + // the file ended w/o a new-line; we still have a line left to parse |
| 371 | + buffer[buffer_rest] = '\0'; |
| 372 | + parse_line_into_object(line, os_dist); |
| 373 | + } |
| 374 | + |
| 375 | + close(fd); |
| 376 | + |
| 377 | + return os_dist; |
| 378 | +} |
| 379 | + |
| 380 | +# endif // defined(SENTRY_PLATFORM_LINUX) |
| 381 | + |
261 | 382 | sentry_value_t
|
262 | 383 | sentry__get_os_context(void)
|
263 | 384 | {
|
@@ -298,6 +419,35 @@ sentry__get_os_context(void)
|
298 | 419 | sentry_value_set_by_key(
|
299 | 420 | os, "version", sentry_value_new_string(uts.release));
|
300 | 421 |
|
| 422 | +# if defined(SENTRY_PLATFORM_LINUX) |
| 423 | + /** |
| 424 | + * The file /etc/os-release takes precedence over /usr/lib/os-release. |
| 425 | + * Applications should check for the former, and exclusively use its data if |
| 426 | + * it exists, and only fall back to /usr/lib/os-release if it is missing. |
| 427 | + * Applications should not read data from both files at the same time. |
| 428 | + * |
| 429 | + * From: |
| 430 | + * https://www.freedesktop.org/software/systemd/man/latest/os-release.html#Description |
| 431 | + */ |
| 432 | + sentry_value_t os_dist = get_linux_os_release("/etc/os-release"); |
| 433 | + if (sentry_value_is_null(os_dist)) { |
| 434 | + os_dist = get_linux_os_release("/usr/lib/os-release"); |
| 435 | + if (sentry_value_is_null(os_dist)) { |
| 436 | + return os; |
| 437 | + } |
| 438 | + } |
| 439 | + sentry_value_set_by_key( |
| 440 | + os, "distribution_name", sentry_value_get_by_key(os_dist, "name")); |
| 441 | + sentry_value_set_by_key(os, "distribution_version", |
| 442 | + sentry_value_get_by_key(os_dist, "version")); |
| 443 | + sentry_value_set_by_key(os, "distribution_pretty_name", |
| 444 | + sentry_value_get_by_key(os_dist, "pretty_name")); |
| 445 | + sentry_value_incref(sentry_value_get_by_key(os_dist, "name")); |
| 446 | + sentry_value_incref(sentry_value_get_by_key(os_dist, "version")); |
| 447 | + sentry_value_incref(sentry_value_get_by_key(os_dist, "pretty_name")); |
| 448 | + sentry_value_decref(os_dist); |
| 449 | +# endif // defined(SENTRY_PLATFORM_LINUX) |
| 450 | + |
301 | 451 | return os;
|
302 | 452 |
|
303 | 453 | fail:
|
|
0 commit comments