1// Copyright 2015 RedHat, Inc. 2// Copyright 2015 CoreOS, Inc. 3// 4// Licensed under the Apache License, Version 2.0 (the "License"); 5// you may not use this file except in compliance with the License. 6// You may obtain a copy of the License at 7// 8// http://www.apache.org/licenses/LICENSE-2.0 9// 10// Unless required by applicable law or agreed to in writing, software 11// distributed under the License is distributed on an "AS IS" BASIS, 12// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13// See the License for the specific language governing permissions and 14// limitations under the License. 15 16// Package sdjournal provides a low-level Go interface to the 17// systemd journal wrapped around the sd-journal C API. 18// 19// All public read methods map closely to the sd-journal API functions. See the 20// sd-journal.h documentation[1] for information about each function. 21// 22// To write to the journal, see the pure-Go "journal" package 23// 24// [1] http://www.freedesktop.org/software/systemd/man/sd-journal.html 25package sdjournal 26 27// #include <systemd/sd-journal.h> 28// #include <systemd/sd-id128.h> 29// #include <stdlib.h> 30// #include <syslog.h> 31// 32// int 33// my_sd_journal_open(void *f, sd_journal **ret, int flags) 34// { 35// int (*sd_journal_open)(sd_journal **, int); 36// 37// sd_journal_open = f; 38// return sd_journal_open(ret, flags); 39// } 40// 41// int 42// my_sd_journal_open_directory(void *f, sd_journal **ret, const char *path, int flags) 43// { 44// int (*sd_journal_open_directory)(sd_journal **, const char *, int); 45// 46// sd_journal_open_directory = f; 47// return sd_journal_open_directory(ret, path, flags); 48// } 49// 50// int 51// my_sd_journal_open_files(void *f, sd_journal **ret, const char **paths, int flags) 52// { 53// int (*sd_journal_open_files)(sd_journal **, const char **, int); 54// 55// sd_journal_open_files = f; 56// return sd_journal_open_files(ret, paths, flags); 57// } 58// 59// void 60// my_sd_journal_close(void *f, sd_journal *j) 61// { 62// int (*sd_journal_close)(sd_journal *); 63// 64// sd_journal_close = f; 65// sd_journal_close(j); 66// } 67// 68// int 69// my_sd_journal_get_usage(void *f, sd_journal *j, uint64_t *bytes) 70// { 71// int (*sd_journal_get_usage)(sd_journal *, uint64_t *); 72// 73// sd_journal_get_usage = f; 74// return sd_journal_get_usage(j, bytes); 75// } 76// 77// int 78// my_sd_journal_add_match(void *f, sd_journal *j, const void *data, size_t size) 79// { 80// int (*sd_journal_add_match)(sd_journal *, const void *, size_t); 81// 82// sd_journal_add_match = f; 83// return sd_journal_add_match(j, data, size); 84// } 85// 86// int 87// my_sd_journal_add_disjunction(void *f, sd_journal *j) 88// { 89// int (*sd_journal_add_disjunction)(sd_journal *); 90// 91// sd_journal_add_disjunction = f; 92// return sd_journal_add_disjunction(j); 93// } 94// 95// int 96// my_sd_journal_add_conjunction(void *f, sd_journal *j) 97// { 98// int (*sd_journal_add_conjunction)(sd_journal *); 99// 100// sd_journal_add_conjunction = f; 101// return sd_journal_add_conjunction(j); 102// } 103// 104// void 105// my_sd_journal_flush_matches(void *f, sd_journal *j) 106// { 107// int (*sd_journal_flush_matches)(sd_journal *); 108// 109// sd_journal_flush_matches = f; 110// sd_journal_flush_matches(j); 111// } 112// 113// int 114// my_sd_journal_next(void *f, sd_journal *j) 115// { 116// int (*sd_journal_next)(sd_journal *); 117// 118// sd_journal_next = f; 119// return sd_journal_next(j); 120// } 121// 122// int 123// my_sd_journal_next_skip(void *f, sd_journal *j, uint64_t skip) 124// { 125// int (*sd_journal_next_skip)(sd_journal *, uint64_t); 126// 127// sd_journal_next_skip = f; 128// return sd_journal_next_skip(j, skip); 129// } 130// 131// int 132// my_sd_journal_previous(void *f, sd_journal *j) 133// { 134// int (*sd_journal_previous)(sd_journal *); 135// 136// sd_journal_previous = f; 137// return sd_journal_previous(j); 138// } 139// 140// int 141// my_sd_journal_previous_skip(void *f, sd_journal *j, uint64_t skip) 142// { 143// int (*sd_journal_previous_skip)(sd_journal *, uint64_t); 144// 145// sd_journal_previous_skip = f; 146// return sd_journal_previous_skip(j, skip); 147// } 148// 149// int 150// my_sd_journal_get_data(void *f, sd_journal *j, const char *field, const void **data, size_t *length) 151// { 152// int (*sd_journal_get_data)(sd_journal *, const char *, const void **, size_t *); 153// 154// sd_journal_get_data = f; 155// return sd_journal_get_data(j, field, data, length); 156// } 157// 158// int 159// my_sd_journal_set_data_threshold(void *f, sd_journal *j, size_t sz) 160// { 161// int (*sd_journal_set_data_threshold)(sd_journal *, size_t); 162// 163// sd_journal_set_data_threshold = f; 164// return sd_journal_set_data_threshold(j, sz); 165// } 166// 167// int 168// my_sd_journal_get_cursor(void *f, sd_journal *j, char **cursor) 169// { 170// int (*sd_journal_get_cursor)(sd_journal *, char **); 171// 172// sd_journal_get_cursor = f; 173// return sd_journal_get_cursor(j, cursor); 174// } 175// 176// int 177// my_sd_journal_test_cursor(void *f, sd_journal *j, const char *cursor) 178// { 179// int (*sd_journal_test_cursor)(sd_journal *, const char *); 180// 181// sd_journal_test_cursor = f; 182// return sd_journal_test_cursor(j, cursor); 183// } 184// 185// int 186// my_sd_journal_get_realtime_usec(void *f, sd_journal *j, uint64_t *usec) 187// { 188// int (*sd_journal_get_realtime_usec)(sd_journal *, uint64_t *); 189// 190// sd_journal_get_realtime_usec = f; 191// return sd_journal_get_realtime_usec(j, usec); 192// } 193// 194// int 195// my_sd_journal_get_monotonic_usec(void *f, sd_journal *j, uint64_t *usec, sd_id128_t *boot_id) 196// { 197// int (*sd_journal_get_monotonic_usec)(sd_journal *, uint64_t *, sd_id128_t *); 198// 199// sd_journal_get_monotonic_usec = f; 200// return sd_journal_get_monotonic_usec(j, usec, boot_id); 201// } 202// 203// int 204// my_sd_journal_seek_head(void *f, sd_journal *j) 205// { 206// int (*sd_journal_seek_head)(sd_journal *); 207// 208// sd_journal_seek_head = f; 209// return sd_journal_seek_head(j); 210// } 211// 212// int 213// my_sd_journal_seek_tail(void *f, sd_journal *j) 214// { 215// int (*sd_journal_seek_tail)(sd_journal *); 216// 217// sd_journal_seek_tail = f; 218// return sd_journal_seek_tail(j); 219// } 220// 221// 222// int 223// my_sd_journal_seek_cursor(void *f, sd_journal *j, const char *cursor) 224// { 225// int (*sd_journal_seek_cursor)(sd_journal *, const char *); 226// 227// sd_journal_seek_cursor = f; 228// return sd_journal_seek_cursor(j, cursor); 229// } 230// 231// int 232// my_sd_journal_seek_realtime_usec(void *f, sd_journal *j, uint64_t usec) 233// { 234// int (*sd_journal_seek_realtime_usec)(sd_journal *, uint64_t); 235// 236// sd_journal_seek_realtime_usec = f; 237// return sd_journal_seek_realtime_usec(j, usec); 238// } 239// 240// int 241// my_sd_journal_wait(void *f, sd_journal *j, uint64_t timeout_usec) 242// { 243// int (*sd_journal_wait)(sd_journal *, uint64_t); 244// 245// sd_journal_wait = f; 246// return sd_journal_wait(j, timeout_usec); 247// } 248// 249// void 250// my_sd_journal_restart_data(void *f, sd_journal *j) 251// { 252// void (*sd_journal_restart_data)(sd_journal *); 253// 254// sd_journal_restart_data = f; 255// sd_journal_restart_data(j); 256// } 257// 258// int 259// my_sd_journal_enumerate_data(void *f, sd_journal *j, const void **data, size_t *length) 260// { 261// int (*sd_journal_enumerate_data)(sd_journal *, const void **, size_t *); 262// 263// sd_journal_enumerate_data = f; 264// return sd_journal_enumerate_data(j, data, length); 265// } 266// 267// int 268// my_sd_journal_query_unique(void *f, sd_journal *j, const char *field) 269// { 270// int(*sd_journal_query_unique)(sd_journal *, const char *); 271// 272// sd_journal_query_unique = f; 273// return sd_journal_query_unique(j, field); 274// } 275// 276// int 277// my_sd_journal_enumerate_unique(void *f, sd_journal *j, const void **data, size_t *length) 278// { 279// int(*sd_journal_enumerate_unique)(sd_journal *, const void **, size_t *); 280// 281// sd_journal_enumerate_unique = f; 282// return sd_journal_enumerate_unique(j, data, length); 283// } 284// 285// void 286// my_sd_journal_restart_unique(void *f, sd_journal *j) 287// { 288// void(*sd_journal_restart_unique)(sd_journal *); 289// 290// sd_journal_restart_unique = f; 291// sd_journal_restart_unique(j); 292// } 293// 294// int 295// my_sd_journal_get_catalog(void *f, sd_journal *j, char **ret) 296// { 297// int(*sd_journal_get_catalog)(sd_journal *, char **); 298// 299// sd_journal_get_catalog = f; 300// return sd_journal_get_catalog(j, ret); 301// } 302// 303// int 304// my_sd_id128_get_boot(void *f, sd_id128_t *boot_id) 305// { 306// int(*sd_id128_get_boot)(sd_id128_t *); 307// 308// sd_id128_get_boot = f; 309// return sd_id128_get_boot(boot_id); 310// } 311// 312// char * 313// my_sd_id128_to_string(void *f, sd_id128_t boot_id, char s[SD_ID128_STRING_MAX]) 314// { 315// char *(*sd_id128_to_string)(sd_id128_t, char *); 316// 317// sd_id128_to_string = f; 318// return sd_id128_to_string(boot_id, s); 319// } 320// 321import "C" 322import ( 323 "bytes" 324 "errors" 325 "fmt" 326 "strings" 327 "sync" 328 "syscall" 329 "time" 330 "unsafe" 331) 332 333// Journal entry field strings which correspond to: 334// http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html 335const ( 336 // User Journal Fields 337 SD_JOURNAL_FIELD_MESSAGE = "MESSAGE" 338 SD_JOURNAL_FIELD_MESSAGE_ID = "MESSAGE_ID" 339 SD_JOURNAL_FIELD_PRIORITY = "PRIORITY" 340 SD_JOURNAL_FIELD_CODE_FILE = "CODE_FILE" 341 SD_JOURNAL_FIELD_CODE_LINE = "CODE_LINE" 342 SD_JOURNAL_FIELD_CODE_FUNC = "CODE_FUNC" 343 SD_JOURNAL_FIELD_ERRNO = "ERRNO" 344 SD_JOURNAL_FIELD_SYSLOG_FACILITY = "SYSLOG_FACILITY" 345 SD_JOURNAL_FIELD_SYSLOG_IDENTIFIER = "SYSLOG_IDENTIFIER" 346 SD_JOURNAL_FIELD_SYSLOG_PID = "SYSLOG_PID" 347 348 // Trusted Journal Fields 349 SD_JOURNAL_FIELD_PID = "_PID" 350 SD_JOURNAL_FIELD_UID = "_UID" 351 SD_JOURNAL_FIELD_GID = "_GID" 352 SD_JOURNAL_FIELD_COMM = "_COMM" 353 SD_JOURNAL_FIELD_EXE = "_EXE" 354 SD_JOURNAL_FIELD_CMDLINE = "_CMDLINE" 355 SD_JOURNAL_FIELD_CAP_EFFECTIVE = "_CAP_EFFECTIVE" 356 SD_JOURNAL_FIELD_AUDIT_SESSION = "_AUDIT_SESSION" 357 SD_JOURNAL_FIELD_AUDIT_LOGINUID = "_AUDIT_LOGINUID" 358 SD_JOURNAL_FIELD_SYSTEMD_CGROUP = "_SYSTEMD_CGROUP" 359 SD_JOURNAL_FIELD_SYSTEMD_SESSION = "_SYSTEMD_SESSION" 360 SD_JOURNAL_FIELD_SYSTEMD_UNIT = "_SYSTEMD_UNIT" 361 SD_JOURNAL_FIELD_SYSTEMD_USER_UNIT = "_SYSTEMD_USER_UNIT" 362 SD_JOURNAL_FIELD_SYSTEMD_OWNER_UID = "_SYSTEMD_OWNER_UID" 363 SD_JOURNAL_FIELD_SYSTEMD_SLICE = "_SYSTEMD_SLICE" 364 SD_JOURNAL_FIELD_SELINUX_CONTEXT = "_SELINUX_CONTEXT" 365 SD_JOURNAL_FIELD_SOURCE_REALTIME_TIMESTAMP = "_SOURCE_REALTIME_TIMESTAMP" 366 SD_JOURNAL_FIELD_BOOT_ID = "_BOOT_ID" 367 SD_JOURNAL_FIELD_MACHINE_ID = "_MACHINE_ID" 368 SD_JOURNAL_FIELD_HOSTNAME = "_HOSTNAME" 369 SD_JOURNAL_FIELD_TRANSPORT = "_TRANSPORT" 370 371 // Address Fields 372 SD_JOURNAL_FIELD_CURSOR = "__CURSOR" 373 SD_JOURNAL_FIELD_REALTIME_TIMESTAMP = "__REALTIME_TIMESTAMP" 374 SD_JOURNAL_FIELD_MONOTONIC_TIMESTAMP = "__MONOTONIC_TIMESTAMP" 375) 376 377// Journal event constants 378const ( 379 SD_JOURNAL_NOP = int(C.SD_JOURNAL_NOP) 380 SD_JOURNAL_APPEND = int(C.SD_JOURNAL_APPEND) 381 SD_JOURNAL_INVALIDATE = int(C.SD_JOURNAL_INVALIDATE) 382) 383 384const ( 385 // IndefiniteWait is a sentinel value that can be passed to 386 // sdjournal.Wait() to signal an indefinite wait for new journal 387 // events. It is implemented as the maximum value for a time.Duration: 388 // https://github.com/golang/go/blob/e4dcf5c8c22d98ac9eac7b9b226596229624cb1d/src/time/time.go#L434 389 IndefiniteWait time.Duration = 1<<63 - 1 390) 391 392var ( 393 // ErrNoTestCursor gets returned when using TestCursor function and cursor 394 // parameter is not the same as the current cursor position. 395 ErrNoTestCursor = errors.New("Cursor parameter is not the same as current position") 396) 397 398// Journal is a Go wrapper of an sd_journal structure. 399type Journal struct { 400 cjournal *C.sd_journal 401 mu sync.Mutex 402} 403 404// JournalEntry represents all fields of a journal entry plus address fields. 405type JournalEntry struct { 406 Fields map[string]string 407 Cursor string 408 RealtimeTimestamp uint64 409 MonotonicTimestamp uint64 410} 411 412// Match is a convenience wrapper to describe filters supplied to AddMatch. 413type Match struct { 414 Field string 415 Value string 416} 417 418// String returns a string representation of a Match suitable for use with AddMatch. 419func (m *Match) String() string { 420 return m.Field + "=" + m.Value 421} 422 423// NewJournal returns a new Journal instance pointing to the local journal 424func NewJournal() (j *Journal, err error) { 425 j = &Journal{} 426 427 sd_journal_open, err := getFunction("sd_journal_open") 428 if err != nil { 429 return nil, err 430 } 431 432 r := C.my_sd_journal_open(sd_journal_open, &j.cjournal, C.SD_JOURNAL_LOCAL_ONLY) 433 434 if r < 0 { 435 return nil, fmt.Errorf("failed to open journal: %s", syscall.Errno(-r).Error()) 436 } 437 438 return j, nil 439} 440 441// NewJournalFromDir returns a new Journal instance pointing to a journal residing 442// in a given directory. 443func NewJournalFromDir(path string) (j *Journal, err error) { 444 j = &Journal{} 445 446 sd_journal_open_directory, err := getFunction("sd_journal_open_directory") 447 if err != nil { 448 return nil, err 449 } 450 451 p := C.CString(path) 452 defer C.free(unsafe.Pointer(p)) 453 454 r := C.my_sd_journal_open_directory(sd_journal_open_directory, &j.cjournal, p, 0) 455 if r < 0 { 456 return nil, fmt.Errorf("failed to open journal in directory %q: %s", path, syscall.Errno(-r).Error()) 457 } 458 459 return j, nil 460} 461 462// NewJournalFromFiles returns a new Journal instance pointing to a journals residing 463// in a given files. 464func NewJournalFromFiles(paths ...string) (j *Journal, err error) { 465 j = &Journal{} 466 467 sd_journal_open_files, err := getFunction("sd_journal_open_files") 468 if err != nil { 469 return nil, err 470 } 471 472 // by making the slice 1 elem too long, we guarantee it'll be null-terminated 473 cPaths := make([]*C.char, len(paths)+1) 474 for idx, path := range paths { 475 p := C.CString(path) 476 cPaths[idx] = p 477 defer C.free(unsafe.Pointer(p)) 478 } 479 480 r := C.my_sd_journal_open_files(sd_journal_open_files, &j.cjournal, &cPaths[0], 0) 481 if r < 0 { 482 return nil, fmt.Errorf("failed to open journals in paths %q: %s", paths, syscall.Errno(-r).Error()) 483 } 484 485 return j, nil 486} 487 488// Close closes a journal opened with NewJournal. 489func (j *Journal) Close() error { 490 sd_journal_close, err := getFunction("sd_journal_close") 491 if err != nil { 492 return err 493 } 494 495 j.mu.Lock() 496 C.my_sd_journal_close(sd_journal_close, j.cjournal) 497 j.mu.Unlock() 498 499 return nil 500} 501 502// AddMatch adds a match by which to filter the entries of the journal. 503func (j *Journal) AddMatch(match string) error { 504 sd_journal_add_match, err := getFunction("sd_journal_add_match") 505 if err != nil { 506 return err 507 } 508 509 m := C.CString(match) 510 defer C.free(unsafe.Pointer(m)) 511 512 j.mu.Lock() 513 r := C.my_sd_journal_add_match(sd_journal_add_match, j.cjournal, unsafe.Pointer(m), C.size_t(len(match))) 514 j.mu.Unlock() 515 516 if r < 0 { 517 return fmt.Errorf("failed to add match: %s", syscall.Errno(-r).Error()) 518 } 519 520 return nil 521} 522 523// AddDisjunction inserts a logical OR in the match list. 524func (j *Journal) AddDisjunction() error { 525 sd_journal_add_disjunction, err := getFunction("sd_journal_add_disjunction") 526 if err != nil { 527 return err 528 } 529 530 j.mu.Lock() 531 r := C.my_sd_journal_add_disjunction(sd_journal_add_disjunction, j.cjournal) 532 j.mu.Unlock() 533 534 if r < 0 { 535 return fmt.Errorf("failed to add a disjunction in the match list: %s", syscall.Errno(-r).Error()) 536 } 537 538 return nil 539} 540 541// AddConjunction inserts a logical AND in the match list. 542func (j *Journal) AddConjunction() error { 543 sd_journal_add_conjunction, err := getFunction("sd_journal_add_conjunction") 544 if err != nil { 545 return err 546 } 547 548 j.mu.Lock() 549 r := C.my_sd_journal_add_conjunction(sd_journal_add_conjunction, j.cjournal) 550 j.mu.Unlock() 551 552 if r < 0 { 553 return fmt.Errorf("failed to add a conjunction in the match list: %s", syscall.Errno(-r).Error()) 554 } 555 556 return nil 557} 558 559// FlushMatches flushes all matches, disjunctions and conjunctions. 560func (j *Journal) FlushMatches() { 561 sd_journal_flush_matches, err := getFunction("sd_journal_flush_matches") 562 if err != nil { 563 return 564 } 565 566 j.mu.Lock() 567 C.my_sd_journal_flush_matches(sd_journal_flush_matches, j.cjournal) 568 j.mu.Unlock() 569} 570 571// Next advances the read pointer into the journal by one entry. 572func (j *Journal) Next() (uint64, error) { 573 sd_journal_next, err := getFunction("sd_journal_next") 574 if err != nil { 575 return 0, err 576 } 577 578 j.mu.Lock() 579 r := C.my_sd_journal_next(sd_journal_next, j.cjournal) 580 j.mu.Unlock() 581 582 if r < 0 { 583 return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error()) 584 } 585 586 return uint64(r), nil 587} 588 589// NextSkip advances the read pointer by multiple entries at once, 590// as specified by the skip parameter. 591func (j *Journal) NextSkip(skip uint64) (uint64, error) { 592 sd_journal_next_skip, err := getFunction("sd_journal_next_skip") 593 if err != nil { 594 return 0, err 595 } 596 597 j.mu.Lock() 598 r := C.my_sd_journal_next_skip(sd_journal_next_skip, j.cjournal, C.uint64_t(skip)) 599 j.mu.Unlock() 600 601 if r < 0 { 602 return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error()) 603 } 604 605 return uint64(r), nil 606} 607 608// Previous sets the read pointer into the journal back by one entry. 609func (j *Journal) Previous() (uint64, error) { 610 sd_journal_previous, err := getFunction("sd_journal_previous") 611 if err != nil { 612 return 0, err 613 } 614 615 j.mu.Lock() 616 r := C.my_sd_journal_previous(sd_journal_previous, j.cjournal) 617 j.mu.Unlock() 618 619 if r < 0 { 620 return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error()) 621 } 622 623 return uint64(r), nil 624} 625 626// PreviousSkip sets back the read pointer by multiple entries at once, 627// as specified by the skip parameter. 628func (j *Journal) PreviousSkip(skip uint64) (uint64, error) { 629 sd_journal_previous_skip, err := getFunction("sd_journal_previous_skip") 630 if err != nil { 631 return 0, err 632 } 633 634 j.mu.Lock() 635 r := C.my_sd_journal_previous_skip(sd_journal_previous_skip, j.cjournal, C.uint64_t(skip)) 636 j.mu.Unlock() 637 638 if r < 0 { 639 return 0, fmt.Errorf("failed to iterate journal: %s", syscall.Errno(-r).Error()) 640 } 641 642 return uint64(r), nil 643} 644 645func (j *Journal) getData(field string) (unsafe.Pointer, C.int, error) { 646 sd_journal_get_data, err := getFunction("sd_journal_get_data") 647 if err != nil { 648 return nil, 0, err 649 } 650 651 f := C.CString(field) 652 defer C.free(unsafe.Pointer(f)) 653 654 var d unsafe.Pointer 655 var l C.size_t 656 657 j.mu.Lock() 658 r := C.my_sd_journal_get_data(sd_journal_get_data, j.cjournal, f, &d, &l) 659 j.mu.Unlock() 660 661 if r < 0 { 662 return nil, 0, fmt.Errorf("failed to read message: %s", syscall.Errno(-r).Error()) 663 } 664 665 return d, C.int(l), nil 666} 667 668// GetData gets the data object associated with a specific field from the 669// the journal entry referenced by the last completed Next/Previous function 670// call. To call GetData, you must have first called one of these functions. 671func (j *Journal) GetData(field string) (string, error) { 672 d, l, err := j.getData(field) 673 if err != nil { 674 return "", err 675 } 676 677 return C.GoStringN((*C.char)(d), l), nil 678} 679 680// GetDataValue gets the data object associated with a specific field from the 681// journal entry referenced by the last completed Next/Previous function call, 682// returning only the value of the object. To call GetDataValue, you must first 683// have called one of the Next/Previous functions. 684func (j *Journal) GetDataValue(field string) (string, error) { 685 val, err := j.GetData(field) 686 if err != nil { 687 return "", err 688 } 689 690 return strings.SplitN(val, "=", 2)[1], nil 691} 692 693// GetDataBytes gets the data object associated with a specific field from the 694// journal entry referenced by the last completed Next/Previous function call. 695// To call GetDataBytes, you must first have called one of these functions. 696func (j *Journal) GetDataBytes(field string) ([]byte, error) { 697 d, l, err := j.getData(field) 698 if err != nil { 699 return nil, err 700 } 701 702 return C.GoBytes(d, l), nil 703} 704 705// GetDataValueBytes gets the data object associated with a specific field from the 706// journal entry referenced by the last completed Next/Previous function call, 707// returning only the value of the object. To call GetDataValueBytes, you must first 708// have called one of the Next/Previous functions. 709func (j *Journal) GetDataValueBytes(field string) ([]byte, error) { 710 val, err := j.GetDataBytes(field) 711 if err != nil { 712 return nil, err 713 } 714 715 return bytes.SplitN(val, []byte("="), 2)[1], nil 716} 717 718// GetEntry returns a full representation of the journal entry referenced by the 719// last completed Next/Previous function call, with all key-value pairs of data 720// as well as address fields (cursor, realtime timestamp and monotonic timestamp). 721// To call GetEntry, you must first have called one of the Next/Previous functions. 722func (j *Journal) GetEntry() (*JournalEntry, error) { 723 sd_journal_get_realtime_usec, err := getFunction("sd_journal_get_realtime_usec") 724 if err != nil { 725 return nil, err 726 } 727 728 sd_journal_get_monotonic_usec, err := getFunction("sd_journal_get_monotonic_usec") 729 if err != nil { 730 return nil, err 731 } 732 733 sd_journal_get_cursor, err := getFunction("sd_journal_get_cursor") 734 if err != nil { 735 return nil, err 736 } 737 738 sd_journal_restart_data, err := getFunction("sd_journal_restart_data") 739 if err != nil { 740 return nil, err 741 } 742 743 sd_journal_enumerate_data, err := getFunction("sd_journal_enumerate_data") 744 if err != nil { 745 return nil, err 746 } 747 748 j.mu.Lock() 749 defer j.mu.Unlock() 750 751 var r C.int 752 entry := &JournalEntry{Fields: make(map[string]string)} 753 754 var realtimeUsec C.uint64_t 755 r = C.my_sd_journal_get_realtime_usec(sd_journal_get_realtime_usec, j.cjournal, &realtimeUsec) 756 if r < 0 { 757 return nil, fmt.Errorf("failed to get realtime timestamp: %s", syscall.Errno(-r).Error()) 758 } 759 760 entry.RealtimeTimestamp = uint64(realtimeUsec) 761 762 var monotonicUsec C.uint64_t 763 var boot_id C.sd_id128_t 764 765 r = C.my_sd_journal_get_monotonic_usec(sd_journal_get_monotonic_usec, j.cjournal, &monotonicUsec, &boot_id) 766 if r < 0 { 767 return nil, fmt.Errorf("failed to get monotonic timestamp: %s", syscall.Errno(-r).Error()) 768 } 769 770 entry.MonotonicTimestamp = uint64(monotonicUsec) 771 772 var c *C.char 773 // since the pointer is mutated by sd_journal_get_cursor, need to wait 774 // until after the call to free the memory 775 r = C.my_sd_journal_get_cursor(sd_journal_get_cursor, j.cjournal, &c) 776 defer C.free(unsafe.Pointer(c)) 777 if r < 0 { 778 return nil, fmt.Errorf("failed to get cursor: %s", syscall.Errno(-r).Error()) 779 } 780 781 entry.Cursor = C.GoString(c) 782 783 // Implements the JOURNAL_FOREACH_DATA_RETVAL macro from journal-internal.h 784 var d unsafe.Pointer 785 var l C.size_t 786 C.my_sd_journal_restart_data(sd_journal_restart_data, j.cjournal) 787 for { 788 r = C.my_sd_journal_enumerate_data(sd_journal_enumerate_data, j.cjournal, &d, &l) 789 if r == 0 { 790 break 791 } 792 793 if r < 0 { 794 return nil, fmt.Errorf("failed to read message field: %s", syscall.Errno(-r).Error()) 795 } 796 797 msg := C.GoStringN((*C.char)(d), C.int(l)) 798 kv := strings.SplitN(msg, "=", 2) 799 if len(kv) < 2 { 800 return nil, fmt.Errorf("failed to parse field") 801 } 802 803 entry.Fields[kv[0]] = kv[1] 804 } 805 806 return entry, nil 807} 808 809// SetDataThreshold sets the data field size threshold for data returned by 810// GetData. To retrieve the complete data fields this threshold should be 811// turned off by setting it to 0, so that the library always returns the 812// complete data objects. 813func (j *Journal) SetDataThreshold(threshold uint64) error { 814 sd_journal_set_data_threshold, err := getFunction("sd_journal_set_data_threshold") 815 if err != nil { 816 return err 817 } 818 819 j.mu.Lock() 820 r := C.my_sd_journal_set_data_threshold(sd_journal_set_data_threshold, j.cjournal, C.size_t(threshold)) 821 j.mu.Unlock() 822 823 if r < 0 { 824 return fmt.Errorf("failed to set data threshold: %s", syscall.Errno(-r).Error()) 825 } 826 827 return nil 828} 829 830// GetRealtimeUsec gets the realtime (wallclock) timestamp of the journal 831// entry referenced by the last completed Next/Previous function call. To 832// call GetRealtimeUsec, you must first have called one of the Next/Previous 833// functions. 834func (j *Journal) GetRealtimeUsec() (uint64, error) { 835 var usec C.uint64_t 836 837 sd_journal_get_realtime_usec, err := getFunction("sd_journal_get_realtime_usec") 838 if err != nil { 839 return 0, err 840 } 841 842 j.mu.Lock() 843 r := C.my_sd_journal_get_realtime_usec(sd_journal_get_realtime_usec, j.cjournal, &usec) 844 j.mu.Unlock() 845 846 if r < 0 { 847 return 0, fmt.Errorf("failed to get realtime timestamp: %s", syscall.Errno(-r).Error()) 848 } 849 850 return uint64(usec), nil 851} 852 853// GetMonotonicUsec gets the monotonic timestamp of the journal entry 854// referenced by the last completed Next/Previous function call. To call 855// GetMonotonicUsec, you must first have called one of the Next/Previous 856// functions. 857func (j *Journal) GetMonotonicUsec() (uint64, error) { 858 var usec C.uint64_t 859 var boot_id C.sd_id128_t 860 861 sd_journal_get_monotonic_usec, err := getFunction("sd_journal_get_monotonic_usec") 862 if err != nil { 863 return 0, err 864 } 865 866 j.mu.Lock() 867 r := C.my_sd_journal_get_monotonic_usec(sd_journal_get_monotonic_usec, j.cjournal, &usec, &boot_id) 868 j.mu.Unlock() 869 870 if r < 0 { 871 return 0, fmt.Errorf("failed to get monotonic timestamp: %s", syscall.Errno(-r).Error()) 872 } 873 874 return uint64(usec), nil 875} 876 877// GetCursor gets the cursor of the last journal entry reeferenced by the 878// last completed Next/Previous function call. To call GetCursor, you must 879// first have called one of the Next/Previous functions. 880func (j *Journal) GetCursor() (string, error) { 881 sd_journal_get_cursor, err := getFunction("sd_journal_get_cursor") 882 if err != nil { 883 return "", err 884 } 885 886 var d *C.char 887 // since the pointer is mutated by sd_journal_get_cursor, need to wait 888 // until after the call to free the memory 889 890 j.mu.Lock() 891 r := C.my_sd_journal_get_cursor(sd_journal_get_cursor, j.cjournal, &d) 892 j.mu.Unlock() 893 defer C.free(unsafe.Pointer(d)) 894 895 if r < 0 { 896 return "", fmt.Errorf("failed to get cursor: %s", syscall.Errno(-r).Error()) 897 } 898 899 cursor := C.GoString(d) 900 901 return cursor, nil 902} 903 904// TestCursor checks whether the current position in the journal matches the 905// specified cursor 906func (j *Journal) TestCursor(cursor string) error { 907 sd_journal_test_cursor, err := getFunction("sd_journal_test_cursor") 908 if err != nil { 909 return err 910 } 911 912 c := C.CString(cursor) 913 defer C.free(unsafe.Pointer(c)) 914 915 j.mu.Lock() 916 r := C.my_sd_journal_test_cursor(sd_journal_test_cursor, j.cjournal, c) 917 j.mu.Unlock() 918 919 if r < 0 { 920 return fmt.Errorf("failed to test to cursor %q: %s", cursor, syscall.Errno(-r).Error()) 921 } else if r == 0 { 922 return ErrNoTestCursor 923 } 924 925 return nil 926} 927 928// SeekHead seeks to the beginning of the journal, i.e. the oldest available 929// entry. This call must be followed by a call to Next before any call to 930// Get* will return data about the first element. 931func (j *Journal) SeekHead() error { 932 sd_journal_seek_head, err := getFunction("sd_journal_seek_head") 933 if err != nil { 934 return err 935 } 936 937 j.mu.Lock() 938 r := C.my_sd_journal_seek_head(sd_journal_seek_head, j.cjournal) 939 j.mu.Unlock() 940 941 if r < 0 { 942 return fmt.Errorf("failed to seek to head of journal: %s", syscall.Errno(-r).Error()) 943 } 944 945 return nil 946} 947 948// SeekTail may be used to seek to the end of the journal, i.e. the most recent 949// available entry. This call must be followed by a call to Previous before any 950// call to Get* will return data about the last element. 951func (j *Journal) SeekTail() error { 952 sd_journal_seek_tail, err := getFunction("sd_journal_seek_tail") 953 if err != nil { 954 return err 955 } 956 957 j.mu.Lock() 958 r := C.my_sd_journal_seek_tail(sd_journal_seek_tail, j.cjournal) 959 j.mu.Unlock() 960 961 if r < 0 { 962 return fmt.Errorf("failed to seek to tail of journal: %s", syscall.Errno(-r).Error()) 963 } 964 965 return nil 966} 967 968// SeekRealtimeUsec seeks to the entry with the specified realtime (wallclock) 969// timestamp, i.e. CLOCK_REALTIME. This call must be followed by a call to 970// Next/Previous before any call to Get* will return data about the sought entry. 971func (j *Journal) SeekRealtimeUsec(usec uint64) error { 972 sd_journal_seek_realtime_usec, err := getFunction("sd_journal_seek_realtime_usec") 973 if err != nil { 974 return err 975 } 976 977 j.mu.Lock() 978 r := C.my_sd_journal_seek_realtime_usec(sd_journal_seek_realtime_usec, j.cjournal, C.uint64_t(usec)) 979 j.mu.Unlock() 980 981 if r < 0 { 982 return fmt.Errorf("failed to seek to %d: %s", usec, syscall.Errno(-r).Error()) 983 } 984 985 return nil 986} 987 988// SeekCursor seeks to a concrete journal cursor. This call must be 989// followed by a call to Next/Previous before any call to Get* will return 990// data about the sought entry. 991func (j *Journal) SeekCursor(cursor string) error { 992 sd_journal_seek_cursor, err := getFunction("sd_journal_seek_cursor") 993 if err != nil { 994 return err 995 } 996 997 c := C.CString(cursor) 998 defer C.free(unsafe.Pointer(c)) 999 1000 j.mu.Lock() 1001 r := C.my_sd_journal_seek_cursor(sd_journal_seek_cursor, j.cjournal, c) 1002 j.mu.Unlock() 1003 1004 if r < 0 { 1005 return fmt.Errorf("failed to seek to cursor %q: %s", cursor, syscall.Errno(-r).Error()) 1006 } 1007 1008 return nil 1009} 1010 1011// Wait will synchronously wait until the journal gets changed. The maximum time 1012// this call sleeps may be controlled with the timeout parameter. If 1013// sdjournal.IndefiniteWait is passed as the timeout parameter, Wait will 1014// wait indefinitely for a journal change. 1015func (j *Journal) Wait(timeout time.Duration) int { 1016 var to uint64 1017 1018 sd_journal_wait, err := getFunction("sd_journal_wait") 1019 if err != nil { 1020 return -1 1021 } 1022 1023 if timeout == IndefiniteWait { 1024 // sd_journal_wait(3) calls for a (uint64_t) -1 to be passed to signify 1025 // indefinite wait, but using a -1 overflows our C.uint64_t, so we use an 1026 // equivalent hex value. 1027 to = 0xffffffffffffffff 1028 } else { 1029 to = uint64(timeout / time.Microsecond) 1030 } 1031 j.mu.Lock() 1032 r := C.my_sd_journal_wait(sd_journal_wait, j.cjournal, C.uint64_t(to)) 1033 j.mu.Unlock() 1034 1035 return int(r) 1036} 1037 1038// GetUsage returns the journal disk space usage, in bytes. 1039func (j *Journal) GetUsage() (uint64, error) { 1040 var out C.uint64_t 1041 1042 sd_journal_get_usage, err := getFunction("sd_journal_get_usage") 1043 if err != nil { 1044 return 0, err 1045 } 1046 1047 j.mu.Lock() 1048 r := C.my_sd_journal_get_usage(sd_journal_get_usage, j.cjournal, &out) 1049 j.mu.Unlock() 1050 1051 if r < 0 { 1052 return 0, fmt.Errorf("failed to get journal disk space usage: %s", syscall.Errno(-r).Error()) 1053 } 1054 1055 return uint64(out), nil 1056} 1057 1058// GetUniqueValues returns all unique values for a given field. 1059func (j *Journal) GetUniqueValues(field string) ([]string, error) { 1060 var result []string 1061 1062 sd_journal_query_unique, err := getFunction("sd_journal_query_unique") 1063 if err != nil { 1064 return nil, err 1065 } 1066 1067 sd_journal_enumerate_unique, err := getFunction("sd_journal_enumerate_unique") 1068 if err != nil { 1069 return nil, err 1070 } 1071 1072 sd_journal_restart_unique, err := getFunction("sd_journal_restart_unique") 1073 if err != nil { 1074 return nil, err 1075 } 1076 1077 j.mu.Lock() 1078 defer j.mu.Unlock() 1079 1080 f := C.CString(field) 1081 defer C.free(unsafe.Pointer(f)) 1082 1083 r := C.my_sd_journal_query_unique(sd_journal_query_unique, j.cjournal, f) 1084 1085 if r < 0 { 1086 return nil, fmt.Errorf("failed to query journal: %s", syscall.Errno(-r).Error()) 1087 } 1088 1089 // Implements the SD_JOURNAL_FOREACH_UNIQUE macro from sd-journal.h 1090 var d unsafe.Pointer 1091 var l C.size_t 1092 C.my_sd_journal_restart_unique(sd_journal_restart_unique, j.cjournal) 1093 for { 1094 r = C.my_sd_journal_enumerate_unique(sd_journal_enumerate_unique, j.cjournal, &d, &l) 1095 if r == 0 { 1096 break 1097 } 1098 1099 if r < 0 { 1100 return nil, fmt.Errorf("failed to read message field: %s", syscall.Errno(-r).Error()) 1101 } 1102 1103 msg := C.GoStringN((*C.char)(d), C.int(l)) 1104 kv := strings.SplitN(msg, "=", 2) 1105 if len(kv) < 2 { 1106 return nil, fmt.Errorf("failed to parse field") 1107 } 1108 1109 result = append(result, kv[1]) 1110 } 1111 1112 return result, nil 1113} 1114 1115// GetCatalog retrieves a message catalog entry for the journal entry referenced 1116// by the last completed Next/Previous function call. To call GetCatalog, you 1117// must first have called one of these functions. 1118func (j *Journal) GetCatalog() (string, error) { 1119 sd_journal_get_catalog, err := getFunction("sd_journal_get_catalog") 1120 if err != nil { 1121 return "", err 1122 } 1123 1124 var c *C.char 1125 1126 j.mu.Lock() 1127 r := C.my_sd_journal_get_catalog(sd_journal_get_catalog, j.cjournal, &c) 1128 j.mu.Unlock() 1129 defer C.free(unsafe.Pointer(c)) 1130 1131 if r < 0 { 1132 return "", fmt.Errorf("failed to retrieve catalog entry for current journal entry: %s", syscall.Errno(-r).Error()) 1133 } 1134 1135 catalog := C.GoString(c) 1136 1137 return catalog, nil 1138} 1139 1140// GetBootID get systemd boot id 1141func (j *Journal) GetBootID() (string, error) { 1142 sd_id128_get_boot, err := getFunction("sd_id128_get_boot") 1143 if err != nil { 1144 return "", err 1145 } 1146 1147 var boot_id C.sd_id128_t 1148 r := C.my_sd_id128_get_boot(sd_id128_get_boot, &boot_id) 1149 if r < 0 { 1150 return "", fmt.Errorf("failed to get boot id: %s", syscall.Errno(-r).Error()) 1151 } 1152 1153 sd_id128_to_string, err := getFunction("sd_id128_to_string") 1154 if err != nil { 1155 return "", err 1156 } 1157 1158 id128StringMax := C.size_t(C.SD_ID128_STRING_MAX) 1159 c := (*C.char)(C.malloc(id128StringMax)) 1160 defer C.free(unsafe.Pointer(c)) 1161 C.my_sd_id128_to_string(sd_id128_to_string, boot_id, c) 1162 1163 bootID := C.GoString(c) 1164 if len(bootID) <= 0 { 1165 return "", fmt.Errorf("get boot id %s is not valid", bootID) 1166 } 1167 1168 return bootID, nil 1169} 1170