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