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