chiark / gitweb /
3b1003ba71f99e5a621ac622b7ab7a370947d607
[elogind.git] / src / python-systemd / _reader.c
1 /*-*- Mode: C; c-basic-offset: 4; indent-tabs-mode: nil -*-*/
2
3 /***
4   This file is part of systemd.
5
6   Copyright 2013 Steven Hiscocks, Zbigniew JÄ™drzejewski-Szmek
7
8   systemd is free software; you can redistribute it and/or modify it
9   under the terms of the GNU Lesser General Public License as published by
10   the Free Software Foundation; either version 2.1 of the License, or
11   (at your option) any later version.
12
13   systemd is distributed in the hope that it will be useful, but
14   WITHOUT ANY WARRANTY; without even the implied warranty of
15   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
16   Lesser General Public License for more details.
17
18   You should have received a copy of the GNU Lesser General Public License
19   along with systemd; If not, see <http://www.gnu.org/licenses/>.
20 ***/
21
22 #include <Python.h>
23 #include <structmember.h>
24 #include <datetime.h>
25 #include <time.h>
26 #include <stdio.h>
27
28 #include <systemd/sd-journal.h>
29
30 #include "pyutil.h"
31 #include "macro.h"
32 #include "util.h"
33 #include "strv.h"
34 #include "build.h"
35
36 typedef struct {
37     PyObject_HEAD
38     sd_journal *j;
39 } Reader;
40 static PyTypeObject ReaderType;
41
42
43 PyDoc_STRVAR(module__doc__,
44              "Class to reads the systemd journal similar to journalctl.");
45
46
47 #if PY_MAJOR_VERSION >= 3
48 static PyTypeObject MonotonicType;
49
50 PyDoc_STRVAR(MonotonicType__doc__,
51              "A tuple of (timestamp, bootid) for holding monotonic timestamps");
52
53 static PyStructSequence_Field MonotonicType_fields[] = {
54     {(char*) "timestamp", (char*) "Time"},
55     {(char*) "bootid", (char*) "Unique identifier of the boot"},
56     {} /* Sentinel */
57 };
58
59 static PyStructSequence_Desc Monotonic_desc = {
60     (char*) "journal.Monotonic",
61     MonotonicType__doc__,
62     MonotonicType_fields,
63     2,
64 };
65 #endif
66
67 /**
68  * Convert a Python sequence object into a strv (char**), and
69  * None into a NULL pointer.
70  */
71 static int strv_converter(PyObject* obj, void *_result) {
72         char ***result = _result;
73         Py_ssize_t i, len;
74
75         assert(result);
76
77         if (!obj)
78             goto cleanup;
79
80         if (obj == Py_None) {
81             *result = NULL;
82             return 1;
83         }
84
85         if (!PySequence_Check(obj))
86             return 0;
87
88         len = PySequence_Length(obj);
89         *result = new0(char*, len + 1);
90
91         for (i = 0; i < len; i++) {
92             PyObject *item;
93 #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
94             int r;
95             PyObject *bytes;
96 #endif
97             char *s, *s2;
98
99             item = PySequence_ITEM(obj, i);
100 #if PY_MAJOR_VERSION >=3 && PY_MINOR_VERSION >= 1
101             r = PyUnicode_FSConverter(item, &bytes);
102             if (r == 0)
103                 goto cleanup;
104
105             s = PyBytes_AsString(bytes);
106 #else
107             s = PyString_AsString(item);
108 #endif
109             if (!s)
110                 goto cleanup;
111
112             s2 = strdup(s);
113             if (!s2)
114                 log_oom();
115
116             (*result)[i] = s2;
117         }
118
119         return 1;
120
121 cleanup:
122         strv_free(*result);
123         *result = NULL;
124
125         return 0;
126 }
127
128 static void Reader_dealloc(Reader* self)
129 {
130     sd_journal_close(self->j);
131     Py_TYPE(self)->tp_free((PyObject*)self);
132 }
133
134 PyDoc_STRVAR(Reader__doc__,
135              "_Reader([flags | path | files]) -> ...\n\n"
136              "_Reader allows filtering and retrieval of Journal entries.\n"
137              "Note: this is a low-level interface, and probably not what you\n"
138              "want, use systemd.journal.Reader instead.\n\n"
139              "Argument `flags` sets open flags of the journal, which can be one\n"
140              "of, or ORed combination of constants: LOCAL_ONLY (default) opens\n"
141              "journal on local machine only; RUNTIME_ONLY opens only\n"
142              "volatile journal files; and SYSTEM opens journal files of\n"
143              "system services and the kernel, and CURRENT_USER opens files\n"
144              "of the current user.\n\n"
145              "Argument `path` is the directory of journal files.\n"
146              "Argument `files` is a list of files. Note that\n"
147              "`flags`, `path`, and `files` are exclusive.\n\n"
148              "_Reader implements the context manager protocol: the journal\n"
149              "will be closed when exiting the block.");
150 static int Reader_init(Reader *self, PyObject *args, PyObject *keywds)
151 {
152     int flags = 0, r;
153     char *path = NULL;
154     char **files = NULL;
155
156     static const char* const kwlist[] = {"flags", "path", "files", NULL};
157     if (!PyArg_ParseTupleAndKeywords(args, keywds, "|izO&", (char**) kwlist,
158                                      &flags, &path, strv_converter, &files))
159         return -1;
160
161     if (!!flags + !!path + !!files > 1) {
162         PyErr_SetString(PyExc_ValueError, "cannot use more than one of flags, path, and files");
163         return -1;
164     }
165
166     if (!flags)
167         flags = SD_JOURNAL_LOCAL_ONLY;
168
169     Py_BEGIN_ALLOW_THREADS
170     if (path)
171         r = sd_journal_open_directory(&self->j, path, 0);
172     else if (files)
173         r = sd_journal_open_files(&self->j, (const char**) files, 0);
174     else
175         r = sd_journal_open(&self->j, flags);
176     Py_END_ALLOW_THREADS
177
178     return set_error(r, path, "Invalid flags or path");
179 }
180
181
182 PyDoc_STRVAR(Reader_fileno__doc__,
183              "fileno() -> int\n\n"
184              "Get a file descriptor to poll for changes in the journal.\n"
185              "This method invokes sd_journal_get_fd().\n"
186              "See man:sd_journal_get_fd(3).");
187 static PyObject* Reader_fileno(Reader *self, PyObject *args)
188 {
189     int fd = sd_journal_get_fd(self->j);
190     set_error(fd, NULL, NULL);
191     if (fd < 0)
192         return NULL;
193     return long_FromLong(fd);
194 }
195
196
197 PyDoc_STRVAR(Reader_reliable_fd__doc__,
198              "reliable_fd() -> bool\n\n"
199              "Returns True iff the journal can be polled reliably.\n"
200              "This method invokes sd_journal_reliable_fd().\n"
201              "See man:sd_journal_reliable_fd(3).");
202 static PyObject* Reader_reliable_fd(Reader *self, PyObject *args)
203 {
204     int r = sd_journal_reliable_fd(self->j);
205     set_error(r, NULL, NULL);
206     if (r < 0)
207         return NULL;
208     return PyBool_FromLong(r);
209 }
210
211
212 PyDoc_STRVAR(Reader_get_events__doc__,
213              "get_events() -> int\n\n"
214              "Returns a mask of poll() events to wait for on the file\n"
215              "descriptor returned by .fileno().\n\n"
216              "See man:sd_journal_get_events(3) for further discussion.");
217 static PyObject* Reader_get_events(Reader *self, PyObject *args)
218 {
219     int r = sd_journal_get_events(self->j);
220     set_error(r, NULL, NULL);
221     if (r < 0)
222         return NULL;
223     return long_FromLong(r);
224 }
225
226
227 PyDoc_STRVAR(Reader_get_timeout__doc__,
228              "get_timeout() -> int or None\n\n"
229              "Returns a timeout value for usage in poll(), the time since the\n"
230              "epoch of clock_gettime(2) in microseconds, or None if no timeout\n"
231              "is necessary.\n\n"
232              "The return value must be converted to a relative timeout in\n"
233              "milliseconds if it is to be used as an argument for poll().\n"
234              "See man:sd_journal_get_timeout(3) for further discussion.");
235 static PyObject* Reader_get_timeout(Reader *self, PyObject *args)
236 {
237     int r;
238     uint64_t t;
239
240     r = sd_journal_get_timeout(self->j, &t);
241     set_error(r, NULL, NULL);
242     if (r < 0)
243         return NULL;
244
245     if (t == (uint64_t) -1)
246         Py_RETURN_NONE;
247
248     assert_cc(sizeof(unsigned long long) == sizeof(t));
249     return PyLong_FromUnsignedLongLong(t);
250 }
251
252
253 PyDoc_STRVAR(Reader_get_timeout_ms__doc__,
254              "get_timeout_ms() -> int\n\n"
255              "Returns a timeout value suitable for usage in poll(), the value\n"
256              "returned by .get_timeout() converted to relative ms, or -1 if\n"
257              "no timeout is necessary.");
258 static PyObject* Reader_get_timeout_ms(Reader *self, PyObject *args)
259 {
260     int r;
261     uint64_t t;
262
263     r = sd_journal_get_timeout(self->j, &t);
264     set_error(r, NULL, NULL);
265     if (r < 0)
266         return NULL;
267
268     return absolute_timeout(t);
269 }
270
271
272 PyDoc_STRVAR(Reader_close__doc__,
273              "close() -> None\n\n"
274              "Free resources allocated by this Reader object.\n"
275              "This method invokes sd_journal_close().\n"
276              "See man:sd_journal_close(3).");
277 static PyObject* Reader_close(Reader *self, PyObject *args)
278 {
279     assert(self);
280     assert(!args);
281
282     sd_journal_close(self->j);
283     self->j = NULL;
284     Py_RETURN_NONE;
285 }
286
287
288 PyDoc_STRVAR(Reader_get_usage__doc__,
289              "get_usage() -> int\n\n"
290              "Returns the total disk space currently used by journal\n"
291              "files (in bytes). If `SD_JOURNAL_LOCAL_ONLY` was\n"
292              "passed when opening the journal this value will only reflect\n"
293              "the size of journal files of the local host, otherwise\n"
294              "of all hosts.\n\n"
295              "This method invokes sd_journal_get_usage().\n"
296              "See man:sd_journal_get_usage(3).");
297 static PyObject* Reader_get_usage(Reader *self, PyObject *args)
298 {
299     int r;
300     uint64_t bytes;
301
302     r = sd_journal_get_usage(self->j, &bytes);
303     if (set_error(r, NULL, NULL))
304         return NULL;
305
306     assert_cc(sizeof(unsigned long long) == sizeof(bytes));
307     return PyLong_FromUnsignedLongLong(bytes);
308 }
309
310
311 PyDoc_STRVAR(Reader___enter____doc__,
312              "__enter__() -> self\n\n"
313              "Part of the context manager protocol.\n"
314              "Returns self.\n");
315 static PyObject* Reader___enter__(PyObject *self, PyObject *args)
316 {
317     assert(self);
318     assert(!args);
319
320     Py_INCREF(self);
321     return self;
322 }
323
324 PyDoc_STRVAR(Reader___exit____doc__,
325              "__exit__(type, value, traceback) -> None\n\n"
326              "Part of the context manager protocol.\n"
327              "Closes the journal.\n");
328 static PyObject* Reader___exit__(Reader *self, PyObject *args)
329 {
330     return Reader_close(self, args);
331 }
332
333
334 PyDoc_STRVAR(Reader_next__doc__,
335              "next([skip]) -> bool\n\n"
336              "Go to the next log entry. Optional skip value means to go to\n"
337              "the `skip`\\-th log entry.\n"
338              "Returns False if at end of file, True otherwise.");
339 static PyObject* Reader_next(Reader *self, PyObject *args)
340 {
341     int64_t skip = 1LL;
342     int r;
343
344     if (!PyArg_ParseTuple(args, "|L:next", &skip))
345         return NULL;
346
347     if (skip == 0LL) {
348         PyErr_SetString(PyExc_ValueError, "skip must be nonzero");
349         return NULL;
350     }
351
352     Py_BEGIN_ALLOW_THREADS
353     if (skip == 1LL)
354         r = sd_journal_next(self->j);
355     else if (skip == -1LL)
356         r = sd_journal_previous(self->j);
357     else if (skip > 1LL)
358         r = sd_journal_next_skip(self->j, skip);
359     else if (skip < -1LL)
360         r = sd_journal_previous_skip(self->j, -skip);
361     else
362         assert_not_reached("should not be here");
363     Py_END_ALLOW_THREADS
364
365     set_error(r, NULL, NULL);
366     if (r < 0)
367         return NULL;
368     return PyBool_FromLong(r);
369 }
370
371 PyDoc_STRVAR(Reader_previous__doc__,
372              "previous([skip]) -> bool\n\n"
373              "Go to the previous log entry. Optional skip value means to \n"
374              "go to the `skip`\\-th previous log entry.\n"
375              "Returns False if at start of file, True otherwise.");
376 static PyObject* Reader_previous(Reader *self, PyObject *args)
377 {
378     int64_t skip = 1LL;
379     if (!PyArg_ParseTuple(args, "|L:previous", &skip))
380         return NULL;
381
382     return PyObject_CallMethod((PyObject *)self, (char*) "_next",
383                                (char*) "L", -skip);
384 }
385
386
387 static int extract(const char* msg, size_t msg_len,
388                    PyObject **key, PyObject **value) {
389     PyObject *k = NULL, *v;
390     const char *delim_ptr;
391
392     delim_ptr = memchr(msg, '=', msg_len);
393     if (!delim_ptr) {
394         PyErr_SetString(PyExc_OSError,
395                         "journal gave us a field without '='");
396         return -1;
397     }
398
399     if (key) {
400         k = unicode_FromStringAndSize(msg, delim_ptr - (const char*) msg);
401         if (!k)
402             return -1;
403     }
404
405     if (value) {
406         v = PyBytes_FromStringAndSize(delim_ptr + 1,
407                              (const char*) msg + msg_len - (delim_ptr + 1));
408         if (!v) {
409             Py_XDECREF(k);
410             return -1;
411         }
412
413         *value = v;
414     }
415
416     if (key)
417         *key = k;
418
419     return 0;
420 }
421
422 PyDoc_STRVAR(Reader_get__doc__,
423              "get(str) -> str\n\n"
424              "Return data associated with this key in current log entry.\n"
425              "Throws KeyError is the data is not available.");
426 static PyObject* Reader_get(Reader *self, PyObject *args)
427 {
428     const char* field;
429     const void* msg;
430     size_t msg_len;
431     PyObject *value;
432     int r;
433
434     assert(self);
435     assert(args);
436
437     if (!PyArg_ParseTuple(args, "s:get", &field))
438         return NULL;
439
440     r = sd_journal_get_data(self->j, field, &msg, &msg_len);
441     if (r == -ENOENT) {
442         PyErr_SetString(PyExc_KeyError, field);
443         return NULL;
444     } else if (set_error(r, NULL, "field name is not valid"))
445         return NULL;
446
447     r = extract(msg, msg_len, NULL, &value);
448     if (r < 0)
449         return NULL;
450     return value;
451 }
452
453
454 PyDoc_STRVAR(Reader_get_all__doc__,
455              "_get_all() -> dict\n\n"
456              "Return dictionary of the current log entry.");
457 static PyObject* Reader_get_all(Reader *self, PyObject *args)
458 {
459     PyObject *dict;
460     const void *msg;
461     size_t msg_len;
462     int r;
463
464     dict = PyDict_New();
465     if (!dict)
466             return NULL;
467
468     SD_JOURNAL_FOREACH_DATA(self->j, msg, msg_len) {
469         _cleanup_Py_DECREF_ PyObject *key = NULL, *value = NULL;
470
471         r = extract(msg, msg_len, &key, &value);
472         if (r < 0)
473             goto error;
474
475         if (PyDict_Contains(dict, key)) {
476             PyObject *cur_value = PyDict_GetItem(dict, key);
477
478             if (PyList_CheckExact(cur_value)) {
479                 r = PyList_Append(cur_value, value);
480                 if (r < 0)
481                     goto error;
482             } else {
483                 _cleanup_Py_DECREF_ PyObject *tmp_list = PyList_New(0);
484                 if (!tmp_list)
485                     goto error;
486
487                 r = PyList_Append(tmp_list, cur_value);
488                 if (r < 0)
489                     goto error;
490
491                 r = PyList_Append(tmp_list, value);
492                 if (r < 0)
493                     goto error;
494
495                 r = PyDict_SetItem(dict, key, tmp_list);
496                 if (r < 0)
497                     goto error;
498             }
499         } else {
500             r = PyDict_SetItem(dict, key, value);
501             if (r < 0)
502                 goto error;
503         }
504     }
505
506     return dict;
507
508 error:
509     Py_DECREF(dict);
510     return NULL;
511 }
512
513
514 PyDoc_STRVAR(Reader_get_realtime__doc__,
515              "get_realtime() -> int\n\n"
516              "Return the realtime timestamp for the current journal entry\n"
517              "in microseconds.\n\n"
518              "Wraps sd_journal_get_realtime_usec().\n"
519              "See man:sd_journal_get_realtime_usec(3).");
520 static PyObject* Reader_get_realtime(Reader *self, PyObject *args)
521 {
522     uint64_t timestamp;
523     int r;
524
525     assert(self);
526     assert(!args);
527
528     r = sd_journal_get_realtime_usec(self->j, &timestamp);
529     if (set_error(r, NULL, NULL))
530         return NULL;
531
532     assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
533     return PyLong_FromUnsignedLongLong(timestamp);
534 }
535
536
537 PyDoc_STRVAR(Reader_get_monotonic__doc__,
538              "get_monotonic() -> (timestamp, bootid)\n\n"
539              "Return the monotonic timestamp for the current journal entry\n"
540              "as a tuple of time in microseconds and bootid.\n\n"
541              "Wraps sd_journal_get_monotonic_usec().\n"
542              "See man:sd_journal_get_monotonic_usec(3).");
543 static PyObject* Reader_get_monotonic(Reader *self, PyObject *args)
544 {
545     uint64_t timestamp;
546     sd_id128_t id;
547     PyObject *monotonic, *bootid, *tuple;
548     int r;
549
550     assert(self);
551     assert(!args);
552
553     r = sd_journal_get_monotonic_usec(self->j, &timestamp, &id);
554     if (set_error(r, NULL, NULL))
555         return NULL;
556
557     assert_cc(sizeof(unsigned long long) == sizeof(timestamp));
558     monotonic = PyLong_FromUnsignedLongLong(timestamp);
559     bootid = PyBytes_FromStringAndSize((const char*) &id.bytes, sizeof(id.bytes));
560 #if PY_MAJOR_VERSION >= 3
561     tuple = PyStructSequence_New(&MonotonicType);
562 #else
563     tuple = PyTuple_New(2);
564 #endif
565     if (!monotonic || !bootid || !tuple) {
566         Py_XDECREF(monotonic);
567         Py_XDECREF(bootid);
568         Py_XDECREF(tuple);
569         return NULL;
570     }
571
572 #if PY_MAJOR_VERSION >= 3
573     PyStructSequence_SET_ITEM(tuple, 0, monotonic);
574     PyStructSequence_SET_ITEM(tuple, 1, bootid);
575 #else
576     PyTuple_SET_ITEM(tuple, 0, monotonic);
577     PyTuple_SET_ITEM(tuple, 1, bootid);
578 #endif
579
580     return tuple;
581 }
582
583 PyDoc_STRVAR(Reader_add_match__doc__,
584              "add_match(match) -> None\n\n"
585              "Add a match to filter journal log entries. All matches of different\n"
586              "fields are combined with logical AND, and matches of the same field\n"
587              "are automatically combined with logical OR.\n"
588              "Match is a string of the form \"FIELD=value\".");
589 static PyObject* Reader_add_match(Reader *self, PyObject *args, PyObject *keywds)
590 {
591     char *match;
592     int match_len, r;
593     if (!PyArg_ParseTuple(args, "s#:add_match", &match, &match_len))
594         return NULL;
595
596     r = sd_journal_add_match(self->j, match, match_len);
597     set_error(r, NULL, "Invalid match");
598     if (r < 0)
599             return NULL;
600
601     Py_RETURN_NONE;
602 }
603
604
605 PyDoc_STRVAR(Reader_add_disjunction__doc__,
606              "add_disjunction() -> None\n\n"
607              "Inserts a logical OR between matches added since previous\n"
608              "add_disjunction() or add_conjunction() and the next\n"
609              "add_disjunction() or add_conjunction().\n\n"
610              "See man:sd_journal_add_disjunction(3) for explanation.");
611 static PyObject* Reader_add_disjunction(Reader *self, PyObject *args)
612 {
613     int r;
614     r = sd_journal_add_disjunction(self->j);
615     set_error(r, NULL, NULL);
616     if (r < 0)
617         return NULL;
618     Py_RETURN_NONE;
619 }
620
621
622 PyDoc_STRVAR(Reader_add_conjunction__doc__,
623              "add_conjunction() -> None\n\n"
624              "Inserts a logical AND between matches added since previous\n"
625              "add_disjunction() or add_conjunction() and the next\n"
626              "add_disjunction() or add_conjunction().\n\n"
627              "See man:sd_journal_add_disjunction(3) for explanation.");
628 static PyObject* Reader_add_conjunction(Reader *self, PyObject *args)
629 {
630     int r;
631     r = sd_journal_add_conjunction(self->j);
632     set_error(r, NULL, NULL);
633     if (r < 0)
634         return NULL;
635     Py_RETURN_NONE;
636 }
637
638
639 PyDoc_STRVAR(Reader_flush_matches__doc__,
640              "flush_matches() -> None\n\n"
641              "Clear all current match filters.");
642 static PyObject* Reader_flush_matches(Reader *self, PyObject *args)
643 {
644     sd_journal_flush_matches(self->j);
645     Py_RETURN_NONE;
646 }
647
648
649 PyDoc_STRVAR(Reader_seek_head__doc__,
650              "seek_head() -> None\n\n"
651              "Jump to the beginning of the journal.\n"
652              "This method invokes sd_journal_seek_head().\n"
653              "See man:sd_journal_seek_head(3).");
654 static PyObject* Reader_seek_head(Reader *self, PyObject *args)
655 {
656     int r;
657     Py_BEGIN_ALLOW_THREADS
658     r = sd_journal_seek_head(self->j);
659     Py_END_ALLOW_THREADS
660     if (set_error(r, NULL, NULL))
661         return NULL;
662     Py_RETURN_NONE;
663 }
664
665
666 PyDoc_STRVAR(Reader_seek_tail__doc__,
667              "seek_tail() -> None\n\n"
668              "Jump to the end of the journal.\n"
669              "This method invokes sd_journal_seek_tail().\n"
670              "See man:sd_journal_seek_tail(3).");
671 static PyObject* Reader_seek_tail(Reader *self, PyObject *args)
672 {
673     int r;
674     Py_BEGIN_ALLOW_THREADS
675     r = sd_journal_seek_tail(self->j);
676     Py_END_ALLOW_THREADS
677     if (set_error(r, NULL, NULL))
678         return NULL;
679     Py_RETURN_NONE;
680 }
681
682
683 PyDoc_STRVAR(Reader_seek_realtime__doc__,
684              "seek_realtime(realtime) -> None\n\n"
685              "Seek to nearest matching journal entry to `realtime`. Argument\n"
686              "`realtime` in specified in seconds.");
687 static PyObject* Reader_seek_realtime(Reader *self, PyObject *args)
688 {
689     uint64_t timestamp;
690     int r;
691
692     if (!PyArg_ParseTuple(args, "K:seek_realtime", &timestamp))
693         return NULL;
694
695     Py_BEGIN_ALLOW_THREADS
696     r = sd_journal_seek_realtime_usec(self->j, timestamp);
697     Py_END_ALLOW_THREADS
698     if (set_error(r, NULL, NULL))
699         return NULL;
700     Py_RETURN_NONE;
701 }
702
703
704 PyDoc_STRVAR(Reader_seek_monotonic__doc__,
705              "seek_monotonic(monotonic[, bootid]) -> None\n\n"
706              "Seek to nearest matching journal entry to `monotonic`. Argument\n"
707              "`monotonic` is an timestamp from boot in microseconds.\n"
708              "Argument `bootid` is a string representing which boot the\n"
709              "monotonic time is reference to. Defaults to current bootid.");
710 static PyObject* Reader_seek_monotonic(Reader *self, PyObject *args)
711 {
712     char *bootid = NULL;
713     uint64_t timestamp;
714     sd_id128_t id;
715     int r;
716
717     if (!PyArg_ParseTuple(args, "K|z:seek_monotonic", &timestamp, &bootid))
718         return NULL;
719
720     if (bootid) {
721         r = sd_id128_from_string(bootid, &id);
722         if (set_error(r, NULL, "Invalid bootid"))
723             return NULL;
724     } else {
725         Py_BEGIN_ALLOW_THREADS
726         r = sd_id128_get_boot(&id);
727         Py_END_ALLOW_THREADS
728         if (set_error(r, NULL, NULL))
729             return NULL;
730     }
731
732     Py_BEGIN_ALLOW_THREADS
733     r = sd_journal_seek_monotonic_usec(self->j, id, timestamp);
734     Py_END_ALLOW_THREADS
735     if (set_error(r, NULL, NULL))
736         return NULL;
737
738     Py_RETURN_NONE;
739 }
740
741
742 PyDoc_STRVAR(Reader_process__doc__,
743              "process() -> state change (integer)\n\n"
744              "Process events and reset the readable state of the file\n"
745              "descriptor returned by .fileno().\n\n"
746              "Will return constants: NOP if no change; APPEND if new\n"
747              "entries have been added to the end of the journal; and\n"
748              "INVALIDATE if journal files have been added or removed.\n\n"
749              "See man:sd_journal_process(3) for further discussion.");
750 static PyObject* Reader_process(Reader *self, PyObject *args)
751 {
752     int r;
753
754     assert(!args);
755
756     Py_BEGIN_ALLOW_THREADS
757     r = sd_journal_process(self->j);
758     Py_END_ALLOW_THREADS
759     if (set_error(r, NULL, NULL) < 0)
760         return NULL;
761
762     return long_FromLong(r);
763 }
764
765
766 PyDoc_STRVAR(Reader_wait__doc__,
767              "wait([timeout]) -> state change (integer)\n\n"
768              "Wait for a change in the journal. Argument `timeout` specifies\n"
769              "the maximum number of microseconds to wait before returning\n"
770              "regardless of wheter the journal has changed. If `timeout` is -1,\n"
771              "then block forever.\n\n"
772              "Will return constants: NOP if no change; APPEND if new\n"
773              "entries have been added to the end of the journal; and\n"
774              "INVALIDATE if journal files have been added or removed.\n\n"
775              "See man:sd_journal_wait(3) for further discussion.");
776 static PyObject* Reader_wait(Reader *self, PyObject *args)
777 {
778     int r;
779     int64_t timeout;
780
781     if (!PyArg_ParseTuple(args, "|L:wait", &timeout))
782         return NULL;
783
784     Py_BEGIN_ALLOW_THREADS
785     r = sd_journal_wait(self->j, timeout);
786     Py_END_ALLOW_THREADS
787     if (set_error(r, NULL, NULL) < 0)
788         return NULL;
789
790     return long_FromLong(r);
791 }
792
793
794 PyDoc_STRVAR(Reader_seek_cursor__doc__,
795              "seek_cursor(cursor) -> None\n\n"
796              "Seek to journal entry by given unique reference `cursor`.");
797 static PyObject* Reader_seek_cursor(Reader *self, PyObject *args)
798 {
799     const char *cursor;
800     int r;
801
802     if (!PyArg_ParseTuple(args, "s:seek_cursor", &cursor))
803         return NULL;
804
805     Py_BEGIN_ALLOW_THREADS
806     r = sd_journal_seek_cursor(self->j, cursor);
807     Py_END_ALLOW_THREADS
808     if (set_error(r, NULL, "Invalid cursor"))
809         return NULL;
810     Py_RETURN_NONE;
811 }
812
813
814 PyDoc_STRVAR(Reader_get_cursor__doc__,
815              "get_cursor() -> str\n\n"
816              "Return a cursor string for the current journal entry.\n\n"
817              "Wraps sd_journal_get_cursor(). See man:sd_journal_get_cursor(3).");
818 static PyObject* Reader_get_cursor(Reader *self, PyObject *args)
819 {
820     _cleanup_free_ char *cursor = NULL;
821     int r;
822
823     assert(self);
824     assert(!args);
825
826     r = sd_journal_get_cursor(self->j, &cursor);
827     if (set_error(r, NULL, NULL))
828         return NULL;
829
830     return unicode_FromString(cursor);
831 }
832
833
834 PyDoc_STRVAR(Reader_test_cursor__doc__,
835              "test_cursor(str) -> bool\n\n"
836              "Test whether the cursor string matches current journal entry.\n\n"
837              "Wraps sd_journal_test_cursor(). See man:sd_journal_test_cursor(3).");
838 static PyObject* Reader_test_cursor(Reader *self, PyObject *args)
839 {
840     const char *cursor;
841     int r;
842
843     assert(self);
844     assert(args);
845
846     if (!PyArg_ParseTuple(args, "s:test_cursor", &cursor))
847         return NULL;
848
849     r = sd_journal_test_cursor(self->j, cursor);
850     set_error(r, NULL, NULL);
851     if (r < 0)
852         return NULL;
853
854     return PyBool_FromLong(r);
855 }
856
857 PyDoc_STRVAR(Reader_query_unique__doc__,
858              "query_unique(field) -> a set of values\n\n"
859              "Return a set of unique values appearing in journal for the\n"
860              "given `field`. Note this does not respect any journal matches.");
861 static PyObject* Reader_query_unique(Reader *self, PyObject *args)
862 {
863     char *query;
864     int r;
865     const void *uniq;
866     size_t uniq_len;
867     PyObject *value_set, *key, *value;
868
869     if (!PyArg_ParseTuple(args, "s:query_unique", &query))
870         return NULL;
871
872     Py_BEGIN_ALLOW_THREADS
873     r = sd_journal_query_unique(self->j, query);
874     Py_END_ALLOW_THREADS
875     if (set_error(r, NULL, "Invalid field name"))
876         return NULL;
877
878     value_set = PySet_New(0);
879     key = unicode_FromString(query);
880
881     SD_JOURNAL_FOREACH_UNIQUE(self->j, uniq, uniq_len) {
882         const char *delim_ptr;
883
884         delim_ptr = memchr(uniq, '=', uniq_len);
885         value = PyBytes_FromStringAndSize(
886             delim_ptr + 1,
887             (const char*) uniq + uniq_len - (delim_ptr + 1));
888         PySet_Add(value_set, value);
889         Py_DECREF(value);
890     }
891     Py_DECREF(key);
892     return value_set;
893 }
894
895
896 PyDoc_STRVAR(Reader_get_catalog__doc__,
897              "get_catalog() -> str\n\n"
898              "Retrieve a message catalog entry for the current journal entry.\n"
899              "Will throw IndexError if the entry has no MESSAGE_ID\n"
900              "and KeyError is the id is specified, but hasn't been found\n"
901              "in the catalog.\n\n"
902              "Wraps man:sd_journal_get_catalog(3).");
903 static PyObject* Reader_get_catalog(Reader *self, PyObject *args)
904 {
905     int r;
906     _cleanup_free_ char *msg = NULL;
907
908     assert(self);
909     assert(!args);
910
911     Py_BEGIN_ALLOW_THREADS
912     r = sd_journal_get_catalog(self->j, &msg);
913     Py_END_ALLOW_THREADS
914     if (r == -ENOENT) {
915         const void* mid;
916         size_t mid_len;
917
918         r = sd_journal_get_data(self->j, "MESSAGE_ID", &mid, &mid_len);
919         if (r == 0) {
920             const size_t l = sizeof("MESSAGE_ID");
921             assert(mid_len > l);
922             PyErr_Format(PyExc_KeyError, "%.*s", (int) (mid_len - l),
923                          (const char*) mid + l);
924         } else if (r == -ENOENT)
925             PyErr_SetString(PyExc_IndexError, "no MESSAGE_ID field");
926         else
927             set_error(r, NULL, NULL);
928         return NULL;
929     } else if (set_error(r, NULL, NULL))
930         return NULL;
931
932     return unicode_FromString(msg);
933 }
934
935
936 PyDoc_STRVAR(get_catalog__doc__,
937              "get_catalog(id128) -> str\n\n"
938              "Retrieve a message catalog entry for the given id.\n"
939              "Wraps man:sd_journal_get_catalog_for_message_id(3).");
940 static PyObject* get_catalog(PyObject *self, PyObject *args)
941 {
942     int r;
943     char *id_ = NULL;
944     sd_id128_t id;
945     _cleanup_free_ char *msg = NULL;
946
947     assert(!self);
948     assert(args);
949
950     if (!PyArg_ParseTuple(args, "z:get_catalog", &id_))
951         return NULL;
952
953     r = sd_id128_from_string(id_, &id);
954     if (set_error(r, NULL, "Invalid id128"))
955         return NULL;
956
957     Py_BEGIN_ALLOW_THREADS
958     r = sd_journal_get_catalog_for_message_id(id, &msg);
959     Py_END_ALLOW_THREADS
960     if (set_error(r, NULL, NULL))
961         return NULL;
962
963     return unicode_FromString(msg);
964 }
965
966
967 PyDoc_STRVAR(data_threshold__doc__,
968              "Threshold for field size truncation in bytes.\n\n"
969              "Fields longer than this will be truncated to the threshold size.\n"
970              "Defaults to 64Kb.");
971
972 static PyObject* Reader_get_data_threshold(Reader *self, void *closure)
973 {
974     size_t cvalue;
975     int r;
976
977     r = sd_journal_get_data_threshold(self->j, &cvalue);
978     if (set_error(r, NULL, NULL))
979         return NULL;
980
981     return long_FromSize_t(cvalue);
982 }
983
984 static int Reader_set_data_threshold(Reader *self, PyObject *value, void *closure)
985 {
986     int r;
987     if (value == NULL) {
988         PyErr_SetString(PyExc_AttributeError, "Cannot delete data threshold");
989         return -1;
990     }
991     if (!long_Check(value)){
992         PyErr_SetString(PyExc_TypeError, "Data threshold must be an int");
993         return -1;
994     }
995     r = sd_journal_set_data_threshold(self->j, (size_t) long_AsLong(value));
996     return set_error(r, NULL, NULL);
997 }
998
999
1000 PyDoc_STRVAR(closed__doc__,
1001              "True iff journal is closed");
1002 static PyObject* Reader_get_closed(Reader *self, void *closure)
1003 {
1004     return PyBool_FromLong(self->j == NULL);
1005 }
1006
1007
1008 static PyGetSetDef Reader_getsetters[] = {
1009     {(char*) "data_threshold",
1010      (getter) Reader_get_data_threshold,
1011      (setter) Reader_set_data_threshold,
1012      (char*) data_threshold__doc__,
1013      NULL},
1014     {(char*) "closed",
1015      (getter) Reader_get_closed,
1016      NULL,
1017      (char*) closed__doc__,
1018      NULL},
1019     {} /* Sentinel */
1020 };
1021
1022 static PyMethodDef Reader_methods[] = {
1023     {"fileno",          (PyCFunction) Reader_fileno, METH_NOARGS, Reader_fileno__doc__},
1024     {"reliable_fd",     (PyCFunction) Reader_reliable_fd, METH_NOARGS, Reader_reliable_fd__doc__},
1025     {"get_events",      (PyCFunction) Reader_get_events, METH_NOARGS, Reader_get_events__doc__},
1026     {"get_timeout",     (PyCFunction) Reader_get_timeout, METH_NOARGS, Reader_get_timeout__doc__},
1027     {"get_timeout_ms",  (PyCFunction) Reader_get_timeout_ms, METH_NOARGS, Reader_get_timeout_ms__doc__},
1028     {"close",           (PyCFunction) Reader_close, METH_NOARGS, Reader_close__doc__},
1029     {"get_usage",       (PyCFunction) Reader_get_usage, METH_NOARGS, Reader_get_usage__doc__},
1030     {"__enter__",       (PyCFunction) Reader___enter__, METH_NOARGS, Reader___enter____doc__},
1031     {"__exit__",        (PyCFunction) Reader___exit__, METH_VARARGS, Reader___exit____doc__},
1032     {"_next",           (PyCFunction) Reader_next, METH_VARARGS, Reader_next__doc__},
1033     {"_previous",       (PyCFunction) Reader_previous, METH_VARARGS, Reader_previous__doc__},
1034     {"_get",            (PyCFunction) Reader_get, METH_VARARGS, Reader_get__doc__},
1035     {"_get_all",        (PyCFunction) Reader_get_all, METH_NOARGS, Reader_get_all__doc__},
1036     {"_get_realtime",   (PyCFunction) Reader_get_realtime, METH_NOARGS, Reader_get_realtime__doc__},
1037     {"_get_monotonic",  (PyCFunction) Reader_get_monotonic, METH_NOARGS, Reader_get_monotonic__doc__},
1038     {"add_match",       (PyCFunction) Reader_add_match, METH_VARARGS|METH_KEYWORDS, Reader_add_match__doc__},
1039     {"add_disjunction", (PyCFunction) Reader_add_disjunction, METH_NOARGS, Reader_add_disjunction__doc__},
1040     {"add_conjunction", (PyCFunction) Reader_add_conjunction, METH_NOARGS, Reader_add_conjunction__doc__},
1041     {"flush_matches",   (PyCFunction) Reader_flush_matches, METH_NOARGS, Reader_flush_matches__doc__},
1042     {"seek_head",       (PyCFunction) Reader_seek_head, METH_NOARGS, Reader_seek_head__doc__},
1043     {"seek_tail",       (PyCFunction) Reader_seek_tail, METH_NOARGS, Reader_seek_tail__doc__},
1044     {"seek_realtime",   (PyCFunction) Reader_seek_realtime, METH_VARARGS, Reader_seek_realtime__doc__},
1045     {"seek_monotonic",  (PyCFunction) Reader_seek_monotonic, METH_VARARGS, Reader_seek_monotonic__doc__},
1046     {"process",         (PyCFunction) Reader_process, METH_NOARGS, Reader_process__doc__},
1047     {"wait",            (PyCFunction) Reader_wait, METH_VARARGS, Reader_wait__doc__},
1048     {"seek_cursor",     (PyCFunction) Reader_seek_cursor, METH_VARARGS, Reader_seek_cursor__doc__},
1049     {"_get_cursor",     (PyCFunction) Reader_get_cursor, METH_NOARGS, Reader_get_cursor__doc__},
1050     {"test_cursor",     (PyCFunction) Reader_test_cursor, METH_VARARGS, Reader_test_cursor__doc__},
1051     {"query_unique",    (PyCFunction) Reader_query_unique, METH_VARARGS, Reader_query_unique__doc__},
1052     {"get_catalog",     (PyCFunction) Reader_get_catalog, METH_NOARGS, Reader_get_catalog__doc__},
1053     {}  /* Sentinel */
1054 };
1055
1056 static PyTypeObject ReaderType = {
1057     PyVarObject_HEAD_INIT(NULL, 0)
1058     .tp_name = "_reader._Reader",
1059     .tp_basicsize = sizeof(Reader),
1060     .tp_dealloc = (destructor) Reader_dealloc,
1061     .tp_flags = Py_TPFLAGS_DEFAULT | Py_TPFLAGS_BASETYPE,
1062     .tp_doc = Reader__doc__,
1063     .tp_methods = Reader_methods,
1064     .tp_getset = Reader_getsetters,
1065     .tp_init = (initproc) Reader_init,
1066     .tp_new = PyType_GenericNew,
1067 };
1068
1069 static PyMethodDef methods[] = {
1070         { "_get_catalog", get_catalog, METH_VARARGS, get_catalog__doc__},
1071         {} /* Sentinel */
1072 };
1073
1074 #if PY_MAJOR_VERSION >= 3
1075 static PyModuleDef module = {
1076     PyModuleDef_HEAD_INIT,
1077     "_reader",
1078     module__doc__,
1079     -1,
1080     methods,
1081 };
1082 #endif
1083
1084 #if PY_MAJOR_VERSION >= 3
1085 static bool initialized = false;
1086 #endif
1087
1088 #pragma GCC diagnostic push
1089 #pragma GCC diagnostic ignored "-Wmissing-prototypes"
1090
1091 PyMODINIT_FUNC
1092 #if PY_MAJOR_VERSION >= 3
1093 PyInit__reader(void)
1094 #else
1095 init_reader(void)
1096 #endif
1097 {
1098     PyObject* m;
1099
1100     PyDateTime_IMPORT;
1101
1102     if (PyType_Ready(&ReaderType) < 0)
1103 #if PY_MAJOR_VERSION >= 3
1104         return NULL;
1105 #else
1106         return;
1107 #endif
1108
1109 #if PY_MAJOR_VERSION >= 3
1110     m = PyModule_Create(&module);
1111     if (m == NULL)
1112         return NULL;
1113
1114     if (!initialized) {
1115         PyStructSequence_InitType(&MonotonicType, &Monotonic_desc);
1116         initialized = true;
1117     }
1118 #else
1119     m = Py_InitModule3("_reader", methods, module__doc__);
1120     if (m == NULL)
1121         return;
1122 #endif
1123
1124     Py_INCREF(&ReaderType);
1125 #if PY_MAJOR_VERSION >= 3
1126     Py_INCREF(&MonotonicType);
1127 #endif
1128     if (PyModule_AddObject(m, "_Reader", (PyObject *) &ReaderType) ||
1129 #if PY_MAJOR_VERSION >= 3
1130         PyModule_AddObject(m, "Monotonic", (PyObject*) &MonotonicType) ||
1131 #endif
1132         PyModule_AddIntConstant(m, "NOP", SD_JOURNAL_NOP) ||
1133         PyModule_AddIntConstant(m, "APPEND", SD_JOURNAL_APPEND) ||
1134         PyModule_AddIntConstant(m, "INVALIDATE", SD_JOURNAL_INVALIDATE) ||
1135         PyModule_AddIntConstant(m, "LOCAL_ONLY", SD_JOURNAL_LOCAL_ONLY) ||
1136         PyModule_AddIntConstant(m, "RUNTIME_ONLY", SD_JOURNAL_RUNTIME_ONLY) ||
1137         PyModule_AddIntConstant(m, "SYSTEM", SD_JOURNAL_SYSTEM) ||
1138         PyModule_AddIntConstant(m, "SYSTEM_ONLY", SD_JOURNAL_SYSTEM_ONLY) ||
1139         PyModule_AddIntConstant(m, "CURRENT_USER", SD_JOURNAL_CURRENT_USER) ||
1140         PyModule_AddStringConstant(m, "__version__", PACKAGE_VERSION)) {
1141 #if PY_MAJOR_VERSION >= 3
1142         Py_DECREF(m);
1143         return NULL;
1144 #endif
1145     }
1146
1147 #if PY_MAJOR_VERSION >= 3
1148     return m;
1149 #endif
1150 }
1151
1152 #pragma GCC diagnostic pop