1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25 package sdjournal
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321 import "C"
322 import (
323 "bytes"
324 "errors"
325 "fmt"
326 "strings"
327 "sync"
328 "syscall"
329 "time"
330 "unsafe"
331 )
332
333
334
335 const (
336
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
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
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
378 const (
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
384 const (
385
386
387
388
389 IndefiniteWait time.Duration = 1<<63 - 1
390 )
391
392 var (
393
394
395 ErrNoTestCursor = errors.New("Cursor parameter is not the same as current position")
396 )
397
398
399 type Journal struct {
400 cjournal *C.sd_journal
401 mu sync.Mutex
402 }
403
404
405 type JournalEntry struct {
406 Fields map[string]string
407 Cursor string
408 RealtimeTimestamp uint64
409 MonotonicTimestamp uint64
410 }
411
412
413 type Match struct {
414 Field string
415 Value string
416 }
417
418
419 func (m *Match) String() string {
420 return m.Field + "=" + m.Value
421 }
422
423
424 func 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
442
443 func 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
463
464 func 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
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
489 func (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
503 func (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
524 func (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
542 func (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
560 func (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
572 func (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
590
591 func (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
609 func (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
627
628 func (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
645 func (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
669
670
671 func (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
681
682
683
684 func (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
694
695
696 func (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
706
707
708
709 func (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
719
720
721
722 func (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
774
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
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
810
811
812
813 func (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
831
832
833
834 func (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
854
855
856
857 func (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
878
879
880 func (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
888
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
905
906 func (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
929
930
931 func (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
949
950
951 func (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
969
970
971 func (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
989
990
991 func (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
1012
1013
1014
1015 func (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
1025
1026
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
1039 func (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
1059 func (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
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
1116
1117
1118 func (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
1141 func (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
View as plain text