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// 303import "C" 304import ( 305 "bytes" 306 "errors" 307 "fmt" 308 "strings" 309 "sync" 310 "syscall" 311 "time" 312 "unsafe" 313) 314 315// Journal entry field strings which correspond to: 316// http://www.freedesktop.org/software/systemd/man/systemd.journal-fields.html 317const ( 318 // User Journal Fields 319 SD_JOURNAL_FIELD_MESSAGE = "MESSAGE" 320 SD_JOURNAL_FIELD_MESSAGE_ID = "MESSAGE_ID" 321 SD_JOURNAL_FIELD_PRIORITY = "PRIORITY" 322 SD_JOURNAL_FIELD_CODE_FILE = "CODE_FILE" 323 SD_JOURNAL_FIELD_CODE_LINE = "CODE_LINE" 324 SD_JOURNAL_FIELD_CODE_FUNC = "CODE_FUNC" 325 SD_JOURNAL_FIELD_ERRNO = "ERRNO" 326 SD_JOURNAL_FIELD_SYSLOG_FACILITY = "SYSLOG_FACILITY" 327 SD_JOURNAL_FIELD_SYSLOG_IDENTIFIER = "SYSLOG_IDENTIFIER" 328 SD_JOURNAL_FIELD_SYSLOG_PID = "SYSLOG_PID" 329 330 // Trusted Journal Fields 331 SD_JOURNAL_FIELD_PID = "_PID" 332 SD_JOURNAL_FIELD_UID = "_UID" 333 SD_JOURNAL_FIELD_GID = "_GID" 334 SD_JOURNAL_FIELD_COMM = "_COMM" 335 SD_JOURNAL_FIELD_EXE = "_EXE" 336 SD_JOURNAL_FIELD_CMDLINE = "_CMDLINE" 337 SD_JOURNAL_FIELD_CAP_EFFECTIVE = "_CAP_EFFECTIVE" 338 SD_JOURNAL_FIELD_AUDIT_SESSION = "_AUDIT_SESSION" 339 SD_JOURNAL_FIELD_AUDIT_LOGINUID = "_AUDIT_LOGINUID" 340 SD_JOURNAL_FIELD_SYSTEMD_CGROUP = "_SYSTEMD_CGROUP" 341 SD_JOURNAL_FIELD_SYSTEMD_SESSION = "_SYSTEMD_SESSION" 342 SD_JOURNAL_FIELD_SYSTEMD_UNIT = "_SYSTEMD_UNIT" 343 SD_JOURNAL_FIELD_SYSTEMD_USER_UNIT = "_SYSTEMD_USER_UNIT" 344 SD_JOURNAL_FIELD_SYSTEMD_OWNER_UID = "_SYSTEMD_OWNER_UID" 345 SD_JOURNAL_FIELD_SYSTEMD_SLICE = "_SYSTEMD_SLICE" 346 SD_JOURNAL_FIELD_SELINUX_CONTEXT = "_SELINUX_CONTEXT" 347 SD_JOURNAL_FIELD_SOURCE_REALTIME_TIMESTAMP = "_SOURCE_REALTIME_TIMESTAMP" 348 SD_JOURNAL_FIELD_BOOT_ID = "_BOOT_ID" 349 SD_JOURNAL_FIELD_MACHINE_ID = "_MACHINE_ID" 350 SD_JOURNAL_FIELD_HOSTNAME = "_HOSTNAME" 351 SD_JOURNAL_FIELD_TRANSPORT = "_TRANSPORT" 352 353 // Address Fields 354 SD_JOURNAL_FIELD_CURSOR = "__CURSOR" 355 SD_JOURNAL_FIELD_REALTIME_TIMESTAMP = "__REALTIME_TIMESTAMP" 356 SD_JOURNAL_FIELD_MONOTONIC_TIMESTAMP = "__MONOTONIC_TIMESTAMP" 357) 358 359// Journal event constants 360const ( 361 SD_JOURNAL_NOP = int(C.SD_JOURNAL_NOP) 362 SD_JOURNAL_APPEND = int(C.SD_JOURNAL_APPEND) 363 SD_JOURNAL_INVALIDATE = int(C.SD_JOURNAL_INVALIDATE) 364) 365 366const ( 367 // IndefiniteWait is a sentinel value that can be passed to 368 // sdjournal.Wait() to signal an indefinite wait for new journal 369 // events. It is implemented as the maximum value for a time.Duration: 370 // https://github.com/golang/go/blob/e4dcf5c8c22d98ac9eac7b9b226596229624cb1d/src/time/time.go#L434 371 IndefiniteWait time.Duration = 1<<63 - 1 372) 373 374var ( 375 // ErrNoTestCursor gets returned when using TestCursor function and cursor 376 // parameter is not the same as the current cursor position. 377 ErrNoTestCursor = errors.New("Cursor parameter is not the same as current position") 378) 379 380// Journal is a Go wrapper of an sd_journal structure. 381type Journal struct { 382 cjournal *C.sd_journal 383 mu sync.Mutex 384} 385 386// JournalEntry represents all fields of a journal entry plus address fields. 387type JournalEntry struct { 388 Fields map[string]string 389 Cursor string 390 RealtimeTimestamp uint64 391 MonotonicTimestamp uint64 392} 393 394// Match is a convenience wrapper to describe filters supplied to AddMatch. 395type Match struct { 396 Field string 397 Value string 398} 399 400// String returns a string representation of a Match suitable for use with AddMatch. 401func (m *Match) String() string { 402 return m.Field + "=" + m.Value 403} 404 405// NewJournal returns a new Journal instance pointing to the local journal 406func NewJournal() (j *Journal, err error) { 407 j = &Journal{} 408 409 sd_journal_open, err := getFunction("sd_journal_open") 410 if err != nil { 411 return nil, err 412 } 413 414 r := C.my_sd_journal_open(sd_journal_open, &j.cjournal, C.SD_JOURNAL_LOCAL_ONLY) 415 416 if r < 0 { 417 return nil, fmt.Errorf("failed to open journal: %d", syscall.Errno(-r)) 418 } 419 420 return j, nil 421} 422 423// NewJournalFromDir returns a new Journal instance pointing to a journal residing 424// in a given directory. 425func NewJournalFromDir(path string) (j *Journal, err error) { 426 j = &Journal{} 427 428 sd_journal_open_directory, err := getFunction("sd_journal_open_directory") 429 if err != nil { 430 return nil, err 431 } 432 433 p := C.CString(path) 434 defer C.free(unsafe.Pointer(p)) 435 436 r := C.my_sd_journal_open_directory(sd_journal_open_directory, &j.cjournal, p, 0) 437 if r < 0 { 438 return nil, fmt.Errorf("failed to open journal in directory %q: %d", path, syscall.Errno(-r)) 439 } 440 441 return j, nil 442} 443 444// NewJournalFromFiles returns a new Journal instance pointing to a journals residing 445// in a given files. 446func NewJournalFromFiles(paths ...string) (j *Journal, err error) { 447 j = &Journal{} 448 449 sd_journal_open_files, err := getFunction("sd_journal_open_files") 450 if err != nil { 451 return nil, err 452 } 453 454 // by making the slice 1 elem too long, we guarantee it'll be null-terminated 455 cPaths := make([]*C.char, len(paths)+1) 456 for idx, path := range paths { 457 p := C.CString(path) 458 cPaths[idx] = p 459 defer C.free(unsafe.Pointer(p)) 460 } 461 462 r := C.my_sd_journal_open_files(sd_journal_open_files, &j.cjournal, &cPaths[0], 0) 463 if r < 0 { 464 return nil, fmt.Errorf("failed to open journals in paths %q: %d", paths, syscall.Errno(-r)) 465 } 466 467 return j, nil 468} 469 470// Close closes a journal opened with NewJournal. 471func (j *Journal) Close() error { 472 sd_journal_close, err := getFunction("sd_journal_close") 473 if err != nil { 474 return err 475 } 476 477 j.mu.Lock() 478 C.my_sd_journal_close(sd_journal_close, j.cjournal) 479 j.mu.Unlock() 480 481 return nil 482} 483 484// AddMatch adds a match by which to filter the entries of the journal. 485func (j *Journal) AddMatch(match string) error { 486 sd_journal_add_match, err := getFunction("sd_journal_add_match") 487 if err != nil { 488 return err 489 } 490 491 m := C.CString(match) 492 defer C.free(unsafe.Pointer(m)) 493 494 j.mu.Lock() 495 r := C.my_sd_journal_add_match(sd_journal_add_match, j.cjournal, unsafe.Pointer(m), C.size_t(len(match))) 496 j.mu.Unlock() 497 498 if r < 0 { 499 return fmt.Errorf("failed to add match: %d", syscall.Errno(-r)) 500 } 501 502 return nil 503} 504 505// AddDisjunction inserts a logical OR in the match list. 506func (j *Journal) AddDisjunction() error { 507 sd_journal_add_disjunction, err := getFunction("sd_journal_add_disjunction") 508 if err != nil { 509 return err 510 } 511 512 j.mu.Lock() 513 r := C.my_sd_journal_add_disjunction(sd_journal_add_disjunction, j.cjournal) 514 j.mu.Unlock() 515 516 if r < 0 { 517 return fmt.Errorf("failed to add a disjunction in the match list: %d", syscall.Errno(-r)) 518 } 519 520 return nil 521} 522 523// AddConjunction inserts a logical AND in the match list. 524func (j *Journal) AddConjunction() error { 525 sd_journal_add_conjunction, err := getFunction("sd_journal_add_conjunction") 526 if err != nil { 527 return err 528 } 529 530 j.mu.Lock() 531 r := C.my_sd_journal_add_conjunction(sd_journal_add_conjunction, j.cjournal) 532 j.mu.Unlock() 533 534 if r < 0 { 535 return fmt.Errorf("failed to add a conjunction in the match list: %d", syscall.Errno(-r)) 536 } 537 538 return nil 539} 540 541// FlushMatches flushes all matches, disjunctions and conjunctions. 542func (j *Journal) FlushMatches() { 543 sd_journal_flush_matches, err := getFunction("sd_journal_flush_matches") 544 if err != nil { 545 return 546 } 547 548 j.mu.Lock() 549 C.my_sd_journal_flush_matches(sd_journal_flush_matches, j.cjournal) 550 j.mu.Unlock() 551} 552 553// Next advances the read pointer into the journal by one entry. 554func (j *Journal) Next() (uint64, error) { 555 sd_journal_next, err := getFunction("sd_journal_next") 556 if err != nil { 557 return 0, err 558 } 559 560 j.mu.Lock() 561 r := C.my_sd_journal_next(sd_journal_next, j.cjournal) 562 j.mu.Unlock() 563 564 if r < 0 { 565 return 0, fmt.Errorf("failed to iterate journal: %d", syscall.Errno(-r)) 566 } 567 568 return uint64(r), nil 569} 570 571// NextSkip advances the read pointer by multiple entries at once, 572// as specified by the skip parameter. 573func (j *Journal) NextSkip(skip uint64) (uint64, error) { 574 sd_journal_next_skip, err := getFunction("sd_journal_next_skip") 575 if err != nil { 576 return 0, err 577 } 578 579 j.mu.Lock() 580 r := C.my_sd_journal_next_skip(sd_journal_next_skip, j.cjournal, C.uint64_t(skip)) 581 j.mu.Unlock() 582 583 if r < 0 { 584 return 0, fmt.Errorf("failed to iterate journal: %d", syscall.Errno(-r)) 585 } 586 587 return uint64(r), nil 588} 589 590// Previous sets the read pointer into the journal back by one entry. 591func (j *Journal) Previous() (uint64, error) { 592 sd_journal_previous, err := getFunction("sd_journal_previous") 593 if err != nil { 594 return 0, err 595 } 596 597 j.mu.Lock() 598 r := C.my_sd_journal_previous(sd_journal_previous, j.cjournal) 599 j.mu.Unlock() 600 601 if r < 0 { 602 return 0, fmt.Errorf("failed to iterate journal: %d", syscall.Errno(-r)) 603 } 604 605 return uint64(r), nil 606} 607 608// PreviousSkip sets back the read pointer by multiple entries at once, 609// as specified by the skip parameter. 610func (j *Journal) PreviousSkip(skip uint64) (uint64, error) { 611 sd_journal_previous_skip, err := getFunction("sd_journal_previous_skip") 612 if err != nil { 613 return 0, err 614 } 615 616 j.mu.Lock() 617 r := C.my_sd_journal_previous_skip(sd_journal_previous_skip, j.cjournal, C.uint64_t(skip)) 618 j.mu.Unlock() 619 620 if r < 0 { 621 return 0, fmt.Errorf("failed to iterate journal: %d", syscall.Errno(-r)) 622 } 623 624 return uint64(r), nil 625} 626 627func (j *Journal) getData(field string) (unsafe.Pointer, C.int, error) { 628 sd_journal_get_data, err := getFunction("sd_journal_get_data") 629 if err != nil { 630 return nil, 0, err 631 } 632 633 f := C.CString(field) 634 defer C.free(unsafe.Pointer(f)) 635 636 var d unsafe.Pointer 637 var l C.size_t 638 639 j.mu.Lock() 640 r := C.my_sd_journal_get_data(sd_journal_get_data, j.cjournal, f, &d, &l) 641 j.mu.Unlock() 642 643 if r < 0 { 644 return nil, 0, fmt.Errorf("failed to read message: %d", syscall.Errno(-r)) 645 } 646 647 return d, C.int(l), nil 648} 649 650// GetData gets the data object associated with a specific field from the 651// the journal entry referenced by the last completed Next/Previous function 652// call. To call GetData, you must have first called one of these functions. 653func (j *Journal) GetData(field string) (string, error) { 654 d, l, err := j.getData(field) 655 if err != nil { 656 return "", err 657 } 658 659 return C.GoStringN((*C.char)(d), l), nil 660} 661 662// GetDataValue gets the data object associated with a specific field from the 663// journal entry referenced by the last completed Next/Previous function call, 664// returning only the value of the object. To call GetDataValue, you must first 665// have called one of the Next/Previous functions. 666func (j *Journal) GetDataValue(field string) (string, error) { 667 val, err := j.GetData(field) 668 if err != nil { 669 return "", err 670 } 671 672 return strings.SplitN(val, "=", 2)[1], nil 673} 674 675// GetDataBytes gets the data object associated with a specific field from the 676// journal entry referenced by the last completed Next/Previous function call. 677// To call GetDataBytes, you must first have called one of these functions. 678func (j *Journal) GetDataBytes(field string) ([]byte, error) { 679 d, l, err := j.getData(field) 680 if err != nil { 681 return nil, err 682 } 683 684 return C.GoBytes(d, l), nil 685} 686 687// GetDataValueBytes gets the data object associated with a specific field from the 688// journal entry referenced by the last completed Next/Previous function call, 689// returning only the value of the object. To call GetDataValueBytes, you must first 690// have called one of the Next/Previous functions. 691func (j *Journal) GetDataValueBytes(field string) ([]byte, error) { 692 val, err := j.GetDataBytes(field) 693 if err != nil { 694 return nil, err 695 } 696 697 return bytes.SplitN(val, []byte("="), 2)[1], nil 698} 699 700// GetEntry returns a full representation of the journal entry referenced by the 701// last completed Next/Previous function call, with all key-value pairs of data 702// as well as address fields (cursor, realtime timestamp and monotonic timestamp). 703// To call GetEntry, you must first have called one of the Next/Previous functions. 704func (j *Journal) GetEntry() (*JournalEntry, error) { 705 sd_journal_get_realtime_usec, err := getFunction("sd_journal_get_realtime_usec") 706 if err != nil { 707 return nil, err 708 } 709 710 sd_journal_get_monotonic_usec, err := getFunction("sd_journal_get_monotonic_usec") 711 if err != nil { 712 return nil, err 713 } 714 715 sd_journal_get_cursor, err := getFunction("sd_journal_get_cursor") 716 if err != nil { 717 return nil, err 718 } 719 720 sd_journal_restart_data, err := getFunction("sd_journal_restart_data") 721 if err != nil { 722 return nil, err 723 } 724 725 sd_journal_enumerate_data, err := getFunction("sd_journal_enumerate_data") 726 if err != nil { 727 return nil, err 728 } 729 730 j.mu.Lock() 731 defer j.mu.Unlock() 732 733 var r C.int 734 entry := &JournalEntry{Fields: make(map[string]string)} 735 736 var realtimeUsec C.uint64_t 737 r = C.my_sd_journal_get_realtime_usec(sd_journal_get_realtime_usec, j.cjournal, &realtimeUsec) 738 if r < 0 { 739 return nil, fmt.Errorf("failed to get realtime timestamp: %d", syscall.Errno(-r)) 740 } 741 742 entry.RealtimeTimestamp = uint64(realtimeUsec) 743 744 var monotonicUsec C.uint64_t 745 var boot_id C.sd_id128_t 746 747 r = C.my_sd_journal_get_monotonic_usec(sd_journal_get_monotonic_usec, j.cjournal, &monotonicUsec, &boot_id) 748 if r < 0 { 749 return nil, fmt.Errorf("failed to get monotonic timestamp: %d", syscall.Errno(-r)) 750 } 751 752 entry.MonotonicTimestamp = uint64(monotonicUsec) 753 754 var c *C.char 755 // since the pointer is mutated by sd_journal_get_cursor, need to wait 756 // until after the call to free the memory 757 r = C.my_sd_journal_get_cursor(sd_journal_get_cursor, j.cjournal, &c) 758 defer C.free(unsafe.Pointer(c)) 759 if r < 0 { 760 return nil, fmt.Errorf("failed to get cursor: %d", syscall.Errno(-r)) 761 } 762 763 entry.Cursor = C.GoString(c) 764 765 // Implements the JOURNAL_FOREACH_DATA_RETVAL macro from journal-internal.h 766 var d unsafe.Pointer 767 var l C.size_t 768 C.my_sd_journal_restart_data(sd_journal_restart_data, j.cjournal) 769 for { 770 r = C.my_sd_journal_enumerate_data(sd_journal_enumerate_data, j.cjournal, &d, &l) 771 if r == 0 { 772 break 773 } 774 775 if r < 0 { 776 return nil, fmt.Errorf("failed to read message field: %d", syscall.Errno(-r)) 777 } 778 779 msg := C.GoStringN((*C.char)(d), C.int(l)) 780 kv := strings.SplitN(msg, "=", 2) 781 if len(kv) < 2 { 782 return nil, fmt.Errorf("failed to parse field") 783 } 784 785 entry.Fields[kv[0]] = kv[1] 786 } 787 788 return entry, nil 789} 790 791// SetDataThreshold sets the data field size threshold for data returned by 792// GetData. To retrieve the complete data fields this threshold should be 793// turned off by setting it to 0, so that the library always returns the 794// complete data objects. 795func (j *Journal) SetDataThreshold(threshold uint64) error { 796 sd_journal_set_data_threshold, err := getFunction("sd_journal_set_data_threshold") 797 if err != nil { 798 return err 799 } 800 801 j.mu.Lock() 802 r := C.my_sd_journal_set_data_threshold(sd_journal_set_data_threshold, j.cjournal, C.size_t(threshold)) 803 j.mu.Unlock() 804 805 if r < 0 { 806 return fmt.Errorf("failed to set data threshold: %d", syscall.Errno(-r)) 807 } 808 809 return nil 810} 811 812// GetRealtimeUsec gets the realtime (wallclock) timestamp of the journal 813// entry referenced by the last completed Next/Previous function call. To 814// call GetRealtimeUsec, you must first have called one of the Next/Previous 815// functions. 816func (j *Journal) GetRealtimeUsec() (uint64, error) { 817 var usec C.uint64_t 818 819 sd_journal_get_realtime_usec, err := getFunction("sd_journal_get_realtime_usec") 820 if err != nil { 821 return 0, err 822 } 823 824 j.mu.Lock() 825 r := C.my_sd_journal_get_realtime_usec(sd_journal_get_realtime_usec, j.cjournal, &usec) 826 j.mu.Unlock() 827 828 if r < 0 { 829 return 0, fmt.Errorf("failed to get realtime timestamp: %d", syscall.Errno(-r)) 830 } 831 832 return uint64(usec), nil 833} 834 835// GetMonotonicUsec gets the monotonic timestamp of the journal entry 836// referenced by the last completed Next/Previous function call. To call 837// GetMonotonicUsec, you must first have called one of the Next/Previous 838// functions. 839func (j *Journal) GetMonotonicUsec() (uint64, error) { 840 var usec C.uint64_t 841 var boot_id C.sd_id128_t 842 843 sd_journal_get_monotonic_usec, err := getFunction("sd_journal_get_monotonic_usec") 844 if err != nil { 845 return 0, err 846 } 847 848 j.mu.Lock() 849 r := C.my_sd_journal_get_monotonic_usec(sd_journal_get_monotonic_usec, j.cjournal, &usec, &boot_id) 850 j.mu.Unlock() 851 852 if r < 0 { 853 return 0, fmt.Errorf("failed to get monotonic timestamp: %d", syscall.Errno(-r)) 854 } 855 856 return uint64(usec), nil 857} 858 859// GetCursor gets the cursor of the last journal entry reeferenced by the 860// last completed Next/Previous function call. To call GetCursor, you must 861// first have called one of the Next/Previous functions. 862func (j *Journal) GetCursor() (string, error) { 863 sd_journal_get_cursor, err := getFunction("sd_journal_get_cursor") 864 if err != nil { 865 return "", err 866 } 867 868 var d *C.char 869 // since the pointer is mutated by sd_journal_get_cursor, need to wait 870 // until after the call to free the memory 871 872 j.mu.Lock() 873 r := C.my_sd_journal_get_cursor(sd_journal_get_cursor, j.cjournal, &d) 874 j.mu.Unlock() 875 defer C.free(unsafe.Pointer(d)) 876 877 if r < 0 { 878 return "", fmt.Errorf("failed to get cursor: %d", syscall.Errno(-r)) 879 } 880 881 cursor := C.GoString(d) 882 883 return cursor, nil 884} 885 886// TestCursor checks whether the current position in the journal matches the 887// specified cursor 888func (j *Journal) TestCursor(cursor string) error { 889 sd_journal_test_cursor, err := getFunction("sd_journal_test_cursor") 890 if err != nil { 891 return err 892 } 893 894 c := C.CString(cursor) 895 defer C.free(unsafe.Pointer(c)) 896 897 j.mu.Lock() 898 r := C.my_sd_journal_test_cursor(sd_journal_test_cursor, j.cjournal, c) 899 j.mu.Unlock() 900 901 if r < 0 { 902 return fmt.Errorf("failed to test to cursor %q: %d", cursor, syscall.Errno(-r)) 903 } else if r == 0 { 904 return ErrNoTestCursor 905 } 906 907 return nil 908} 909 910// SeekHead seeks to the beginning of the journal, i.e. the oldest available 911// entry. This call must be followed by a call to Next before any call to 912// Get* will return data about the first element. 913func (j *Journal) SeekHead() error { 914 sd_journal_seek_head, err := getFunction("sd_journal_seek_head") 915 if err != nil { 916 return err 917 } 918 919 j.mu.Lock() 920 r := C.my_sd_journal_seek_head(sd_journal_seek_head, j.cjournal) 921 j.mu.Unlock() 922 923 if r < 0 { 924 return fmt.Errorf("failed to seek to head of journal: %d", syscall.Errno(-r)) 925 } 926 927 return nil 928} 929 930// SeekTail may be used to seek to the end of the journal, i.e. the most recent 931// available entry. This call must be followed by a call to Next before any 932// call to Get* will return data about the last element. 933func (j *Journal) SeekTail() error { 934 sd_journal_seek_tail, err := getFunction("sd_journal_seek_tail") 935 if err != nil { 936 return err 937 } 938 939 j.mu.Lock() 940 r := C.my_sd_journal_seek_tail(sd_journal_seek_tail, j.cjournal) 941 j.mu.Unlock() 942 943 if r < 0 { 944 return fmt.Errorf("failed to seek to tail of journal: %d", syscall.Errno(-r)) 945 } 946 947 return nil 948} 949 950// SeekRealtimeUsec seeks to the entry with the specified realtime (wallclock) 951// timestamp, i.e. CLOCK_REALTIME. This call must be followed by a call to 952// Next/Previous before any call to Get* will return data about the sought entry. 953func (j *Journal) SeekRealtimeUsec(usec uint64) error { 954 sd_journal_seek_realtime_usec, err := getFunction("sd_journal_seek_realtime_usec") 955 if err != nil { 956 return err 957 } 958 959 j.mu.Lock() 960 r := C.my_sd_journal_seek_realtime_usec(sd_journal_seek_realtime_usec, j.cjournal, C.uint64_t(usec)) 961 j.mu.Unlock() 962 963 if r < 0 { 964 return fmt.Errorf("failed to seek to %d: %d", usec, syscall.Errno(-r)) 965 } 966 967 return nil 968} 969 970// SeekCursor seeks to a concrete journal cursor. This call must be 971// followed by a call to Next/Previous before any call to Get* will return 972// data about the sought entry. 973func (j *Journal) SeekCursor(cursor string) error { 974 sd_journal_seek_cursor, err := getFunction("sd_journal_seek_cursor") 975 if err != nil { 976 return err 977 } 978 979 c := C.CString(cursor) 980 defer C.free(unsafe.Pointer(c)) 981 982 j.mu.Lock() 983 r := C.my_sd_journal_seek_cursor(sd_journal_seek_cursor, j.cjournal, c) 984 j.mu.Unlock() 985 986 if r < 0 { 987 return fmt.Errorf("failed to seek to cursor %q: %d", cursor, syscall.Errno(-r)) 988 } 989 990 return nil 991} 992 993// Wait will synchronously wait until the journal gets changed. The maximum time 994// this call sleeps may be controlled with the timeout parameter. If 995// sdjournal.IndefiniteWait is passed as the timeout parameter, Wait will 996// wait indefinitely for a journal change. 997func (j *Journal) Wait(timeout time.Duration) int { 998 var to uint64 999 1000 sd_journal_wait, err := getFunction("sd_journal_wait") 1001 if err != nil { 1002 return -1 1003 } 1004 1005 if timeout == IndefiniteWait { 1006 // sd_journal_wait(3) calls for a (uint64_t) -1 to be passed to signify 1007 // indefinite wait, but using a -1 overflows our C.uint64_t, so we use an 1008 // equivalent hex value. 1009 to = 0xffffffffffffffff 1010 } else { 1011 to = uint64(timeout / time.Microsecond) 1012 } 1013 j.mu.Lock() 1014 r := C.my_sd_journal_wait(sd_journal_wait, j.cjournal, C.uint64_t(to)) 1015 j.mu.Unlock() 1016 1017 return int(r) 1018} 1019 1020// GetUsage returns the journal disk space usage, in bytes. 1021func (j *Journal) GetUsage() (uint64, error) { 1022 var out C.uint64_t 1023 1024 sd_journal_get_usage, err := getFunction("sd_journal_get_usage") 1025 if err != nil { 1026 return 0, err 1027 } 1028 1029 j.mu.Lock() 1030 r := C.my_sd_journal_get_usage(sd_journal_get_usage, j.cjournal, &out) 1031 j.mu.Unlock() 1032 1033 if r < 0 { 1034 return 0, fmt.Errorf("failed to get journal disk space usage: %d", syscall.Errno(-r)) 1035 } 1036 1037 return uint64(out), nil 1038} 1039 1040// GetUniqueValues returns all unique values for a given field. 1041func (j *Journal) GetUniqueValues(field string) ([]string, error) { 1042 var result []string 1043 1044 sd_journal_query_unique, err := getFunction("sd_journal_query_unique") 1045 if err != nil { 1046 return nil, err 1047 } 1048 1049 sd_journal_enumerate_unique, err := getFunction("sd_journal_enumerate_unique") 1050 if err != nil { 1051 return nil, err 1052 } 1053 1054 sd_journal_restart_unique, err := getFunction("sd_journal_restart_unique") 1055 if err != nil { 1056 return nil, err 1057 } 1058 1059 j.mu.Lock() 1060 defer j.mu.Unlock() 1061 1062 f := C.CString(field) 1063 defer C.free(unsafe.Pointer(f)) 1064 1065 r := C.my_sd_journal_query_unique(sd_journal_query_unique, j.cjournal, f) 1066 1067 if r < 0 { 1068 return nil, fmt.Errorf("failed to query journal: %d", syscall.Errno(-r)) 1069 } 1070 1071 // Implements the SD_JOURNAL_FOREACH_UNIQUE macro from sd-journal.h 1072 var d unsafe.Pointer 1073 var l C.size_t 1074 C.my_sd_journal_restart_unique(sd_journal_restart_unique, j.cjournal) 1075 for { 1076 r = C.my_sd_journal_enumerate_unique(sd_journal_enumerate_unique, j.cjournal, &d, &l) 1077 if r == 0 { 1078 break 1079 } 1080 1081 if r < 0 { 1082 return nil, fmt.Errorf("failed to read message field: %d", syscall.Errno(-r)) 1083 } 1084 1085 msg := C.GoStringN((*C.char)(d), C.int(l)) 1086 kv := strings.SplitN(msg, "=", 2) 1087 if len(kv) < 2 { 1088 return nil, fmt.Errorf("failed to parse field") 1089 } 1090 1091 result = append(result, kv[1]) 1092 } 1093 1094 return result, nil 1095} 1096 1097// GetCatalog retrieves a message catalog entry for the journal entry referenced 1098// by the last completed Next/Previous function call. To call GetCatalog, you 1099// must first have called one of these functions. 1100func (j *Journal) GetCatalog() (string, error) { 1101 sd_journal_get_catalog, err := getFunction("sd_journal_get_catalog") 1102 if err != nil { 1103 return "", err 1104 } 1105 1106 var c *C.char 1107 1108 j.mu.Lock() 1109 r := C.my_sd_journal_get_catalog(sd_journal_get_catalog, j.cjournal, &c) 1110 j.mu.Unlock() 1111 defer C.free(unsafe.Pointer(c)) 1112 1113 if r < 0 { 1114 return "", fmt.Errorf("failed to retrieve catalog entry for current journal entry: %d", syscall.Errno(-r)) 1115 } 1116 1117 catalog := C.GoString(c) 1118 1119 return catalog, nil 1120} 1121