Skip to content

Commit ce21643

Browse files
committed
sdjournal: add support for cursors (get&seek&test)
1 parent 1d9051f commit ce21643

File tree

3 files changed

+161
-0
lines changed

3 files changed

+161
-0
lines changed

sdjournal/journal.go

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -155,6 +155,24 @@ package sdjournal
155155
// }
156156
//
157157
// int
158+
// my_sd_journal_get_cursor(void *f, sd_journal *j, char **cursor)
159+
// {
160+
// int (*sd_journal_get_cursor)(sd_journal *, char **);
161+
//
162+
// sd_journal_get_cursor = f;
163+
// return sd_journal_get_cursor(j, cursor);
164+
// }
165+
//
166+
// int
167+
// my_sd_journal_test_cursor(void *f, sd_journal *j, const char *cursor)
168+
// {
169+
// int (*sd_journal_test_cursor)(sd_journal *, const char *);
170+
//
171+
// sd_journal_test_cursor = f;
172+
// return sd_journal_test_cursor(j, cursor);
173+
// }
174+
//
175+
// int
158176
// my_sd_journal_get_realtime_usec(void *f, sd_journal *j, uint64_t *usec)
159177
// {
160178
// int (*sd_journal_get_realtime_usec)(sd_journal *, uint64_t *);
@@ -181,6 +199,16 @@ package sdjournal
181199
// return sd_journal_seek_tail(j);
182200
// }
183201
//
202+
//
203+
// int
204+
// my_sd_journal_seek_cursor(void *f, sd_journal *j, const char *cursor)
205+
// {
206+
// int (*sd_journal_seek_cursor)(sd_journal *, const char *);
207+
//
208+
// sd_journal_seek_cursor = f;
209+
// return sd_journal_seek_cursor(j, cursor);
210+
// }
211+
//
184212
// int
185213
// my_sd_journal_seek_realtime_usec(void *f, sd_journal *j, uint64_t usec)
186214
// {
@@ -596,6 +624,50 @@ func (j *Journal) GetRealtimeUsec() (uint64, error) {
596624
return uint64(usec), nil
597625
}
598626

627+
// GetCursor gets the cursor of the current journal entry.
628+
func (j *Journal) GetCursor() (string, error) {
629+
var d *C.char
630+
631+
sd_journal_get_cursor, err := j.getFunction("sd_journal_get_cursor")
632+
if err != nil {
633+
return "", err
634+
}
635+
636+
j.mu.Lock()
637+
r := C.my_sd_journal_get_cursor(sd_journal_get_cursor, j.cjournal, &d)
638+
j.mu.Unlock()
639+
640+
if r < 0 {
641+
return "", fmt.Errorf("failed to get cursor: %d", syscall.Errno(-r))
642+
}
643+
644+
cursor := C.GoString(d)
645+
646+
return cursor, nil
647+
}
648+
649+
// TestCursor checks whether the current position in the journal matches the
650+
// specified cursor
651+
func (j *Journal) TestCursor(cursor string) error {
652+
sd_journal_test_cursor, err := j.getFunction("sd_journal_test_cursor")
653+
if err != nil {
654+
return err
655+
}
656+
657+
c := C.CString(cursor)
658+
defer C.free(unsafe.Pointer(c))
659+
660+
j.mu.Lock()
661+
r := C.my_sd_journal_test_cursor(sd_journal_test_cursor, j.cjournal, c)
662+
j.mu.Unlock()
663+
664+
if r < 0 {
665+
return fmt.Errorf("failed to test to cursor %q: %d", cursor, syscall.Errno(-r))
666+
}
667+
668+
return nil
669+
}
670+
599671
// SeekHead seeks to the beginning of the journal, i.e. the oldest available
600672
// entry.
601673
func (j *Journal) SeekHead() error {
@@ -653,6 +725,27 @@ func (j *Journal) SeekRealtimeUsec(usec uint64) error {
653725
return nil
654726
}
655727

728+
// SeekCursor seeks to a concrete journal cursor.
729+
func (j *Journal) SeekCursor(cursor string) error {
730+
sd_journal_seek_cursor, err := j.getFunction("sd_journal_seek_cursor")
731+
if err != nil {
732+
return err
733+
}
734+
735+
c := C.CString(cursor)
736+
defer C.free(unsafe.Pointer(c))
737+
738+
j.mu.Lock()
739+
r := C.my_sd_journal_seek_cursor(sd_journal_seek_cursor, j.cjournal, c)
740+
j.mu.Unlock()
741+
742+
if r < 0 {
743+
return fmt.Errorf("failed to seek to cursor %q: %d", cursor, syscall.Errno(-r))
744+
}
745+
746+
return nil
747+
}
748+
656749
// Wait will synchronously wait until the journal gets changed. The maximum time
657750
// this call sleeps may be controlled with the timeout parameter. If
658751
// sdjournal.IndefiniteWait is passed as the timeout parameter, Wait will

sdjournal/journal_test.go

100644100755
Lines changed: 62 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,9 @@
1616
package sdjournal
1717

1818
import (
19+
"errors"
20+
"fmt"
21+
"io"
1922
"os"
2023
"testing"
2124
"time"
@@ -88,3 +91,62 @@ func TestJournalGetUsage(t *testing.T) {
8891
t.Fatalf("Error getting journal size: %s", err)
8992
}
9093
}
94+
95+
func TestJournalCursorGetSeekAndTest(t *testing.T) {
96+
j, err := NewJournal()
97+
if err != nil {
98+
t.Fatalf("Error opening journal: %s", err)
99+
}
100+
101+
if j == nil {
102+
t.Fatal("Got a nil journal")
103+
}
104+
105+
defer j.Close()
106+
107+
waitAndNext := func(j *Journal) error {
108+
r := j.Wait(time.Duration(1) * time.Second)
109+
if r < 0 {
110+
return errors.New("Error waiting to journal")
111+
}
112+
113+
n, err := j.Next()
114+
if err != nil {
115+
return fmt.Errorf("Error reading to journal: %s", err)
116+
}
117+
118+
if n == 0 {
119+
return fmt.Errorf("Error reading to journal: %s", io.EOF)
120+
}
121+
122+
return nil
123+
}
124+
125+
err = journal.Print(journal.PriInfo, "test message for cursor %s", time.Now())
126+
if err != nil {
127+
t.Fatalf("Error writing to journal: %s", err)
128+
}
129+
130+
if err = waitAndNext(j); err != nil {
131+
t.Fatalf(err.Error())
132+
}
133+
134+
c, err := j.GetCursor()
135+
if err != nil {
136+
t.Fatalf("Error getting cursor from journal: %s", err)
137+
}
138+
139+
err = j.SeekCursor(c)
140+
if err != nil {
141+
t.Fatalf("Error seeking cursor to journal: %s", err)
142+
}
143+
144+
if err = waitAndNext(j); err != nil {
145+
t.Fatalf(err.Error())
146+
}
147+
148+
err = j.TestCursor(c)
149+
if err != nil {
150+
t.Fatalf("Error testing cursor to journal: %s", err)
151+
}
152+
}

sdjournal/read.go

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -33,6 +33,7 @@ type JournalReaderConfig struct {
3333
// where the reading begins within the journal.
3434
Since time.Duration // start relative to a Duration from now
3535
NumFromTail uint64 // start relative to the tail
36+
Cursor string // start relative to the cursor
3637

3738
// Show only journal entries whose fields match the supplied values. If
3839
// the array is empty, entries will not be filtered.
@@ -89,6 +90,11 @@ func NewJournalReader(config JournalReaderConfig) (*JournalReader, error) {
8990
if _, err := r.journal.PreviousSkip(config.NumFromTail + 1); err != nil {
9091
return nil, err
9192
}
93+
} else if config.Cursor != "" {
94+
// Start based on a custom cursor
95+
if err := r.journal.SeekCursor(config.Cursor); err != nil {
96+
return nil, err
97+
}
9298
}
9399

94100
return r, nil

0 commit comments

Comments
 (0)